From c2b881e10e3fb19ac025f00a81b7046e89cf365b Mon Sep 17 00:00:00 2001 From: jessop Date: Tue, 20 Aug 2019 08:29:59 -0400 Subject: [PATCH] recommit --- src/ui/component/common/icon-custom.jsx | 12 + .../component/walletSecurityAndSync/index.js | 68 ++++ .../component/walletSecurityAndSync/view.jsx | 340 ++++++++++++++++++ src/ui/constants/action_types.js | 1 + src/ui/constants/icons.js | 2 + src/ui/constants/keychain.js | 3 + src/ui/constants/modal_types.js | 2 + src/ui/modal/modalPasswordUnsave/index.js | 15 + src/ui/modal/modalPasswordUnsave/view.jsx | 38 ++ src/ui/modal/modalRouter/view.jsx | 3 + src/ui/modal/modalWalletDecrypt/index.js | 5 +- src/ui/modal/modalWalletDecrypt/view.jsx | 5 + src/ui/modal/modalWalletEncrypt/index.js | 11 +- src/ui/modal/modalWalletEncrypt/view.jsx | 23 +- src/ui/modal/modalWalletUnlock/index.js | 5 +- src/ui/modal/modalWalletUnlock/view.jsx | 26 +- src/ui/page/settings/index.js | 3 + src/ui/page/settings/view.jsx | 33 +- src/ui/reducers.js | 2 + src/ui/redux/actions/app.js | 21 ++ src/ui/redux/reducers/app.js | 7 + src/ui/redux/selectors/app.js | 5 + src/ui/util/saved-passwords.js | 10 + webpack.base.config.js | 1 + 24 files changed, 625 insertions(+), 16 deletions(-) create mode 100644 src/ui/component/walletSecurityAndSync/index.js create mode 100644 src/ui/component/walletSecurityAndSync/view.jsx create mode 100644 src/ui/constants/keychain.js create mode 100644 src/ui/modal/modalPasswordUnsave/index.js create mode 100644 src/ui/modal/modalPasswordUnsave/view.jsx create mode 100644 src/ui/util/saved-passwords.js diff --git a/src/ui/component/common/icon-custom.jsx b/src/ui/component/common/icon-custom.jsx index b6eacb5ab..d0123f8d3 100644 --- a/src/ui/component/common/icon-custom.jsx +++ b/src/ui/component/common/icon-custom.jsx @@ -276,6 +276,18 @@ export const icons = { ), + [ICONS.EYE]: buildIcon( + + + + + ), + [ICONS.EYE_OFF]: buildIcon( + + + + + ), [ICONS.VIEW]: buildIcon( diff --git a/src/ui/component/walletSecurityAndSync/index.js b/src/ui/component/walletSecurityAndSync/index.js new file mode 100644 index 000000000..1493fa5f3 --- /dev/null +++ b/src/ui/component/walletSecurityAndSync/index.js @@ -0,0 +1,68 @@ +import * as SETTINGS from 'constants/settings'; +import { connect } from 'react-redux'; +import { + doWalletStatus, + doWalletEncrypt, + doWalletDecrypt, + selectWalletEncryptSucceeded, + selectWalletEncryptPending, + selectWalletEncryptResult, + selectWalletIsEncrypted, + selectHasTransactions, +} from 'lbry-redux'; +import { doPasswordSaved } from 'redux/actions/app'; +import WalletSecurityAndSync from './view'; +import { + doCheckSync, + doGetSync, + doSetDefaultAccount, + doSyncApply, + selectHasSyncedWallet, + selectGetSyncIsPending, + selectSetSyncIsPending, + selectSyncApplyIsPending, + selectSyncApplyErrorMessage, + selectSyncData, + selectSyncHash, + selectHashChanged, + selectUser, +} from 'lbryinc'; +import { selectIsPasswordSaved } from 'redux/selectors/app'; +import { doSetClientSetting } from 'redux/actions/settings'; +import { makeSelectClientSetting } from 'redux/selectors/settings'; + +const select = state => ({ + walletEncryptSucceeded: selectWalletEncryptSucceeded(state), + walletEncryptPending: selectWalletEncryptPending(state), + walletEncryptResult: selectWalletEncryptResult(state), + walletEncrypted: selectWalletIsEncrypted(state), + walletHasTransactions: selectHasTransactions(state), + user: selectUser(state), + syncEnabled: makeSelectClientSetting(SETTINGS.ENABLE_SYNC)(state), + hasSyncedWallet: selectHasSyncedWallet(state), + getSyncIsPending: selectGetSyncIsPending(state), + setSyncIsPending: selectSetSyncIsPending(state), + syncApplyIsPending: selectSyncApplyIsPending(state), + syncApplyErrorMessage: selectSyncApplyErrorMessage(state), + syncData: selectSyncData(state), + syncHash: selectSyncHash(state), + isPasswordSaved: selectIsPasswordSaved(state), + hashChanged: selectHashChanged(state), +}); + +const perform = dispatch => ({ + encryptWallet: password => dispatch(doWalletEncrypt(password)), + decryptWallet: () => dispatch(doWalletDecrypt()), + updateWalletStatus: () => dispatch(doWalletStatus()), + setPasswordSaved: saved => dispatch(doPasswordSaved(saved)), + syncApply: (hash, data, password) => dispatch(doSyncApply(hash, data, password)), + getSync: password => dispatch(doGetSync(password)), + checkSync: () => dispatch(doCheckSync()), + setDefaultAccount: () => dispatch(doSetDefaultAccount()), + setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)), +}); + +export default connect( + select, + perform +)(WalletSecurityAndSync); diff --git a/src/ui/component/walletSecurityAndSync/view.jsx b/src/ui/component/walletSecurityAndSync/view.jsx new file mode 100644 index 000000000..7fb027793 --- /dev/null +++ b/src/ui/component/walletSecurityAndSync/view.jsx @@ -0,0 +1,340 @@ +// @flow +import React, { useState, useEffect } from 'react'; +import { Form, FormField, Submit } from 'component/common/form'; +import Button from 'component/button'; +import UserEmail from 'component/userEmail'; +import * as ICONS from 'constants/icons'; + +import { getSavedPassword, setSavedPassword, deleteSavedPassword } from 'util/saved-passwords'; +// import { AUTH_ORG } from 'constants/keychain'; + +/* +On mount: checkSync() + if +Display on mount + if !email + SyncWallet.disabled = true + SyncWallet.checked = false + if walletEncrypted + EncryptWallet.checked = true + password = savedPassword ? savedPassword : '' + rememberPassword.checked = savedPassword + if !walletEncrypted + password = savedPassword ? savedPassword : '' + rememberPassword.checked = savedPassword + else email + if walletEncrypted + EncryptWallet.checked = true + password = savedPassword ? savedPassword : '' + rememberPassword.checked = savedPassword + syncEnabled = settings.syncEnabled + if syncEnabled + message = 'hi' + else + message = 'hi' + else if !walletEncrypted + EncryptWallet.checked = false + password = savedPassword ? savedPassword : '' + rememberPassword.checked = savedPassword + syncEnabled = settings.syncEnabled + if syncEnabled + message = 'hi' + else + message = 'hi' + */ +type Props = { + // wallet statuses + walletEncryptSucceeded: boolean, + walletEncryptPending: boolean, + walletDecryptSucceeded: boolean, + walletDecryptPending: boolean, + updateWalletStatus: boolean, + walletEncrypted: boolean, + // wallet methods + encryptWallet: (?string) => void, + decryptWallet: (?string) => void, + updateWalletStatus: () => void, + // housekeeping + setPasswordSaved: () => void, + syncEnabled: boolean, + setClientSetting: (string, boolean | string) => void, + isPasswordSaved: boolean, + // data + user: any, + // sync statuses + hasSyncedWallet: boolean, + getSyncIsPending?: boolean, + syncApplyErrorMessage?: string, + hashChanged: boolean, + // sync data + syncData: string | null, + syncHash: string | null, + // sync methods + syncApply: (string | null, string | null, string) => void, + checkSync: () => void, + setDefaultAccount: () => void, + hasTransactions: boolean, +}; + +type State = { + newPassword: string, + newPasswordConfirm: string, + passwordMatch: boolean, + understandConfirmed: boolean, + understandError: boolean, + submitted: boolean, + failMessage: ?string, + rememberPassword: boolean, + showEmailReg: boolean, + failed: boolean, + enableSync: boolean, + encryptWallet: boolean, + obscurePassword: boolean, +}; + +function WalletSecurityAndSync(props: Props) { + const { + // walletEncryptSucceeded, + // walletEncryptPending, + // walletDecryptSucceeded, + // walletDecryptPending, + // updateWalletStatus, + walletEncrypted, + encryptWallet, + decryptWallet, + // setPasswordSaved, + syncEnabled, + // setClientSetting, + // isPasswordSaved, + user, + hasSyncedWallet, + getSyncIsPending, + syncApplyErrorMessage, + hashChanged, + syncData, + syncHash, + syncApply, + checkSync, + hasTransactions, + // setDefaultAccount, + } = props; + + const defaultComponentState: State = { + newPassword: '', + newPasswordConfirm: '', + passwordMatch: false, + understandConfirmed: false, + understandError: false, + submitted: false, // Prior actions could be marked complete + failMessage: undefined, + rememberPassword: false, + showEmailReg: false, + failed: false, + enableSync: syncEnabled, + encryptWallet: walletEncrypted, + obscurePassword: true, + }; + const [componentState, setComponentState] = useState(defaultComponentState); + + const safeToSync = !hasTransactions || !hashChanged; + + useEffect(() => { + checkSync(); + }, []); + useEffect(() => { + setComponentState({ + ...componentState, + passwordMatch: componentState.newPassword === componentState.newPasswordConfirm, + }); + }, [componentState.newPassword, componentState.newPasswordConfirm]); + + const isEmailVerified = user && user.primary_email && user.has_verified_email; + // const syncDisabledMessage = 'You cannot sync without an email'; + + function onChangeNewPassword(event: SyntheticInputEvent<>) { + setComponentState({ ...componentState, newPassword: event.target.value }); + } + + function onChangeRememberPassword(event: SyntheticInputEvent<>) { + setComponentState({ ...componentState, rememberPassword: event.target.checked }); + } + + function onChangeNewPasswordConfirm(event: SyntheticInputEvent<>) { + setComponentState({ ...componentState, newPasswordConfirm: event.target.value }); + } + + function onChangeUnderstandConfirm(event: SyntheticInputEvent<>) { + setComponentState({ ...componentState, understandConfirmed: /^.?i understand.?$/i.test(event.target.value) }); + } + + function onChangeSync(event: SyntheticInputEvent<>) { + setComponentState({ ...componentState, enableSync: event.target.checked }); + } + + function onChangeEncrypt(event: SyntheticInputEvent<>) { + setComponentState({ ...componentState, encryptWallet: event.target.checked }); + } + + async function apply() { + setComponentState({ ...componentState, failed: false }); + + await checkSync(); + if (componentState.enableSync) { + await syncApply(syncHash, syncData, componentState.newPassword); + if (syncApplyErrorMessage) { + setComponentState({ ...componentState, failed: true }); + } + } + await decryptWallet(); + if (componentState.failed !== true) { + await encryptWallet(componentState.newPassword); + } + if (componentState.encryptWallet) { + await encryptWallet(componentState.newPassword); + } + + if (componentState.failed === false) { + } + // this.setState({ submitted: true }); + // this.props.encryptWallet(state.newPassword); + } + + return ( +
+

