Add basic wallet encryption flows
This commit is contained in:
parent
1c55774165
commit
6809ac3e3a
13 changed files with 474 additions and 31 deletions
|
@ -1,7 +1,7 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { selectDaemonVersionMatched } from 'redux/selectors/app';
|
import { selectDaemonVersionMatched } from 'redux/selectors/app';
|
||||||
import { selectNotification } from 'lbry-redux';
|
import { selectNotification } from 'lbry-redux';
|
||||||
import { doCheckDaemonVersion } from 'redux/actions/app';
|
import { doCheckDaemonVersion, doNotifyUnlockWallet } from 'redux/actions/app';
|
||||||
import SplashScreen from './view';
|
import SplashScreen from './view';
|
||||||
|
|
||||||
const select = state => ({
|
const select = state => ({
|
||||||
|
@ -11,6 +11,10 @@ const select = state => ({
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
checkDaemonVersion: () => dispatch(doCheckDaemonVersion()),
|
checkDaemonVersion: () => dispatch(doCheckDaemonVersion()),
|
||||||
|
notifyUnlockWallet: () => dispatch(doNotifyUnlockWallet()),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, perform)(SplashScreen);
|
export default connect(
|
||||||
|
select,
|
||||||
|
perform
|
||||||
|
)(SplashScreen);
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Lbry, MODALS } from 'lbry-redux';
|
import { Lbry, MODALS } from 'lbry-redux';
|
||||||
import LoadScreen from './internal/load-screen';
|
import LoadScreen from './internal/load-screen';
|
||||||
|
import ModalWalletUnlock from 'modal/modalWalletUnlock';
|
||||||
import ModalIncompatibleDaemon from 'modal/modalIncompatibleDaemon';
|
import ModalIncompatibleDaemon from 'modal/modalIncompatibleDaemon';
|
||||||
import ModalUpgrade from 'modal/modalUpgrade';
|
import ModalUpgrade from 'modal/modalUpgrade';
|
||||||
import ModalDownloading from 'modal/modalDownloading';
|
import ModalDownloading from 'modal/modalDownloading';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
checkDaemonVersion: () => Promise<any>,
|
checkDaemonVersion: () => Promise<any>,
|
||||||
|
notifyUnlockWallet: () => Promise<any>,
|
||||||
notification: ?{
|
notification: ?{
|
||||||
id: string,
|
id: string,
|
||||||
},
|
},
|
||||||
|
@ -17,6 +19,7 @@ type State = {
|
||||||
message: string,
|
message: string,
|
||||||
isRunning: boolean,
|
isRunning: boolean,
|
||||||
isLagging: boolean,
|
isLagging: boolean,
|
||||||
|
launchedModal: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
export class SplashScreen extends React.PureComponent<Props, State> {
|
export class SplashScreen extends React.PureComponent<Props, State> {
|
||||||
|
@ -28,18 +31,23 @@ export class SplashScreen extends React.PureComponent<Props, State> {
|
||||||
message: __('Connecting'),
|
message: __('Connecting'),
|
||||||
isRunning: false,
|
isRunning: false,
|
||||||
isLagging: false,
|
isLagging: false,
|
||||||
|
launchedModal: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
updateStatus() {
|
updateStatus() {
|
||||||
Lbry.status().then(status => {
|
Lbry.status().then(status => {
|
||||||
this._updateStatusCallback(status);
|
this._updateStatusCallback(status);
|
||||||
|
window.status = status;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_updateStatusCallback(status) {
|
_updateStatusCallback(status) {
|
||||||
|
const { notifyUnlockWallet } = this.props;
|
||||||
|
const { launchedModal } = this.state;
|
||||||
|
|
||||||
const startupStatus = status.startup_status;
|
const startupStatus = status.startup_status;
|
||||||
if (startupStatus.code == 'started') {
|
if (startupStatus.code === 'started') {
|
||||||
// Wait until we are able to resolve a name before declaring
|
// Wait until we are able to resolve a name before declaring
|
||||||
// that we are done.
|
// that we are done.
|
||||||
// TODO: This is a hack, and the logic should live in the daemon
|
// TODO: This is a hack, and the logic should live in the daemon
|
||||||
|
@ -61,6 +69,7 @@ export class SplashScreen extends React.PureComponent<Props, State> {
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status.blockchain_status && status.blockchain_status.blocks_behind > 0) {
|
if (status.blockchain_status && status.blockchain_status.blocks_behind > 0) {
|
||||||
const format =
|
const format =
|
||||||
status.blockchain_status.blocks_behind == 1 ? '%s block behind' : '%s blocks behind';
|
status.blockchain_status.blocks_behind == 1 ? '%s block behind' : '%s blocks behind';
|
||||||
|
@ -69,6 +78,17 @@ export class SplashScreen extends React.PureComponent<Props, State> {
|
||||||
details: __(format, status.blockchain_status.blocks_behind),
|
details: __(format, status.blockchain_status.blocks_behind),
|
||||||
isLagging: startupStatus.is_lagging,
|
isLagging: startupStatus.is_lagging,
|
||||||
});
|
});
|
||||||
|
} else if (startupStatus.code === 'waiting_for_wallet_unlock') {
|
||||||
|
this.setState({
|
||||||
|
message: __('Unlock Wallet'),
|
||||||
|
details: __('Please unlock your wallet to proceed.'),
|
||||||
|
isLagging: false,
|
||||||
|
isRunning: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (launchedModal === false) {
|
||||||
|
this.setState({ launchedModal: true }, () => notifyUnlockWallet());
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
this.setState({
|
this.setState({
|
||||||
message: __('Network Loading'),
|
message: __('Network Loading'),
|
||||||
|
@ -114,6 +134,7 @@ export class SplashScreen extends React.PureComponent<Props, State> {
|
||||||
in the modals won't work. */}
|
in the modals won't work. */}
|
||||||
{isRunning && (
|
{isRunning && (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
|
{notificationId === MODALS.WALLET_UNLOCK && <ModalWalletUnlock />}
|
||||||
{notificationId === MODALS.INCOMPATIBLE_DAEMON && <ModalIncompatibleDaemon />}
|
{notificationId === MODALS.INCOMPATIBLE_DAEMON && <ModalIncompatibleDaemon />}
|
||||||
{notificationId === MODALS.UPGRADE && <ModalUpgrade />}
|
{notificationId === MODALS.UPGRADE && <ModalUpgrade />}
|
||||||
{notificationId === MODALS.DOWNLOADING && <ModalDownloading />}
|
{notificationId === MODALS.DOWNLOADING && <ModalDownloading />}
|
||||||
|
|
|
@ -23,6 +23,9 @@ import ModalSendTip from '../modalSendTip';
|
||||||
import ModalPublish from '../modalPublish';
|
import ModalPublish from '../modalPublish';
|
||||||
import ModalOpenExternalLink from '../modalOpenExternalLink';
|
import ModalOpenExternalLink from '../modalOpenExternalLink';
|
||||||
import ModalConfirmThumbnailUpload from 'modal/modalConfirmThumbnailUpload';
|
import ModalConfirmThumbnailUpload from 'modal/modalConfirmThumbnailUpload';
|
||||||
|
import ModalWalletEncrypt from 'modal/modalWalletEncrypt';
|
||||||
|
import ModalWalletDecrypt from 'modal/modalWalletDecrypt';
|
||||||
|
import ModalWalletUnlock from 'modal/modalWalletUnlock';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
modal: string,
|
modal: string,
|
||||||
|
@ -165,6 +168,12 @@ class ModalRouter extends React.PureComponent<Props> {
|
||||||
return <ModalConfirmTransaction {...notificationProps} />;
|
return <ModalConfirmTransaction {...notificationProps} />;
|
||||||
case MODALS.CONFIRM_THUMBNAIL_UPLOAD:
|
case MODALS.CONFIRM_THUMBNAIL_UPLOAD:
|
||||||
return <ModalConfirmThumbnailUpload {...notificationProps} />;
|
return <ModalConfirmThumbnailUpload {...notificationProps} />;
|
||||||
|
case MODALS.WALLET_ENCRYPT:
|
||||||
|
return <ModalWalletEncrypt {...notificationProps} />;
|
||||||
|
case MODALS.WALLET_DECRYPT:
|
||||||
|
return <ModalWalletDecrypt {...notificationProps} />;
|
||||||
|
case MODALS.WALLET_UNLOCK:
|
||||||
|
return <ModalWalletUnlock {...notificationProps} />;
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
23
src/renderer/modal/modalWalletDecrypt/index.js
Normal file
23
src/renderer/modal/modalWalletDecrypt/index.js
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import {
|
||||||
|
doHideNotification,
|
||||||
|
doWalletStatus,
|
||||||
|
doWalletDecrypt,
|
||||||
|
selectWalletDecryptSucceeded,
|
||||||
|
} from 'lbry-redux';
|
||||||
|
import ModalWalletDecrypt from './view';
|
||||||
|
|
||||||
|
const select = state => ({
|
||||||
|
walletDecryptSucceded: selectWalletDecryptSucceeded(state),
|
||||||
|
});
|
||||||
|
|
||||||
|
const perform = dispatch => ({
|
||||||
|
closeModal: () => dispatch(doHideNotification()),
|
||||||
|
decryptWallet: password => dispatch(doWalletDecrypt(password)),
|
||||||
|
updateWalletStatus: () => dispatch(doWalletStatus()),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
select,
|
||||||
|
perform
|
||||||
|
)(ModalWalletDecrypt);
|
61
src/renderer/modal/modalWalletDecrypt/view.jsx
Normal file
61
src/renderer/modal/modalWalletDecrypt/view.jsx
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
// @flow
|
||||||
|
import React from 'react';
|
||||||
|
import { FormRow, FormField } from 'component/common/form';
|
||||||
|
import { Modal } from 'modal/modal';
|
||||||
|
import Button from 'component/button';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
closeModal: () => void,
|
||||||
|
unlockWallet: string => void,
|
||||||
|
walletDecryptSucceded: boolean,
|
||||||
|
updateWalletStatus: boolean,
|
||||||
|
};
|
||||||
|
|
||||||
|
class ModalWalletDecrypt extends React.PureComponent<Props> {
|
||||||
|
state = {
|
||||||
|
submitted: false, // Prior actions could be marked complete
|
||||||
|
};
|
||||||
|
|
||||||
|
submitDecryptForm() {
|
||||||
|
this.setState({ submitted: true });
|
||||||
|
this.props.decryptWallet();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate() {
|
||||||
|
const { props, state } = this;
|
||||||
|
|
||||||
|
if (state.submitted && props.walletDecryptSucceded === true) {
|
||||||
|
props.closeModal();
|
||||||
|
props.updateWalletStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { closeModal, walletDecryptSucceded } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
isOpen
|
||||||
|
contentLabel={__('Decrypt Wallet')}
|
||||||
|
type="confirm"
|
||||||
|
confirmButtonLabel={__('Decrypt Wallet')}
|
||||||
|
abortButtonLabel={__('Cancel')}
|
||||||
|
onConfirmed={() => this.submitDecryptForm()}
|
||||||
|
onAborted={closeModal}
|
||||||
|
>
|
||||||
|
{__(
|
||||||
|
'Your wallet has been encrypted with a local password, performing this action will remove this password.'
|
||||||
|
)}
|
||||||
|
<FormRow padded>
|
||||||
|
<Button
|
||||||
|
button="link"
|
||||||
|
label={__('Learn more')}
|
||||||
|
href="https://lbry.io/faq/wallet-encryption"
|
||||||
|
/>
|
||||||
|
</FormRow>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ModalWalletDecrypt;
|
26
src/renderer/modal/modalWalletEncrypt/index.js
Normal file
26
src/renderer/modal/modalWalletEncrypt/index.js
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import {
|
||||||
|
doHideNotification,
|
||||||
|
doWalletStatus,
|
||||||
|
doWalletEncrypt,
|
||||||
|
selectWalletEncryptPending,
|
||||||
|
selectWalletEncryptSucceeded,
|
||||||
|
selectWalletEncryptResult,
|
||||||
|
} from 'lbry-redux';
|
||||||
|
import ModalWalletEncrypt from './view';
|
||||||
|
|
||||||
|
const select = state => ({
|
||||||
|
walletEncryptSucceded: selectWalletEncryptSucceeded(state),
|
||||||
|
walletEncryptResult: selectWalletEncryptResult(state),
|
||||||
|
});
|
||||||
|
|
||||||
|
const perform = dispatch => ({
|
||||||
|
closeModal: () => dispatch(doHideNotification()),
|
||||||
|
encryptWallet: password => dispatch(doWalletEncrypt(password)),
|
||||||
|
updateWalletStatus: () => dispatch(doWalletStatus()),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
select,
|
||||||
|
perform
|
||||||
|
)(ModalWalletEncrypt);
|
142
src/renderer/modal/modalWalletEncrypt/view.jsx
Normal file
142
src/renderer/modal/modalWalletEncrypt/view.jsx
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
// @flow
|
||||||
|
import React from 'react';
|
||||||
|
import { FormRow, FormField } from 'component/common/form';
|
||||||
|
import { Modal } from 'modal/modal';
|
||||||
|
import Button from 'component/button';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
closeModal: () => void,
|
||||||
|
unlockWallet: string => void,
|
||||||
|
walletEncryptSucceded: boolean,
|
||||||
|
walletEncryptResult: boolean,
|
||||||
|
updateWalletStatus: boolean,
|
||||||
|
};
|
||||||
|
|
||||||
|
class ModalWalletEncrypt extends React.PureComponent<Props> {
|
||||||
|
state = {
|
||||||
|
newPassword: null,
|
||||||
|
newPasswordConfirm: null,
|
||||||
|
passwordMismatch: false,
|
||||||
|
understandConfirmed: false,
|
||||||
|
understandError: false,
|
||||||
|
submitted: false, // Prior actions could be marked complete
|
||||||
|
failMessage: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
onChangeNewPassword(event) {
|
||||||
|
this.setState({ newPassword: event.target.value });
|
||||||
|
}
|
||||||
|
|
||||||
|
onChangeNewPasswordConfirm(event) {
|
||||||
|
this.setState({ newPasswordConfirm: event.target.value });
|
||||||
|
}
|
||||||
|
|
||||||
|
onChangeUnderstandConfirm(event) {
|
||||||
|
this.setState({
|
||||||
|
understandConfirmed: /^.?i understand.?$/i.test(event.target.value),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
submitEncryptForm() {
|
||||||
|
const { state } = this;
|
||||||
|
|
||||||
|
let invalidEntries = false;
|
||||||
|
|
||||||
|
if (state.newPassword !== state.newPasswordConfirm) {
|
||||||
|
this.setState({ passwordMismatch: true });
|
||||||
|
invalidEntries = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.understandConfirmed === false) {
|
||||||
|
this.setState({ understandError: true });
|
||||||
|
invalidEntries = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (invalidEntries === true) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({ submitted: true });
|
||||||
|
this.props.encryptWallet(state.newPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate() {
|
||||||
|
const { props, state } = this;
|
||||||
|
|
||||||
|
if (state.submitted) {
|
||||||
|
if (props.walletEncryptSucceded === true) {
|
||||||
|
props.closeModal();
|
||||||
|
props.updateWalletStatus();
|
||||||
|
} else if (props.walletEncryptSucceded === false) {
|
||||||
|
// See https://github.com/lbryio/lbry/issues/1307
|
||||||
|
this.setState({ failMessage: 'Unable to encrypt wallet.' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { closeModal } = this.props;
|
||||||
|
|
||||||
|
const { passwordMismatch, understandError, failMessage } = this.state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
isOpen
|
||||||
|
contentLabel={__('Encrypt Wallet')}
|
||||||
|
type="confirm"
|
||||||
|
confirmButtonLabel={__('Encrypt Wallet')}
|
||||||
|
abortButtonLabel={__('Cancel')}
|
||||||
|
onConfirmed={() => this.submitEncryptForm()}
|
||||||
|
onAborted={closeModal}
|
||||||
|
>
|
||||||
|
{__(
|
||||||
|
'Encrypting your wallet will require a password to access your local wallet data when LBRY starts. Please enter a new password for your wallet.'
|
||||||
|
)}
|
||||||
|
<FormRow padded>
|
||||||
|
<FormField
|
||||||
|
stretch
|
||||||
|
error={passwordMismatch === true ? 'Passwords do not match' : false}
|
||||||
|
label={__('New Password')}
|
||||||
|
type="password"
|
||||||
|
name="wallet-new-password"
|
||||||
|
onChange={event => this.onChangeNewPassword(event)}
|
||||||
|
/>
|
||||||
|
</FormRow>
|
||||||
|
<FormRow padded>
|
||||||
|
<FormField
|
||||||
|
stretch
|
||||||
|
error={passwordMismatch === true ? 'Passwords do not match' : false}
|
||||||
|
label={__('Confirm Password')}
|
||||||
|
type="password"
|
||||||
|
name="wallet-new-password-confirm"
|
||||||
|
onChange={event => this.onChangeNewPasswordConfirm(event)}
|
||||||
|
/>
|
||||||
|
</FormRow>
|
||||||
|
<br />
|
||||||
|
{__(
|
||||||
|
'If your password is lost, it cannot be recovered. You will not be able to access your wallet without a password.'
|
||||||
|
)}
|
||||||
|
<FormRow padded>
|
||||||
|
<FormField
|
||||||
|
stretch
|
||||||
|
error={understandError === true ? 'You must enter "I understand"' : false}
|
||||||
|
label={__('Enter "I understand"')}
|
||||||
|
type="text"
|
||||||
|
name="wallet-understand"
|
||||||
|
onChange={event => this.onChangeUnderstandConfirm(event)}
|
||||||
|
/>
|
||||||
|
</FormRow>
|
||||||
|
<FormRow padded>
|
||||||
|
<Button
|
||||||
|
button="link"
|
||||||
|
label={__('Learn more')}
|
||||||
|
href="https://lbry.io/faq/wallet-encryption"
|
||||||
|
/>
|
||||||
|
</FormRow>
|
||||||
|
{failMessage && <div className="error-text">{__(failMessage)}</div>}
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ModalWalletEncrypt;
|
24
src/renderer/modal/modalWalletUnlock/index.js
Normal file
24
src/renderer/modal/modalWalletUnlock/index.js
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import {
|
||||||
|
doHideNotification,
|
||||||
|
doWalletUnlock,
|
||||||
|
selectWalletUnlockPending,
|
||||||
|
selectWalletUnlockSucceeded,
|
||||||
|
} from 'lbry-redux';
|
||||||
|
import { doQuit } from 'redux/actions/app';
|
||||||
|
import ModalWalletUnlock from './view';
|
||||||
|
|
||||||
|
const select = state => ({
|
||||||
|
walletUnlockSucceded: selectWalletUnlockSucceeded(state),
|
||||||
|
});
|
||||||
|
|
||||||
|
const perform = dispatch => ({
|
||||||
|
closeModal: () => dispatch(doHideNotification()),
|
||||||
|
quit: () => dispatch(doQuit()),
|
||||||
|
unlockWallet: password => dispatch(doWalletUnlock(password)),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
select,
|
||||||
|
perform
|
||||||
|
)(ModalWalletUnlock);
|
71
src/renderer/modal/modalWalletUnlock/view.jsx
Normal file
71
src/renderer/modal/modalWalletUnlock/view.jsx
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
// @flow
|
||||||
|
import React from 'react';
|
||||||
|
import { FormRow, FormField } from 'component/common/form';
|
||||||
|
import { Modal } from 'modal/modal';
|
||||||
|
import Button from 'component/button';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
closeModal: () => void,
|
||||||
|
quit: () => void,
|
||||||
|
unlockWallet: string => void,
|
||||||
|
walletUnlockSucceded: boolean,
|
||||||
|
};
|
||||||
|
|
||||||
|
class ModalWalletUnlock extends React.PureComponent<Props> {
|
||||||
|
state = {
|
||||||
|
password: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
componentDidUpdate() {
|
||||||
|
const { props } = this;
|
||||||
|
|
||||||
|
if (props.walletUnlockSucceded === true) {
|
||||||
|
props.closeModal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onChangePassword(event) {
|
||||||
|
this.setState({ password: event.target.value });
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { quit, unlockWallet, walletUnlockSucceded, closeModal } = this.props;
|
||||||
|
|
||||||
|
const { password } = this.state;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
isOpen
|
||||||
|
contentLabel={__('Unlock Wallet')}
|
||||||
|
type="confirm"
|
||||||
|
confirmButtonLabel={__('Unlock')}
|
||||||
|
abortButtonLabel={__('Exit')}
|
||||||
|
onConfirmed={() => unlockWallet(password)}
|
||||||
|
onAborted={quit}
|
||||||
|
>
|
||||||
|
{__(
|
||||||
|
'Your wallet has been encrypted with a local password. Please enter your wallet password to proceed.'
|
||||||
|
)}
|
||||||
|
<FormRow padded>
|
||||||
|
<FormField
|
||||||
|
stretch
|
||||||
|
error={walletUnlockSucceded === false ? 'Incorrect Password' : false}
|
||||||
|
label={__('Wallet Password')}
|
||||||
|
type="password"
|
||||||
|
name="wallet-password"
|
||||||
|
onChange={event => this.onChangePassword(event)}
|
||||||
|
/>
|
||||||
|
</FormRow>
|
||||||
|
<FormRow padded>
|
||||||
|
<Button
|
||||||
|
button="link"
|
||||||
|
label={__('Learn more')}
|
||||||
|
href="https://lbry.io/faq/wallet-encryption"
|
||||||
|
/>
|
||||||
|
</FormRow>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ModalWalletUnlock;
|
|
@ -1,6 +1,6 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import * as settings from 'constants/settings';
|
import * as settings from 'constants/settings';
|
||||||
import { doClearCache } from 'redux/actions/app';
|
import { doClearCache, doNotifyEncryptWallet, doNotifyDecryptWallet } from 'redux/actions/app';
|
||||||
import {
|
import {
|
||||||
doSetDaemonSetting,
|
doSetDaemonSetting,
|
||||||
doSetClientSetting,
|
doSetClientSetting,
|
||||||
|
@ -13,6 +13,7 @@ import {
|
||||||
selectLanguages,
|
selectLanguages,
|
||||||
} from 'redux/selectors/settings';
|
} from 'redux/selectors/settings';
|
||||||
import { selectCurrentLanguage } from 'redux/selectors/app';
|
import { selectCurrentLanguage } from 'redux/selectors/app';
|
||||||
|
import { doWalletStatus, selectWalletIsEncrypted } from 'lbry-redux';
|
||||||
import SettingsPage from './view';
|
import SettingsPage from './view';
|
||||||
|
|
||||||
const select = state => ({
|
const select = state => ({
|
||||||
|
@ -26,6 +27,7 @@ const select = state => ({
|
||||||
languages: selectLanguages(state),
|
languages: selectLanguages(state),
|
||||||
automaticDarkModeEnabled: makeSelectClientSetting(settings.AUTOMATIC_DARK_MODE_ENABLED)(state),
|
automaticDarkModeEnabled: makeSelectClientSetting(settings.AUTOMATIC_DARK_MODE_ENABLED)(state),
|
||||||
autoplay: makeSelectClientSetting(settings.AUTOPLAY)(state),
|
autoplay: makeSelectClientSetting(settings.AUTOPLAY)(state),
|
||||||
|
walletEncrypted: selectWalletIsEncrypted(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
|
@ -34,6 +36,9 @@ const perform = dispatch => ({
|
||||||
setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)),
|
setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)),
|
||||||
getThemes: () => dispatch(doGetThemes()),
|
getThemes: () => dispatch(doGetThemes()),
|
||||||
changeLanguage: newLanguage => dispatch(doChangeLanguage(newLanguage)),
|
changeLanguage: newLanguage => dispatch(doChangeLanguage(newLanguage)),
|
||||||
|
encryptWallet: () => dispatch(doNotifyEncryptWallet()),
|
||||||
|
decryptWallet: () => dispatch(doNotifyDecryptWallet()),
|
||||||
|
updateWalletStatus: () => dispatch(doWalletStatus()),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
|
|
|
@ -31,6 +31,9 @@ type Props = {
|
||||||
themes: Array<string>,
|
themes: Array<string>,
|
||||||
automaticDarkModeEnabled: boolean,
|
automaticDarkModeEnabled: boolean,
|
||||||
autoplay: boolean,
|
autoplay: boolean,
|
||||||
|
encryptWallet: () => void,
|
||||||
|
decryptWallet: () => void,
|
||||||
|
walletEncrypted: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
|
@ -58,7 +61,10 @@ class SettingsPage extends React.PureComponent<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.props.getThemes();
|
const { props } = this;
|
||||||
|
|
||||||
|
props.getThemes();
|
||||||
|
props.updateWalletStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
onRunOnStartChange(event: SyntheticInputEvent<*>) {
|
onRunOnStartChange(event: SyntheticInputEvent<*>) {
|
||||||
|
@ -111,6 +117,11 @@ class SettingsPage extends React.PureComponent<Props, State> {
|
||||||
this.props.setClientSetting(settings.SHOW_NSFW, event.target.checked);
|
this.props.setClientSetting(settings.SHOW_NSFW, event.target.checked);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onChangeEncryptWallet() {
|
||||||
|
const { props } = this;
|
||||||
|
props.walletEncrypted ? props.decryptWallet() : props.encryptWallet();
|
||||||
|
}
|
||||||
|
|
||||||
setDaemonSetting(name: string, value: boolean | string | Price) {
|
setDaemonSetting(name: string, value: boolean | string | Price) {
|
||||||
this.props.setDaemonSetting(name, value);
|
this.props.setDaemonSetting(name, value);
|
||||||
}
|
}
|
||||||
|
@ -138,6 +149,7 @@ class SettingsPage extends React.PureComponent<Props, State> {
|
||||||
themes,
|
themes,
|
||||||
automaticDarkModeEnabled,
|
automaticDarkModeEnabled,
|
||||||
autoplay,
|
autoplay,
|
||||||
|
walletEncrypted,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const noDaemonSettings = !daemonSettings || Object.keys(daemonSettings).length === 0;
|
const noDaemonSettings = !daemonSettings || Object.keys(daemonSettings).length === 0;
|
||||||
|
@ -272,7 +284,6 @@ class SettingsPage extends React.PureComponent<Props, State> {
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
{
|
|
||||||
<section className="card card--section">
|
<section className="card card--section">
|
||||||
<div className="card__title">{__('Theme')}</div>
|
<div className="card__title">{__('Theme')}</div>
|
||||||
<FormField
|
<FormField
|
||||||
|
@ -297,7 +308,19 @@ class SettingsPage extends React.PureComponent<Props, State> {
|
||||||
postfix={__('Automatic dark mode (9pm to 8am)')}
|
postfix={__('Automatic dark mode (9pm to 8am)')}
|
||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
}
|
<section className="card card--section">
|
||||||
|
<div className="card__title">{__('Wallet Security')}</div>
|
||||||
|
<FormField
|
||||||
|
type="checkbox"
|
||||||
|
name="encrypt_wallet"
|
||||||
|
onChange={e => this.onChangeEncryptWallet(e)}
|
||||||
|
checked={walletEncrypted}
|
||||||
|
postfix={__('Encrypt my wallet with a custom password.')}
|
||||||
|
helper={__(
|
||||||
|
'Secure your local wallet data with a custom password. Lost passwords cannot be recovered.'
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
<section className="card card--section">
|
<section className="card card--section">
|
||||||
<div className="card__title">{__('Application Cache')}</div>
|
<div className="card__title">{__('Application Cache')}</div>
|
||||||
<span className="card__subtitle">
|
<span className="card__subtitle">
|
||||||
|
|
|
@ -274,6 +274,36 @@ export function doCheckDaemonVersion() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function doNotifyEncryptWallet() {
|
||||||
|
return dispatch => {
|
||||||
|
dispatch(
|
||||||
|
doNotify({
|
||||||
|
id: MODALS.WALLET_ENCRYPT,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doNotifyDecryptWallet() {
|
||||||
|
return dispatch => {
|
||||||
|
dispatch(
|
||||||
|
doNotify({
|
||||||
|
id: MODALS.WALLET_DECRYPT,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doNotifyUnlockWallet() {
|
||||||
|
return dispatch => {
|
||||||
|
dispatch(
|
||||||
|
doNotify({
|
||||||
|
id: MODALS.WALLET_UNLOCK,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function doAlertError(errorList) {
|
export function doAlertError(errorList) {
|
||||||
return dispatch => {
|
return dispatch => {
|
||||||
dispatch(
|
dispatch(
|
||||||
|
|
|
@ -361,3 +361,7 @@ p {
|
||||||
margin-top: $spacing-vertical * 2/3;
|
margin-top: $spacing-vertical * 2/3;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.error-text {
|
||||||
|
color: var(--color-error);
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue