From 1e91bb41908e53490e277ca64643b5d8913eb470 Mon Sep 17 00:00:00 2001 From: bridiver Date: Sun, 2 Oct 2016 20:06:57 -0700 Subject: [PATCH 1/2] wip tabs api cc @jkup --- app/browser/tabs.js | 106 ++++++++++++++++++++++++++++++++++++++++++++ app/index.js | 2 + 2 files changed, 108 insertions(+) create mode 100644 app/browser/tabs.js diff --git a/app/browser/tabs.js b/app/browser/tabs.js new file mode 100644 index 00000000000..93f680469ed --- /dev/null +++ b/app/browser/tabs.js @@ -0,0 +1,106 @@ +const {app, BrowserWindow, session, webContents} = require('electron') +const extensions = process.atomBinding('extension') +const { getIndexHTML } = require('../../js/lib/appUrlUtil') + +let currentWebContents = {} +let activeTab = null + +const cleanupWebContents = (tabId) => { + delete currentWebContents[tabId] +} + +const tabs = { + init: () => { + app.on('web-contents-created', function (event, tab) { + // TODO(bridiver) - also exclude extension action windows?? + if (extensions.isBackgroundPage(tab) || tab.getURL() === getIndexHTML()) { + return + } + let tabId = tab.getId() + tab.on('destroyed', cleanupWebContents.bind(null, tabId)) + tab.on('crashed', cleanupWebContents.bind(null, tabId)) + tab.on('close', cleanupWebContents.bind(null, tabId)) + tab.on('set-active', function (evt, active) { + if (active) { + activeTab = tab + } + }) + currentWebContents[tabId] = tab + }) + }, + + getWebContents: (tabId) => { + return currentWebContents[tabId] + }, + + create: (createProperties) => { + return new Promise((resolve, reject) => { + // TODO(bridiver) - make this available from electron + var payload = {} + process.emit('ELECTRON_GUEST_VIEW_MANAGER_NEXT_INSTANCE_ID', payload) + var guestInstanceId = payload.returnValue + + let win = BrowserWindow.getFocusedWindow() + let windowId = createProperties.windowId + if (windowId && windowId !== -2) { + win = BrowserWindow.fromId(windowId) || win + } + if (!win) { + reject('Could not find a window for new tab') + return + } + let opener = null + let newSession = session.defaultSession + let openerTabId = createProperties.openerTabId + if (openerTabId) { + opener = tabs.getWebContents(openerTabId) + if (!opener) { + reject('Opener does not exist') + return + } + // only use the opener if it is in the same window + if (opener.webContents.hostWebContents !== win.webContents) { + reject('Opener must be in the same window as new tab') + return + } + } + + opener = opener || activeTab + if (opener) { + newSession = opener.session + } else { + reject('Could not find an opener for new tab') + return + } + + let webPreferences = { + isGuest: true, + embedder: win.webContents, + session: newSession, + guestInstanceId, + delayedLoadUrl: createProperties.url || 'about:newtab' + } + webPreferences = Object.assign({}, opener.getWebPreferences(), webPreferences) + let guest = webContents.create(webPreferences) + process.emit('ELECTRON_GUEST_VIEW_MANAGER_REGISTER_GUEST', { sender: opener }, guest, guestInstanceId) + + guest.once('did-finish-load', () => { + resolve(guest) + }) + let active = createProperties.active !== false + if (!active) { + active = createProperties.selected !== false + } + let disposition = active ? 'foreground-tab' : 'background-tab' + + process.emit('ELECTRON_GUEST_VIEW_MANAGER_TAB_OPEN', + { sender: opener }, // event + 'about:blank', + '', + disposition, + { webPreferences: guest.getWebPreferences() }) + }) + } +} + +module.exports = tabs diff --git a/app/index.js b/app/index.js index 524db8e4efe..a0599a083d3 100644 --- a/app/index.js +++ b/app/index.js @@ -80,6 +80,7 @@ const contentSettings = require('../js/state/contentSettings') const privacy = require('../js/state/privacy') const basicAuth = require('./browser/basicAuth') const async = require('async') +const tabs = require('./browser/tabs') // temporary fix for #4517, #4518 and #4472 app.commandLine.appendSwitch('enable-use-zoom-for-dsf', 'false') @@ -415,6 +416,7 @@ app.on('ready', () => { Menu.init(initialState, null) return loadedPerWindowState }).then((loadedPerWindowState) => { + tabs.init() basicAuth.init() contentSettings.init() privacy.init() From b0ffd72763ce4ba78f7eeab3d1c08fe0d36df6c3 Mon Sep 17 00:00:00 2001 From: Jon Kuperman Date: Thu, 29 Sep 2016 23:25:28 -0700 Subject: [PATCH 2/2] ledger backup and recovery --- .../brave/locales/en-US/app.properties | 2 +- .../locales/en-US/preferences.properties | 24 +- app/ledger.js | 86 +++++++- app/locale.js | 9 +- app/sessionStore.js | 4 + docs/appActions.md | 12 + docs/state.md | 11 +- js/about/aboutActions.js | 30 +++ js/about/preferences.js | 205 +++++++++++++++++- js/actions/appActions.js | 18 ++ js/components/frame.js | 2 +- js/components/main.js | 3 + js/constants/appConfig.js | 2 + js/constants/appConstants.js | 5 + js/constants/settings.js | 5 +- js/constants/windowConstants.js | 1 + js/stores/appStore.js | 19 +- js/stores/windowStore.js | 3 + less/about/preferences.less | 66 +++++- less/button.less | 4 + less/modalOverlay.less | 22 ++ less/variables.less | 1 + 22 files changed, 518 insertions(+), 16 deletions(-) diff --git a/app/extensions/brave/locales/en-US/app.properties b/app/extensions/brave/locales/en-US/app.properties index 67e9b01c33f..ea1ee92fa10 100644 --- a/app/extensions/brave/locales/en-US/app.properties +++ b/app/extensions/brave/locales/en-US/app.properties @@ -126,7 +126,7 @@ flashInstalled=Flash is already installed and can be enabled in Preferences > Se goToPrefs=Open Preferences goToAdobe=Reinstall Flash allowFlashPlayer=Allow {{origin}} to run Flash Player? - +ledgerBackupText=Your ledger keys are {{paymentId}} and {{passphrase}} error=Error caseSensitivity=Match case nameField=Title: diff --git a/app/extensions/brave/locales/en-US/preferences.properties b/app/extensions/brave/locales/en-US/preferences.properties index dca67ced4d1..b0ccee7148a 100644 --- a/app/extensions/brave/locales/en-US/preferences.properties +++ b/app/extensions/brave/locales/en-US/preferences.properties @@ -59,10 +59,12 @@ bitcoinVisitAccount=Transfer BTC bitcoinBalance=Please transfer:  bitcoinWalletNotAvailable=Wallet information not available. :( usd=$ +cancel=Cancel done=Done off=off on=on -notifications=notifications +ok=Ok +notifications=Show payment notifications moneyAdd=Use your debit/credit card moneyAddSubTitle=No Bitcoin needed! outsideUSAPayment=Need to buy Bitcoin outside of the USA? @@ -71,6 +73,11 @@ add=Fund with debit/credit transferTime=Transfer may take up to 40 minutes addFundsTitle=Add funds… addFunds=Three ways to add funds to your Brave Wallet +copy=Copy +firstKey=Key 1 +secondKey=Key 2 +firstRecoveryKey=Recovery Key 1 +secondRecoveryKey=Recovery Key 2 copyToClipboard=Copy to clipboard smartphoneTitle=Use your smartphone app to transfer Bitcoin displayQRCode=Display QR code @@ -110,6 +117,21 @@ offerSearchSuggestions=Autocomplete search term as you type doNotTrackTitle=Do Not Track doNotTrack=Send a 'Do Not Track' header with browsing requests (requires browser restart) blockCanvasFingerprinting=Fingerprinting Protection (may break some websites) +advancedSettings=Advanced Settings... +advancedSettingsTitle=Advanced Settings for Brave Payments +ledgerRecoveryTitle=Recover your Brave wallet +ledgerRecoverySubtitle=Enter your recovery keys below +ledgerRecoveryContent=The balance of the recovered wallet will be transferred to your new Brave wallet. The old wallet will still exist as an empty wallet. +ledgerBackupTitle=Backup your Brave wallet +ledgerBackupContent=Below, you will find the anonymized recovery keys that are required if you ever lose access to this computer. +minimumPageTimeSetting=Minimum page time before logging a visit +minimumVisitsSetting=Minimum visits for publisher relevancy +backupLedger=Backup your wallet +balanceRecovered={{balance}} BTC was recovered and transferred to your Brave wallet. +recoverLedger=Recover your wallet +recover=Recover +printKeys=Print keys +saveRecoveryFile=Save recovery file... advancedPrivacySettings=Advanced Privacy Settings: braveryDefaults=Bravery Defaults blockAttackSites=Block reported attack sites (not available yet) diff --git a/app/ledger.js b/app/ledger.js index 1476d179b2b..272d3b1f0ae 100644 --- a/app/ledger.js +++ b/app/ledger.js @@ -53,6 +53,8 @@ const appStore = require('../js/stores/appStore') const eventStore = require('../js/stores/eventStore') const rulesolver = require('./extensions/brave/content/scripts/pageInformation') const ledgerUtil = require('./common/lib/ledgerUtil') +const Tabs = require('./browser/tabs') +const {fileUrl} = require('../js/lib/appUrlUtil') // TBD: remove these post beta [MTR] const logPath = 'ledger-log.json' @@ -133,9 +135,25 @@ const doAction = (action) => { case settings.PAYMENTS_ENABLED: initialize(action.value) break + case settings.PAYMENTS_CONTRIBUTION_AMOUNT: setPaymentInfo(action.value) break + + case settings.MINIMUM_VISIT_TIME: + if (action.value <= 0) break + + synopsis.options.minDuration = action.value + updatePublisherInfo() + break + + case settings.MINIMUM_VISTS: + if (action.value <= 0) break + + synopsis.options.minPublisherVisits = action.value + updatePublisherInfo() + break + default: break } @@ -215,6 +233,51 @@ var boot = () => { }) } +/* + * Print or Save Recovery Keys + */ + +var backupKeys = (appState, action) => { + const paymentId = appState.getIn(['ledgerInfo', 'paymentId']) + const passphrase = appState.getIn(['ledgerInfo', 'passphrase']) + const message = locale.translation('ledgerBackupText', {paymentId, passphrase}) + const filePath = path.join(app.getPath('userData'), '/brave_wallet_recovery.txt') + + fs.writeFile(filePath, message, (err) => { + if (err) { + console.log(err) + } else { + Tabs.create({url: fileUrl(filePath)}).then((webContents) => { + if (action.backupAction === 'print') { + webContents.print({silent: false, printBackground: false}) + } else { + webContents.downloadURL(fileUrl(filePath)) + } + }).catch((err) => { + console.error(err) + }) + } + }) + + return appState +} + +/* + * Recover Ledger Keys + */ + +var recoverKeys = (appState, action) => { + client.recoverWallet(action.firstRecoveryKey, action.secondRecoveryKey, (err, body) => { + if (err) { + setImmediate(() => appActions.ledgerRecoveryFailed()) + } else { + setImmediate(() => appActions.ledgerRecoverySucceeded()) + } + }) + + return appState +} + /* * IPC entry point */ @@ -571,6 +634,8 @@ var enable = (paymentsEnabled) => { */ var publisherInfo = { + options: undefined, + synopsis: undefined, _internal: { @@ -601,13 +666,15 @@ var updatePublisherInfo = () => { syncWriter(pathName(synopsisPath), synopsis, () => {}) publisherInfo.synopsis = synopsisNormalizer() + publisherInfo.synopsisOptions = synopsis.options + if (publisherInfo._internal.debugP) { data = [] publisherInfo.synopsis.forEach((entry) => { data.push(underscore.extend(underscore.omit(entry, [ 'faviconURL' ]), { faviconURL: entry.faviconURL && '...' })) }) - console.log('\nupdatePublisherInfo: ' + JSON.stringify(data, null, 2)) + console.log('\nupdatePublisherInfo: ' + JSON.stringify({ options: publisherInfo.synopsisOptions, synopsis: data }, null, 2)) } appActions.updatePublisherInfo(underscore.omit(publisherInfo, [ '_internal' ])) @@ -871,6 +938,14 @@ var ledgerInfo = { buyURL: undefined, bravery: undefined, + // wallet credentials + paymentId: undefined, + passphrase: undefined, + + // advanced ledger settings + minDuration: undefined, + minPublisherVisits: undefined, + hasBitcoinHandler: false, // geoIP/exchange information @@ -1109,6 +1184,12 @@ var getStateInfo = (state) => { var info = state.paymentInfo var then = underscore.now() - msecs.year + ledgerInfo.paymentId = state.properties.wallet.paymentId + ledgerInfo.passphrase = state.properties.wallet.keychains.passphrase + + ledgerInfo.minDuration = synopsis.options.minDuration + ledgerInfo.minPublisherVisits = synopsis.options.minPublisherVisits + ledgerInfo.created = !!state.properties.wallet ledgerInfo.creating = !ledgerInfo.created @@ -1230,7 +1311,6 @@ var getPaymentInfo = () => { info = underscore.extend(info, underscore.pick(body, [ 'buyURL', 'buyURLExpires', 'balance', 'unconfirmed', 'satoshis' ])) info.address = client.getWalletAddress() - info.passphrase = client.getWalletPassphrase() if ((amount) && (currency)) { info = underscore.extend(info, { amount: amount, currency: currency }) if ((body.rates) && (body.rates[currency])) { @@ -1452,6 +1532,8 @@ const showNotificationPaymentDone = (transactionContributionFiat) => { module.exports = { init: init, + recoverKeys: recoverKeys, + backupKeys: backupKeys, quit: quit, boot: boot } diff --git a/app/locale.js b/app/locale.js index f6d6d73ceb1..7ce065d2406 100644 --- a/app/locale.js +++ b/app/locale.js @@ -52,6 +52,7 @@ var rendererIdentifiers = function () { 'deleteBookmark', 'deleteHistoryEntry', 'deleteLedgerEntry', + 'ledgerBackupText', 'editFolder', 'editBookmark', 'unmuteTabs', @@ -229,13 +230,19 @@ var ctx = null var translations = {} var lang = 'en-US' +// todo: FSI/PDI stripping can probably be replaced once +// https://github.com/l20n/l20n.js/commit/2fea50bf43c43a8e930a519a37f0f64f3626e885 +// is released +const FSI = '\u2068' +const PDI = '\u2069' + // Return a translate token from cache or a placeholder // indicating that no translation is available exports.translation = function (token, replacements = {}) { if (translations[token]) { let returnVal = translations[token] for (var key in replacements) { - returnVal = returnVal.replace(new RegExp('{{\\s*' + key + '\\s*}}'), replacements[key]) + returnVal = returnVal.replace(new RegExp(FSI + '{{\\s*' + key + '\\s*}}' + PDI), replacements[key]) } return returnVal } else { diff --git a/app/sessionStore.js b/app/sessionStore.js index 06f7e595136..49cb50dbb05 100644 --- a/app/sessionStore.js +++ b/app/sessionStore.js @@ -233,6 +233,10 @@ module.exports.cleanAppData = (data, isShutdown) => { data.temporarySiteSettings = {} // Delete Flash state since this is checked on startup delete data.flashInitialized + // Delete Recovery status on shut down + try { + delete data.ui.about.preferences.recoverySucceeded + } catch (e) {} // We used to store a huge list of IDs but we didn't use them. // Get rid of them here. delete data.windows diff --git a/docs/appActions.md b/docs/appActions.md index b090869a0d1..e69bc0a7ddc 100644 --- a/docs/appActions.md +++ b/docs/appActions.md @@ -104,6 +104,18 @@ Dispatches a message to clear all completed downloads +### ledgerRecoverySucceeded() + +Dispatches a message to clear all completed downloads + + + +### ledgerRecoveryFailed() + +Dispatches a message to clear all completed downloads + + + ### setDefaultWindowSize(size) Sets the default window size diff --git a/docs/state.md b/docs/state.md index 9930d278db3..40d4779921f 100644 --- a/docs/state.md +++ b/docs/state.md @@ -461,14 +461,15 @@ WindowStore exchangeName: string, // the name of the BTC exchange exchangeURL: string // the URL of the BTC exchange }, + recoverySucceeded: boolean // the status of an attempted recovery paymentIMG: string, // the QR code equivalent of `paymentURL` expressed as "data:image/...;base64,..." error: { // non-null if the last updateLedgerInfo happened concurrently with an error caller: string // function in which error was handled error: object // error object returned } }, - publisherInfo: [ // one entry for each publisher having a non-zero `score` - { + publisherInfo: { + synopsis: [ { // one entry for each publisher having a non-zero `score` rank: number, // i.e., 1, 2, 3, ... verified: boolean, // there is a verified wallet for this publisher site: string, // publisher name, e.g., "wikipedia.org" @@ -482,8 +483,12 @@ WindowStore percentage: number, // i.e., 0, 1, ... 100 publisherURL: string, // publisher site, e.g., "https://wikipedia.org/" faviconURL: string // i.e., "data:image/...;base64,..." + } ], + synopsisOptions: { + minDuration: number, // e.g., 8000 for 8 seconds + minPublisherVisits: number // e.g., 0 } - ], + } autofillAddressDetail: { name: string, organization: string, diff --git a/js/about/aboutActions.js b/js/about/aboutActions.js index 02e3207122c..d2a818c863b 100644 --- a/js/about/aboutActions.js +++ b/js/about/aboutActions.js @@ -87,6 +87,36 @@ const aboutActions = { }) }, + /** + * Generates a file with the users backup keys + */ + ledgerGenerateKeyFile: function (backupAction) { + aboutActions.dispatchAction({ + actionType: appConstants.APP_BACKUP_KEYS, + backupAction + }) + }, + + /** + * Recover wallet by merging old wallet into new one + */ + ledgerRecoverWallet: function (firstRecoveryKey, secondRecoveryKey) { + aboutActions.dispatchAction({ + actionType: appConstants.APP_RECOVER_WALLET, + firstRecoveryKey, + secondRecoveryKey + }) + }, + + /** + * Clear wallet recovery status + */ + clearRecoveryStatus: function () { + aboutActions.dispatchAction({ + actionType: appConstants.APP_CLEAR_RECOVERY + }) + }, + /** * Click through a certificate error. * diff --git a/js/about/preferences.js b/js/about/preferences.js index b18a8ab0c65..77c9829b876 100644 --- a/js/about/preferences.js +++ b/js/about/preferences.js @@ -721,7 +721,13 @@ class TabsTab extends ImmutableComponent { class PaymentsTab extends ImmutableComponent { constructor () { super() + + this.printKeys = this.printKeys.bind(this) + this.saveKeys = this.saveKeys.bind(this) this.createWallet = this.createWallet.bind(this) + this.recoverWallet = this.recoverWallet.bind(this) + this.handleFirstRecoveryKeyChange = this.handleFirstRecoveryKeyChange.bind(this) + this.handleSecondRecoveryKeyChange = this.handleSecondRecoveryKeyChange.bind(this) } createWallet () { @@ -730,6 +736,38 @@ class PaymentsTab extends ImmutableComponent { } } + handleFirstRecoveryKeyChange (e) { + this.setState({FirstRecoveryKey: e.target.value}) + } + + handleSecondRecoveryKeyChange (e) { + this.setState({SecondRecoveryKey: e.target.value}) + } + + recoverWallet () { + aboutActions.ledgerRecoverWallet(this.state.FirstRecoveryKey, this.state.SecondRecoveryKey) + } + + copyToClipboard (text) { + aboutActions.setClipboard(text) + } + + generateKeyFile (backupAction) { + aboutActions.ledgerGenerateKeyFile(backupAction) + } + + clearRecoveryStatus () { + aboutActions.clearRecoveryStatus() + } + + printKeys () { + this.generateKeyFile('print') + } + + saveKeys () { + this.generateKeyFile('save') + } + get enabled () { return getSetting(settings.PAYMENTS_ENABLED, this.props.settings) } @@ -839,6 +877,143 @@ class PaymentsTab extends ImmutableComponent { } + get advancedSettingsContent () { + const minDuration = this.props.ledgerData.get('minDuration') + const minPublisherVisits = this.props.ledgerData.get('minPublisherVisits') + + return
+
+
+
+ + + + + +
+ + + + + +
+
+ {this.enabled + ? + : null} +
+
+
+ } + + get advancedSettingsFooter () { + return
+
+ } + + get ledgerBackupContent () { + const paymentId = this.props.ledgerData.get('paymentId') + const passphrase = this.props.ledgerData.get('passphrase') + + return
+
+ +
+
+
+
+

+ {paymentId} +

+
+
+
+
+
+

+ {passphrase} +

+
+
+
+ } + + get ledgerBackupFooter () { + return
+
+ } + + get ledgerRecoveryContent () { + const l10nDataArgs = { + balance: this.props.ledgerData.get('balance') + } + return
+ { + this.props.ledgerData.get('recoverySucceeded') === true + ?
+

Success!

+

+

+ : null + } + { + this.props.ledgerData.get('recoverySucceeded') === false + ?
+

Recovery failed

+

Please re-enter keys or try different keys.

+
+ : null + } +
+

+ + + +

+ +

+ + + +

+
+ } + + get ledgerRecoveryFooter () { + return
+
+
+
+ } + get nextReconcileDate () { const ledgerData = this.props.ledgerData if (!ledgerData.get('reconcileStamp')) { @@ -947,6 +1122,21 @@ class PaymentsTab extends ImmutableComponent { ? : null } + { + this.enabled && this.props.advancedSettingsOverlayVisible + ? + : null + } + { + this.enabled && this.props.ledgerBackupOverlayVisible + ? + : null + } + { + this.enabled && this.props.ledgerRecoveryOverlayVisible + ? + : null + }
Brave Payments @@ -957,7 +1147,7 @@ class PaymentsTab extends ImmutableComponent {
- {this.enabled ? : null} +
{ @@ -1387,6 +1577,9 @@ class AboutPreferences extends React.Component { bitcoinOverlayVisible: false, qrcodeOverlayVisible: false, paymentHistoryOverlayVisible: false, + advancedSettingsOverlayVisible: false, + ledgerBackupOverlayVisible: false, + ledgerRecoveryOverlayVisible: false, addFundsOverlayVisible: false, preferenceTab: hash.toUpperCase() in preferenceTabs ? hash : preferenceTabs.GENERAL, hintNumber: this.getNextHintNumber(), @@ -1395,7 +1588,9 @@ class AboutPreferences extends React.Component { settings: Immutable.Map(), siteSettings: Immutable.Map(), braveryDefaults: Immutable.Map(), - ledgerData: Immutable.Map() + ledgerData: Immutable.Map(), + firstRecoveryKey: '', + secondRecoveryKey: '' } aboutActions.checkFlashInstalled() @@ -1415,7 +1610,6 @@ class AboutPreferences extends React.Component { this.setState({ flashInstalled }) }) ipc.on(messages.LANGUAGE, (e, {langCode, languageCodes}) => { - moment.locale(langCode) this.setState({ languageCodes }) }) ipc.send(messages.REQUEST_LANGUAGE) @@ -1515,9 +1709,14 @@ class AboutPreferences extends React.Component { tab = diff --git a/js/actions/appActions.js b/js/actions/appActions.js index 96fca28d8ca..5f7ac70b57d 100644 --- a/js/actions/appActions.js +++ b/js/actions/appActions.js @@ -126,6 +126,24 @@ const appActions = { }) }, + /** + * Dispatches a message to clear all completed downloads + */ + ledgerRecoverySucceeded: function () { + AppDispatcher.dispatch({ + actionType: AppConstants.APP_LEDGER_RECOVERY_SUCCEEDED + }) + }, + + /** + * Dispatches a message to clear all completed downloads + */ + ledgerRecoveryFailed: function () { + AppDispatcher.dispatch({ + actionType: AppConstants.APP_LEDGER_RECOVERY_FAILED + }) + }, + /** * Sets the default window size * @param {Array} size - [width, height] diff --git a/js/components/frame.js b/js/components/frame.js index 02d01057e73..8b09e9c733a 100644 --- a/js/components/frame.js +++ b/js/components/frame.js @@ -77,7 +77,7 @@ class Frame extends ImmutableComponent { let location = getBaseUrl(this.props.location) if (location === 'about:preferences') { ipc.send(messages.CHECK_BITCOIN_HANDLER, FrameStateUtil.getPartition(this.frame)) - const ledgerData = this.props.ledgerInfo.merge(this.props.publisherInfo).toJS() + const ledgerData = this.props.ledgerInfo.merge(this.props.publisherInfo).merge(this.props.preferencesData).toJS() this.webview.send(messages.LEDGER_UPDATED, ledgerData) this.webview.send(messages.SETTINGS_UPDATED, this.props.settings ? this.props.settings.toJS() : null) this.webview.send(messages.SITE_SETTINGS_UPDATED, this.props.allSiteSettings ? this.props.allSiteSettings.toJS() : null) diff --git a/js/components/main.js b/js/components/main.js index 57e2bfa68ef..c6f3332c04a 100644 --- a/js/components/main.js +++ b/js/components/main.js @@ -1106,6 +1106,9 @@ class Main extends ImmutableComponent { extensions={frame.get('location') === 'about:extensions' ? this.props.appState.get('extensions') || emptyMap : null} + preferencesData={frame.get('location') === 'about:preferences#payments' + ? this.props.appState.getIn(['ui', 'about', 'preferences']) || emptyMap + : null} downloads={this.props.appState.get('downloads') || emptyMap} bookmarkFolders={frame.get('location') === 'about:bookmarks' ? this.props.appState.get('sites') diff --git a/js/constants/appConfig.js b/js/constants/appConfig.js index 46ff517e6a2..add9ca97053 100644 --- a/js/constants/appConfig.js +++ b/js/constants/appConfig.js @@ -125,6 +125,8 @@ module.exports = { 'advanced.pdfjs-enabled': true, 'advanced.smooth-scroll-enabled': false, 'advanced.send-crash-reports': true, + 'advanced.minimum-visit-time': 8, + 'advanced.minimum-visits': 5, 'shutdown.clear-history': false, 'shutdown.clear-downloads': false, 'shutdown.clear-cache': false, diff --git a/js/constants/appConstants.js b/js/constants/appConstants.js index 95328216302..dfb9a19896a 100644 --- a/js/constants/appConstants.js +++ b/js/constants/appConstants.js @@ -32,12 +32,17 @@ const AppConstants = { APP_CLEAR_DATA: _, APP_IMPORT_BROWSER_DATA: _, APP_UPDATE_LEDGER_INFO: _, + APP_LEDGER_RECOVERY_SUCCEEDED: _, + APP_LEDGER_RECOVERY_FAILED: _, APP_UPDATE_PUBLISHER_INFO: _, APP_SHOW_MESSAGE_BOX: _, /** @param {Object} detail */ APP_HIDE_MESSAGE_BOX: _, /** @param {string} message */ APP_CLEAR_MESSAGE_BOXES: _, /** @param {string} origin */ APP_ADD_WORD: _, /** @param {string} word, @param {boolean} learn */ APP_SET_DICTIONARY: _, /** @param {string} locale */ + APP_BACKUP_KEYS: _, + APP_RECOVER_WALLET: _, + APP_CLEAR_RECOVERY: _, APP_ADD_AUTOFILL_ADDRESS: _, APP_REMOVE_AUTOFILL_ADDRESS: _, APP_ADD_AUTOFILL_CREDIT_CARD: _, diff --git a/js/constants/settings.js b/js/constants/settings.js index 7d2e5dd71a9..99dd7671be1 100644 --- a/js/constants/settings.js +++ b/js/constants/settings.js @@ -58,8 +58,9 @@ const settings = { DEFAULT_ZOOM_LEVEL: 'advanced.default-zoom-level', SMOOTH_SCROLL_ENABLED: 'advanced.smooth-scroll-enabled', SEND_CRASH_REPORTS: 'advanced.send-crash-reports', - - ADBLOCK_CUSTOM_RULES: 'adblock.customRules' + ADBLOCK_CUSTOM_RULES: 'adblock.customRules', + MINIMUM_VISIT_TIME: 'advanced.minimum-visit-time', + MINIMUM_VISTS: 'advanced.minimum-visits' } module.exports = settings diff --git a/js/constants/windowConstants.js b/js/constants/windowConstants.js index b0a4a1c88f7..b5fba4a9d16 100644 --- a/js/constants/windowConstants.js +++ b/js/constants/windowConstants.js @@ -9,6 +9,7 @@ const windowConstants = { WINDOW_SET_URL: _, WINDOW_SET_NAVBAR_INPUT: _, WINDOW_NEW_FRAME: _, + WINDOW_VIEW_KEY: _, WINDOW_CLONE_FRAME: _, WINDOW_CLOSE_FRAME: _, WINDOW_SET_ACTIVE_FRAME: _, diff --git a/js/stores/appStore.js b/js/stores/appStore.js index 4e76d5d3e4b..d7935aa4bf9 100644 --- a/js/stores/appStore.js +++ b/js/stores/appStore.js @@ -27,7 +27,7 @@ const getSetting = require('../settings').getSetting const EventEmitter = require('events').EventEmitter const Immutable = require('immutable') const diff = require('immutablediff') -const debounce = require('../lib/debounce') +const debounce = require('../lib/debounce.js') const locale = require('../../app/locale') const path = require('path') const {channel} = require('../../app/channel') @@ -328,6 +328,8 @@ function handleChangeSettingAction (settingKey, settingValue) { } const handleAppAction = (action) => { + const ledger = require('../../app/ledger') + switch (action.actionType) { case AppConstants.APP_SET_STATE: appState = action.appState @@ -585,6 +587,21 @@ const handleAppAction = (action) => { case AppConstants.APP_SET_DICTIONARY: appState = appState.setIn(['dictionary', 'locale'], action.locale) break + case AppConstants.APP_BACKUP_KEYS: + appState = ledger.backupKeys(appState, action) + break + case AppConstants.APP_RECOVER_WALLET: + appState = ledger.recoverKeys(appState, action) + break + case AppConstants.APP_LEDGER_RECOVERY_SUCCEEDED: + appState = appState.setIn(['ui', 'about', 'preferences', 'recoverySucceeded'], true) + break + case AppConstants.APP_LEDGER_RECOVERY_FAILED: + appState = appState.setIn(['ui', 'about', 'preferences', 'recoverySucceeded'], false) + break + case AppConstants.APP_CLEAR_RECOVERY: + appState = appState.setIn(['ui', 'about', 'preferences', 'recoverySucceeded'], undefined) + break case AppConstants.APP_CLEAR_DATA: if (action.clearDataDetail.get('browserHistory')) { handleAppAction({actionType: AppConstants.APP_CLEAR_HISTORY}) diff --git a/js/stores/windowStore.js b/js/stores/windowStore.js index 194fc2c7bae..b2cb6908d53 100644 --- a/js/stores/windowStore.js +++ b/js/stores/windowStore.js @@ -460,6 +460,9 @@ const doAction = (action) => { case WindowConstants.WINDOW_NEW_FRAME: newFrame(action.frameOpts, action.openInForeground) break + case WindowConstants.WINDOW_VIEW_KEY: + newFrame(action.frameOpts, action.openInForeground) + break case WindowConstants.WINDOW_CLONE_FRAME: let insertionIndex = FrameStateUtil.findIndexForFrameKey(windowState.get('frames'), action.frameOpts.key) + 1 newFrame(FrameStateUtil.cloneFrame(action.frameOpts, action.guestInstanceId), action.openInForeground, insertionIndex) diff --git a/less/about/preferences.less b/less/about/preferences.less index 0dd5ca67b01..155020568b4 100644 --- a/less/about/preferences.less +++ b/less/about/preferences.less @@ -797,6 +797,14 @@ div.nextPaymentSubmission { } } +#bitcoinDashboard { + .board { + .panel { + padding-left: 100px; + } + } +} + .board { overflow-x: hidden; clear: both; @@ -806,7 +814,16 @@ div.nextPaymentSubmission { position: relative; margin-top: 8px; margin-bottom: 8px; - padding: 20px 20px 20px 100px; + padding: 20px; + + &.advancedSettings { + padding-left: 50px; + padding-right: 50px; + + select { + width: 100%; + } + } .settingsPanelDivider { width: 50%; @@ -903,6 +920,31 @@ div.nextPaymentSubmission { display:block; clear:both; } + + .ledgerBackupContent { + width: 75%; + margin: 0 auto; + display: block; + line-height: 1.3em; + margin-bottom: 60px; + } + + .copyKeyContainer { + display: flex; + justify-content: space-between; + width: 75%; + margin: 20px auto; + + .copyContainer { + margin-top: 35px; + } + + .keyContainer { + h3 { + margin-bottom: 15px; + } + } + } } .panelFooter { color: @darkGray; @@ -923,6 +965,28 @@ div.nextPaymentSubmission { padding: 15px 0; } } + + .recoveryContent { + h4, + span { + margin-bottom: 20px; + } + } + + .recoveryFooterButtons { + float: right; + } +} + +.advancedSettingsFooter { + padding: 20px 100px; + text-align: right; +} + +.recoveryOverlay { + .spaceAround { + margin: 50px auto; + } } #flashInfoIcon { diff --git a/less/button.less b/less/button.less index a9310cf3995..893baa81d56 100644 --- a/less/button.less +++ b/less/button.less @@ -118,6 +118,10 @@ span.browserButton, padding: 3px 35px; } + &.inlineButton { + display: inline; + } + &.secondaryButton { background-color: #eee; } diff --git a/less/modalOverlay.less b/less/modalOverlay.less index 76f47a4b10b..58a19eedc73 100644 --- a/less/modalOverlay.less +++ b/less/modalOverlay.less @@ -97,6 +97,7 @@ border: solid 1px @lightGray; border-radius: @borderRadius; box-shadow: @dialogShadow; + position: relative; z-index: 9000; box-sizing: border-box; @@ -114,6 +115,27 @@ .sectionTitle { color: @darkGray; } + + .recoveryOverlay { + background-color: @black75; + border: 1px solid @black75; + position: absolute; + top: -1px; + left: -1px; + width: 100%; + height: 100%; + text-align: center; + z-index: 999; + + h1, + p { + color: white; + } + + h1 { + margin-top: 120px; + } + } } button { diff --git a/less/variables.less b/less/variables.less index 918c7271915..e9341bea578 100644 --- a/less/variables.less +++ b/less/variables.less @@ -94,6 +94,7 @@ @black10: rgba(0, 0, 0, 0.1); @black25: rgba(0, 0, 0, 0.25); @black50: rgba(0, 0, 0, 0.5); +@black75: rgba(0, 0, 0, 0.75); @buttonShadow: 0px 1px 5px -1px rgba(0, 0, 0, 1.0); @dialogShadow: 0px 8px 22px 0px rgba(0, 0, 0, .5);