{__('Wallet Sync and Security')}

+ {!isEmailVerified && ( + +

+ {__(`It looks like we don't have your email.`)}{' '} +

+ ); +} + +export default WalletSecurityAndSync; diff --git a/src/ui/constants/action_types.js b/src/ui/constants/action_types.js index d3065d3f7..40e45a281 100644 --- a/src/ui/constants/action_types.js +++ b/src/ui/constants/action_types.js @@ -19,6 +19,7 @@ export const SHOW_MODAL = 'SHOW_MODAL'; export const HIDE_MODAL = 'HIDE_MODAL'; export const CHANGE_MODALS_ALLOWED = 'CHANGE_MODALS_ALLOWED'; export const TOGGLE_SEARCH_EXPANDED = 'TOGGLE_SEARCH_EXPANDED'; +export const PASSWORD_SAVED = 'PASSWORD_SAVED'; // Navigation export const CHANGE_AFTER_AUTH_PATH = 'CHANGE_AFTER_AUTH_PATH'; diff --git a/src/ui/constants/icons.js b/src/ui/constants/icons.js index d93ffeb86..b2a88790f 100644 --- a/src/ui/constants/icons.js +++ b/src/ui/constants/icons.js @@ -73,3 +73,5 @@ export const SUPPORT = 'TrendingUp'; export const BLOCK = 'Slash'; export const UNBLOCK = 'Circle'; export const VIEW = 'View'; +export const EYE = 'Eye'; +export const EYE_OFF = 'EyeOff'; diff --git a/src/ui/constants/keychain.js b/src/ui/constants/keychain.js new file mode 100644 index 000000000..e4d8994b6 --- /dev/null +++ b/src/ui/constants/keychain.js @@ -0,0 +1,3 @@ +export const AUTH_ORG = 'LBRY'; +export const KEY_WALLET_PASSWORD = 'wallet_password'; +export const KEY_AUTH_TOKEN = 'auth_token'; diff --git a/src/ui/constants/modal_types.js b/src/ui/constants/modal_types.js index 8cd8ee674..244a50e8f 100644 --- a/src/ui/constants/modal_types.js +++ b/src/ui/constants/modal_types.js @@ -28,3 +28,5 @@ export const CONFIRM_THUMBNAIL_UPLOAD = 'confirm_thumbnail_upload'; export const WALLET_ENCRYPT = 'wallet_encrypt'; export const WALLET_DECRYPT = 'wallet_decrypt'; export const WALLET_UNLOCK = 'wallet_unlock'; +export const WALLET_SYNC = 'wallet_sync'; +export const WALLET_PASSWORD_UNSAVE = 'wallet_password_unsave'; diff --git a/src/ui/modal/modalPasswordUnsave/index.js b/src/ui/modal/modalPasswordUnsave/index.js new file mode 100644 index 000000000..a4f84c9c9 --- /dev/null +++ b/src/ui/modal/modalPasswordUnsave/index.js @@ -0,0 +1,15 @@ +import { connect } from 'react-redux'; +import ModalPasswordUnsave from './view'; +import { doHideModal, doPasswordSaved } from 'redux/actions/app'; + +// const select = () => ({}); +// +const perform = dispatch => ({ + closeModal: () => dispatch(doHideModal()), + setPasswordSaved: saved => dispatch(doPasswordSaved(saved)), +}); + +export default connect( + null, + perform +)(ModalPasswordUnsave); diff --git a/src/ui/modal/modalPasswordUnsave/view.jsx b/src/ui/modal/modalPasswordUnsave/view.jsx new file mode 100644 index 000000000..84697da99 --- /dev/null +++ b/src/ui/modal/modalPasswordUnsave/view.jsx @@ -0,0 +1,38 @@ +// @flow +import React from 'react'; +import { Modal } from 'modal/modal'; +import keytar from 'keytar'; + +type Props = { + closeModal: () => void, + setPasswordSaved: boolean => void, +}; + +class ModalPasswordUnsave extends React.PureComponent { + render() { + return ( + + keytar.deletePassword('LBRY', 'wallet_password').then(() => { + this.props.setPasswordSaved(false); + this.props.closeModal(); + }) + } + onAborted={this.props.closeModal} + > +

+ {__('You are about to delete your saved password.')}{' '} + {__('Your wallet will still be encrypted, but you will have to remember and enter it manually on startup.')} +

+
+ ); + } +} + +export default ModalPasswordUnsave; diff --git a/src/ui/modal/modalRouter/view.jsx b/src/ui/modal/modalRouter/view.jsx index af910c611..25a81cc04 100644 --- a/src/ui/modal/modalRouter/view.jsx +++ b/src/ui/modal/modalRouter/view.jsx @@ -28,6 +28,7 @@ import ModalWalletEncrypt from 'modal/modalWalletEncrypt'; import ModalWalletDecrypt from 'modal/modalWalletDecrypt'; import ModalWalletUnlock from 'modal/modalWalletUnlock'; import ModalRewardCode from 'modal/modalRewardCode'; +import ModalPasswordUnsave from 'modal/modalPasswordUnsave'; type Props = { modal: { id: string, modalProps: {} }, @@ -100,6 +101,8 @@ function ModalRouter(props: Props) { return ; case MODALS.WALLET_UNLOCK: return ; + case MODALS.WALLET_PASSWORD_UNSAVE: + return ; case MODALS.REWARD_GENERATED_CODE: return ; default: diff --git a/src/ui/modal/modalWalletDecrypt/index.js b/src/ui/modal/modalWalletDecrypt/index.js index 6ed8a8b0d..c2266fc79 100644 --- a/src/ui/modal/modalWalletDecrypt/index.js +++ b/src/ui/modal/modalWalletDecrypt/index.js @@ -1,16 +1,19 @@ import { connect } from 'react-redux'; import { doWalletStatus, doWalletDecrypt, selectWalletDecryptSucceeded } from 'lbry-redux'; -import { doHideModal } from 'redux/actions/app'; +import { doHideModal, doPasswordSaved } from 'redux/actions/app'; import ModalWalletDecrypt from './view'; +import { selectIsPasswordSaved } from 'redux/selectors/app'; const select = state => ({ walletDecryptSucceded: selectWalletDecryptSucceeded(state), + isPasswordSaved: selectIsPasswordSaved(state), }); const perform = dispatch => ({ closeModal: () => dispatch(doHideModal()), decryptWallet: password => dispatch(doWalletDecrypt(password)), updateWalletStatus: () => dispatch(doWalletStatus()), + setPasswordSaved: saved => dispatch(doPasswordSaved(saved)), }); export default connect( diff --git a/src/ui/modal/modalWalletDecrypt/view.jsx b/src/ui/modal/modalWalletDecrypt/view.jsx index 0b92d9a26..9e79ecbdd 100644 --- a/src/ui/modal/modalWalletDecrypt/view.jsx +++ b/src/ui/modal/modalWalletDecrypt/view.jsx @@ -2,12 +2,15 @@ import React from 'react'; import { Modal } from 'modal/modal'; import Button from 'component/button'; +import keytar from 'keytar'; type Props = { closeModal: () => void, decryptWallet: () => void, updateWalletStatus: () => void, walletDecryptSucceded: boolean, + passwordUnsaved: () => void, + setPasswordSaved: boolean => void, }; type State = { @@ -23,6 +26,8 @@ class ModalWalletDecrypt extends React.PureComponent { const { props, state } = this; if (state.submitted && props.walletDecryptSucceded === true) { + keytar.deletePassword('LBRY', 'wallet_password'); + props.setPasswordSaved(false); props.closeModal(); props.updateWalletStatus(); } diff --git a/src/ui/modal/modalWalletEncrypt/index.js b/src/ui/modal/modalWalletEncrypt/index.js index e60839da7..2d736ab23 100644 --- a/src/ui/modal/modalWalletEncrypt/index.js +++ b/src/ui/modal/modalWalletEncrypt/index.js @@ -1,12 +1,6 @@ import { connect } from 'react-redux'; -import { - doWalletStatus, - doWalletEncrypt, - selectWalletEncryptPending, - selectWalletEncryptSucceeded, - selectWalletEncryptResult, -} from 'lbry-redux'; -import { doHideModal } from 'redux/actions/app'; +import { doWalletStatus, doWalletEncrypt, selectWalletEncryptSucceeded, selectWalletEncryptResult } from 'lbry-redux'; +import { doHideModal, doPasswordSaved } from 'redux/actions/app'; import ModalWalletEncrypt from './view'; const select = state => ({ @@ -18,6 +12,7 @@ const perform = dispatch => ({ closeModal: () => dispatch(doHideModal()), encryptWallet: password => dispatch(doWalletEncrypt(password)), updateWalletStatus: () => dispatch(doWalletStatus()), + setPasswordSaved: saved => dispatch(doPasswordSaved(saved)), }); export default connect( diff --git a/src/ui/modal/modalWalletEncrypt/view.jsx b/src/ui/modal/modalWalletEncrypt/view.jsx index 0bcf0f15d..944138ce9 100644 --- a/src/ui/modal/modalWalletEncrypt/view.jsx +++ b/src/ui/modal/modalWalletEncrypt/view.jsx @@ -3,6 +3,7 @@ import React from 'react'; import { Form, FormField, Submit } from 'component/common/form'; import { Modal } from 'modal/modal'; import Button from 'component/button'; +import keytar from 'keytar'; type Props = { closeModal: () => void, @@ -10,6 +11,7 @@ type Props = { updateWalletStatus: boolean, encryptWallet: (?string) => void, updateWalletStatus: () => void, + setPasswordSaved: boolean => void, }; type State = { @@ -20,6 +22,7 @@ type State = { understandError: boolean, submitted: boolean, failMessage: ?string, + rememberPassword: boolean, }; class ModalWalletEncrypt extends React.PureComponent { @@ -31,6 +34,7 @@ class ModalWalletEncrypt extends React.PureComponent { understandError: false, submitted: false, // Prior actions could be marked complete failMessage: undefined, + rememberPassword: false, }; componentDidUpdate() { @@ -51,6 +55,10 @@ class ModalWalletEncrypt extends React.PureComponent { this.setState({ newPassword: event.target.value }); } + onChangeRememberPassword(event: SyntheticInputEvent<>) { + this.setState({ rememberPassword: event.target.checked }); + } + onChangeNewPasswordConfirm(event: SyntheticInputEvent<>) { this.setState({ newPasswordConfirm: event.target.value }); } @@ -79,7 +87,10 @@ class ModalWalletEncrypt extends React.PureComponent { if (invalidEntries === true) { return; } - + if (state.rememberPassword === true) { + this.props.setPasswordSaved(true); + keytar.setPassword('LBRY', 'wallet_password', state.newPassword); + } this.setState({ submitted: true }); this.props.encryptWallet(state.newPassword); } @@ -88,7 +99,6 @@ class ModalWalletEncrypt extends React.PureComponent { const { closeModal } = this.props; const { passwordMismatch, understandError, failMessage } = this.state; - return ( { onChange={event => this.onChangeNewPasswordConfirm(event)} /> + + this.onChangeRememberPassword(event)} + checked={this.state.rememberPassword} + /> +
{__( diff --git a/src/ui/modal/modalWalletUnlock/index.js b/src/ui/modal/modalWalletUnlock/index.js index 721805c44..86c29fdea 100644 --- a/src/ui/modal/modalWalletUnlock/index.js +++ b/src/ui/modal/modalWalletUnlock/index.js @@ -1,6 +1,6 @@ import { connect } from 'react-redux'; -import { doWalletUnlock, selectWalletUnlockPending, selectWalletUnlockSucceeded } from 'lbry-redux'; -import { doQuit, doHideModal } from 'redux/actions/app'; +import { doWalletUnlock, selectWalletUnlockSucceeded } from 'lbry-redux'; +import { doQuit, doHideModal, doPasswordSaved } from 'redux/actions/app'; import ModalWalletUnlock from './view'; const select = state => ({ @@ -11,6 +11,7 @@ const perform = dispatch => ({ closeModal: () => dispatch(doHideModal()), quit: () => dispatch(doQuit()), unlockWallet: password => dispatch(doWalletUnlock(password)), + setPasswordSaved: saved => dispatch(doPasswordSaved(saved)), }); export default connect( diff --git a/src/ui/modal/modalWalletUnlock/view.jsx b/src/ui/modal/modalWalletUnlock/view.jsx index 6233d8214..f34feb2a5 100644 --- a/src/ui/modal/modalWalletUnlock/view.jsx +++ b/src/ui/modal/modalWalletUnlock/view.jsx @@ -3,27 +3,35 @@ import React from 'react'; import { Form, FormField } from 'component/common/form'; import { Modal } from 'modal/modal'; import Button from 'component/button'; +import keytar from 'keytar'; type Props = { quit: () => void, closeModal: () => void, unlockWallet: (?string) => void, walletUnlockSucceded: boolean, + setPasswordSaved: boolean => void, }; type State = { password: string, + rememberPassword: boolean, }; class ModalWalletUnlock extends React.PureComponent { state = { password: '', + rememberPassword: false, }; componentDidUpdate() { const { props } = this; if (props.walletUnlockSucceded === true) { + if (this.state.rememberPassword) { + this.props.setPasswordSaved(true); + keytar.setPassword('LBRY', 'wallet_password', this.state.password); + } props.closeModal(); } } @@ -32,11 +40,14 @@ class ModalWalletUnlock extends React.PureComponent { this.setState({ password: event.target.value }); } + onChangeRememberPassword(event: SyntheticInputEvent<>) { + this.setState({ rememberPassword: event.target.checked }); + } + render() { const { quit, unlockWallet, walletUnlockSucceded } = this.props; - const { password } = this.state; - + const { password, rememberPassword } = this.state; return ( { type="password" name="wallet-password" onChange={event => this.onChangePassword(event)} + value={password || ''} /> + + this.onChangeRememberPassword(event)} + checked={rememberPassword} + helper={__('You will no longer see this at startup')} + /> + ); diff --git a/src/ui/page/settings/index.js b/src/ui/page/settings/index.js index 41eab4c95..853bb5a6b 100644 --- a/src/ui/page/settings/index.js +++ b/src/ui/page/settings/index.js @@ -2,6 +2,7 @@ import { connect } from 'react-redux'; import * as SETTINGS from 'constants/settings'; import { doClearCache, doNotifyEncryptWallet, doNotifyDecryptWallet } from 'redux/actions/app'; import { doSetDaemonSetting, doSetClientSetting, doGetThemes, doSetDarkTime } from 'redux/actions/settings'; +import { selectIsPasswordSaved } from 'redux/selectors/app'; import { doSetPlayingUri } from 'redux/actions/content'; import { makeSelectClientSetting, selectDaemonSettings, selectosNotificationsEnabled } from 'redux/selectors/settings'; import { doWalletStatus, selectWalletIsEncrypted, selectBlockedChannelsCount } from 'lbry-redux'; @@ -34,6 +35,8 @@ const perform = dispatch => ({ encryptWallet: () => dispatch(doNotifyEncryptWallet()), decryptWallet: () => dispatch(doNotifyDecryptWallet()), updateWalletStatus: () => dispatch(doWalletStatus()), + confirmForgetPassword: modalProps => dispatch(doNotifyForgetPassword(modalProps)), + setPasswordSaved: saved => dispatch(doPasswordSaved(saved)), clearPlayingUri: () => dispatch(doSetPlayingUri(null)), setDarkTime: (time, options) => dispatch(doSetDarkTime(time, options)), }); diff --git a/src/ui/page/settings/view.jsx b/src/ui/page/settings/view.jsx index ac2c97195..f29aab379 100644 --- a/src/ui/page/settings/view.jsx +++ b/src/ui/page/settings/view.jsx @@ -11,6 +11,9 @@ import I18nMessage from 'component/i18nMessage'; import Page from 'component/page'; import SettingLanguage from 'component/settingLanguage'; import FileSelector from 'component/common/file-selector'; +import UnsupportedOnWeb from 'component/common/unsupported-on-web'; +import keytar from 'keytar'; +import WalletSecurityAndSync from '../../component/walletSecurityAndSync'; type Price = { currency: string, @@ -60,6 +63,9 @@ type Props = { supportOption: boolean, userBlockedChannelsCount?: number, hideBalance: boolean, + confirmForgetPassword: () => void, + isPasswordSaved: boolean, + setPasswordSaved: boolean => void, floatingPlayer: boolean, clearPlayingUri: () => void, darkModeTimes: DarkModeTimes, @@ -68,6 +74,7 @@ type Props = { type State = { clearingCache: boolean, + storedPassword: boolean, }; class SettingsPage extends React.PureComponent { @@ -76,6 +83,7 @@ class SettingsPage extends React.PureComponent { this.state = { clearingCache: false, + storedPassword: false, }; (this: any).onKeyFeeChange = this.onKeyFeeChange.bind(this); @@ -91,6 +99,13 @@ class SettingsPage extends React.PureComponent { componentDidMount() { this.props.getThemes(); this.props.updateWalletStatus(); + keytar.getPassword('LBRY', 'wallet_password').then(p => { + if (p || p === '') { + this.props.setPasswordSaved(true); + } else { + this.props.setPasswordSaved(false); + } + }); } onKeyFeeChange(newValue: Price) { @@ -137,6 +152,11 @@ class SettingsPage extends React.PureComponent { } } + onConfirmForgetPassword() { + const { confirmForgetPassword } = this.props; + confirmForgetPassword(); + } + onChangeTime(event: SyntheticInputEvent<*>, options: OptionTimes) { const { value } = event.target; @@ -189,6 +209,7 @@ class SettingsPage extends React.PureComponent { floatingPlayer, clearPlayingUri, darkModeTimes, + isPasswordSaved, } = this.props; const noDaemonSettings = !daemonSettings || Object.keys(daemonSettings).length === 0; @@ -229,6 +250,7 @@ class SettingsPage extends React.PureComponent {

{__('LBRY downloads will be saved here.')}

+

{__('Network and Data Settings')}

@@ -497,7 +519,16 @@ class SettingsPage extends React.PureComponent { } /> - + {isPasswordSaved && ( +

+ {__('Your password is saved in your OS keychain.')}{' '} +