mirror of
https://github.com/LBRYFoundation/lbry-desktop.git
synced 2025-08-27 23:41:29 +00:00
404 lines
14 KiB
JavaScript
404 lines
14 KiB
JavaScript
// @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';
|
|
|
|
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,
|
|
advancedMode: boolean,
|
|
showPasswordFields: boolean,
|
|
};
|
|
|
|
function WalletSecurityAndSync(props: Props) {
|
|
const {
|
|
// walletEncryptSucceeded,
|
|
// walletEncryptPending,
|
|
// walletDecryptSucceeded,
|
|
// walletDecryptPending,
|
|
// updateWalletStatus,
|
|
walletEncrypted,
|
|
encryptWallet,
|
|
decryptWallet,
|
|
syncEnabled,
|
|
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,
|
|
advancedMode: false,
|
|
showPasswordFields: false,
|
|
};
|
|
const [componentState, setComponentState] = useState<State>(defaultComponentState);
|
|
|
|
const safeToSync = !hasTransactions || !hashChanged;
|
|
|
|
// on mount
|
|
useEffect(() => {
|
|
checkSync();
|
|
getSavedPassword().then(p => {
|
|
if (p) {
|
|
setComponentState({
|
|
...componentState,
|
|
newPassword: p,
|
|
newPasswordConfirm: p,
|
|
showPasswordFields: true,
|
|
rememberPassword: true,
|
|
});
|
|
}
|
|
});
|
|
}, []);
|
|
|
|
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<>) {
|
|
if (componentState.rememberPassword) {
|
|
deleteSavedPassword();
|
|
}
|
|
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<>) {
|
|
if (componentState.enableSync) {
|
|
setComponentState({ ...componentState, enableSync: false, newPassword: '', newPasswordConfirm: '' });
|
|
setComponentState({ ...componentState, enableSync: false, newPassword: '', newPasswordConfirm: '' });
|
|
}
|
|
if (!(walletEncrypted || syncApplyErrorMessage || componentState.advancedMode)) {
|
|
easyApply();
|
|
} else {
|
|
setComponentState({ ...componentState, enableSync: true });
|
|
}
|
|
}
|
|
|
|
function onChangeEncrypt(event: SyntheticInputEvent<>) {
|
|
setComponentState({ ...componentState, encryptWallet: event.target.checked });
|
|
}
|
|
|
|
async function easyApply() {
|
|
return new Promise((resolve, reject) => {
|
|
return syncApply(syncHash, syncData, componentState.newPassword);
|
|
})
|
|
.then(() => {
|
|
setComponentState({ ...componentState, enableSync: event.target.checked });
|
|
})
|
|
.catch();
|
|
}
|
|
|
|
async function apply() {
|
|
setComponentState({ ...componentState, failed: false });
|
|
|
|
await checkSync();
|
|
|
|
if (componentState.enableSync) {
|
|
await syncApply(syncHash, syncData, componentState.newPassword);
|
|
if (syncApplyErrorMessage) {
|
|
setComponentState({ ...componentState, failed: true });
|
|
}
|
|
}
|
|
|
|
if (walletEncrypted) {
|
|
await decryptWallet();
|
|
}
|
|
|
|
if (componentState.encryptWallet && !componentState.failed) {
|
|
await encryptWallet(componentState.newPassword)
|
|
.then(() => {})
|
|
.catch(() => {
|
|
setComponentState({ ...componentState, failed: false });
|
|
});
|
|
}
|
|
|
|
if (componentState.rememberPassword && !componentState.failed) {
|
|
setSavedPassword(componentState.newPassword);
|
|
}
|
|
}
|
|
|
|
return (
|
|
<React.Fragment>
|
|
<section className="card card--section">
|
|
<h2 className="card__title">{__('Wallet Sync and Security')}</h2>
|
|
{!isEmailVerified && (
|
|
<React.Fragment>
|
|
<p className="card__subtitle">
|
|
{__(`It looks like we don't have your email.`)}{' '}
|
|
<Button
|
|
button="link"
|
|
label={__('Verify your email')}
|
|
onClick={() => setComponentState({ ...componentState, showEmailReg: !componentState.showEmailReg })}
|
|
/>{' '}
|
|
{__(`and then come back here.`)}
|
|
</p>
|
|
{componentState.showEmailReg && <UserEmail />}
|
|
</React.Fragment>
|
|
)}
|
|
<p>
|
|
{__(
|
|
'Your LBRY password can help you encrypt your wallet or sync it to another device. You must use the same LBRY password on every device if you wish to sync.'
|
|
)}{' '}
|
|
<Button button="link" label={__('Learn more')} href="https://lbry.com/faq/wallet-encryption" />.
|
|
</p>
|
|
{/* Errors and status */}
|
|
{!componentState.advancedMode && (
|
|
<React.Fragment>
|
|
<p className="card__subtitle">
|
|
{__(`Easy Mode: Sync and go with default security! Don't trust your roommate?`)}{' '}
|
|
<Button
|
|
button="link"
|
|
label={__('Advanced Mode')}
|
|
onClick={() => setComponentState({ ...componentState, advancedMode: !componentState.advancedMode })}
|
|
/>
|
|
.
|
|
</p>
|
|
</React.Fragment>
|
|
)}
|
|
{componentState.advancedMode && (
|
|
<React.Fragment>
|
|
<p className="card__subtitle">
|
|
{__('Advanced Mode: Enter a password that matches your other devices LBRY password.')}{' '}
|
|
<Button
|
|
button="link"
|
|
label={__('Easy Mode')}
|
|
onClick={() => setComponentState({ ...componentState, advancedMode: !componentState.advancedMode })}
|
|
/>
|
|
.
|
|
</p>
|
|
</React.Fragment>
|
|
)}
|
|
{syncApplyErrorMessage && <div className="card__subtitle--status">{__(syncApplyErrorMessage)}</div>}
|
|
|
|
<Form onSubmit={() => apply()}>
|
|
{componentState.advancedMode && (
|
|
<FormField
|
|
type="checkbox"
|
|
name="sync_enabled"
|
|
checked={componentState.enableSync}
|
|
disabled={!isEmailVerified || !safeToSync}
|
|
prefix={<span className="badge badge--alert">ALPHA</span>}
|
|
onChange={event => onChangeSync(event)}
|
|
label={
|
|
<React.Fragment>
|
|
{__('Enable Sync')} <Button button="link" label={__('(?)')} href="https://lbry.com/privacypolicy" />{' '}
|
|
<span className="badge badge--alert">ALPHA</span>
|
|
</React.Fragment>
|
|
}
|
|
/>
|
|
)}
|
|
{!componentState.advancedMode && (
|
|
<Button
|
|
button="primary"
|
|
label={__('Sync my wallet')}
|
|
onClick={() => syncApply(syncHash, syncData, componentState.newPassword)}
|
|
/>
|
|
)}
|
|
{(walletEncrypted ||
|
|
syncApplyErrorMessage ||
|
|
componentState.advancedMode ||
|
|
componentState.showPasswordFields) && (
|
|
<React.Fragment>
|
|
<FormField
|
|
autoFocus
|
|
inputButton={
|
|
<Button
|
|
icon={componentState.obscurePassword ? ICONS.EYE : ICONS.EYE_OFF}
|
|
button={'primary'}
|
|
onClick={() =>
|
|
setComponentState({ ...componentState, obscurePassword: !componentState.obscurePassword })
|
|
}
|
|
/>
|
|
}
|
|
label={__('Password')}
|
|
placeholder={__('Shh...')}
|
|
type={componentState.obscurePassword ? 'password' : 'text'}
|
|
name="wallet-new-password"
|
|
onChange={event => onChangeNewPassword(event)}
|
|
value={componentState.newPassword}
|
|
/>
|
|
<FormField
|
|
error={componentState.passwordMatch === false ? 'No match' : false}
|
|
label={__('Same Password')}
|
|
placeholder={__('Your eyes only')}
|
|
type="password"
|
|
name="wallet-new-password-confirm"
|
|
onChange={event => onChangeNewPasswordConfirm(event)}
|
|
value={componentState.newPasswordConfirm}
|
|
/>
|
|
<FormField
|
|
label={__('Remember Password')}
|
|
type="checkbox"
|
|
name="wallet-remember-password"
|
|
onChange={event => onChangeRememberPassword(event)}
|
|
checked={componentState.rememberPassword}
|
|
/>
|
|
</React.Fragment>
|
|
)}
|
|
|
|
{/* Confirmation */}
|
|
|
|
{(walletEncrypted || componentState.advancedMode) && (
|
|
<React.Fragment>
|
|
<FormField
|
|
type="checkbox"
|
|
name="encrypt_enabled"
|
|
checked={componentState.encryptWallet}
|
|
disabled={false}
|
|
onChange={event => onChangeEncrypt(event)}
|
|
label={__('Encrypt Wallet')}
|
|
/>
|
|
<div className="card__subtitle--status">
|
|
{__(
|
|
'If your password is lost, it cannot be recovered. You will not be able to access your wallet without a password.'
|
|
)}
|
|
</div>
|
|
<FormField
|
|
error={componentState.understandError === true ? 'You must enter "I understand"' : false}
|
|
label={__('Enter "I understand"')}
|
|
placeholder={__('I understand')}
|
|
type="text"
|
|
name="wallet-understand"
|
|
onChange={event => onChangeUnderstandConfirm(event)}
|
|
/>
|
|
</React.Fragment>
|
|
)}
|
|
|
|
{componentState.failMessage && <div className="error-text">{__(componentState.failMessage)}</div>}
|
|
{(walletEncrypted || componentState.advancedMode || syncApplyErrorMessage) && (
|
|
<Submit
|
|
disabled={!componentState.passwordMatch || (!componentState.enableSync && !componentState.encryptWallet)}
|
|
label={componentState.failMessage ? __('Encrypting Wallet') : __('Apply')}
|
|
/>
|
|
)}
|
|
</Form>
|
|
</section>
|
|
|
|
{/* Testing stuff and Diagnostics */}
|
|
|
|
<section className="card card--section">
|
|
<Button
|
|
button="primary"
|
|
label={__('Sync Apply')}
|
|
onClick={() => syncApply(syncHash, syncData, componentState.newPassword)}
|
|
/>{' '}
|
|
<Button button="primary" label={__('Check Sync')} onClick={() => checkSync()} />{' '}
|
|
<Button button="primary" label={__('Setpass test')} onClick={() => setSavedPassword('testpass')} />{' '}
|
|
<Button
|
|
button="primary"
|
|
label={__('Getpass test')}
|
|
onClick={() => getSavedPassword().then(p => setComponentState({ ...componentState, newPassword: p }))}
|
|
/>{' '}
|
|
<Button button="primary" label={__('Deletepass test')} onClick={() => deleteSavedPassword()} />{' '}
|
|
<p>
|
|
password:{' '}
|
|
{componentState.newPassword
|
|
? componentState.newPassword
|
|
: componentState.newPassword === ''
|
|
? 'blankString'
|
|
: 'null'}
|
|
</p>
|
|
<p>encryptWallet: {String(componentState.encryptWallet)}</p>
|
|
<p>enableSync: {String(componentState.enableSync)}</p>
|
|
<p>syncApplyError: {String(syncApplyErrorMessage)}</p>
|
|
<p>Has Synced: {String(hasSyncedWallet)}</p>
|
|
<p>getSyncPending: {String(getSyncIsPending)}</p>
|
|
<p>syncEnabled: {String(syncEnabled)}</p>
|
|
<p>syncHash: {syncHash ? syncHash.slice(0, 10) : 'null'}</p>
|
|
<p>syncData: {syncData ? syncData.slice(0, 10) : 'null'}</p>
|
|
<p>walletEncrypted: {String(walletEncrypted)}</p>
|
|
<p>emailRegistered: {String(isEmailVerified)}</p>
|
|
<p>hashChanged: {String(hashChanged)}</p>
|
|
</section>
|
|
</React.Fragment>
|
|
);
|
|
}
|
|
|
|
export default WalletSecurityAndSync;
|