Merge pull request #1846 from warrickct/registration-progress-banner
Registration progress bannerpull/1873/head
commit
d32562673d
@ -1,216 +1,198 @@
|
|||||||
import React from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { SessionButton } from '../session/SessionButton';
|
import { SessionButton } from '../session/SessionButton';
|
||||||
import { ToastUtils, UserUtils } from '../../session/utils';
|
import { ToastUtils, UserUtils } from '../../session/utils';
|
||||||
import { withTheme } from 'styled-components';
|
|
||||||
import { PasswordUtil } from '../../util';
|
import { PasswordUtil } from '../../util';
|
||||||
import { getPasswordHash } from '../../data/data';
|
import { getPasswordHash } from '../../data/data';
|
||||||
import { QRCode } from 'react-qr-svg';
|
import { QRCode } from 'react-qr-svg';
|
||||||
import { mn_decode } from '../../session/crypto/mnemonic';
|
import { mn_decode } from '../../session/crypto/mnemonic';
|
||||||
import { SessionWrapperModal } from '../session/SessionWrapperModal';
|
import { SessionWrapperModal } from '../session/SessionWrapperModal';
|
||||||
import { SpacerLG, SpacerSM, SpacerXS } from '../basic/Text';
|
import { SpacerLG, SpacerSM, SpacerXS } from '../basic/Text';
|
||||||
import autoBind from 'auto-bind';
|
|
||||||
import { recoveryPhraseModal } from '../../state/ducks/modalDialog';
|
import { recoveryPhraseModal } from '../../state/ducks/modalDialog';
|
||||||
|
import { useDispatch } from 'react-redux';
|
||||||
|
|
||||||
interface State {
|
interface PasswordProps {
|
||||||
error: string;
|
setPasswordValid: (val: boolean) => any;
|
||||||
loadingPassword: boolean;
|
|
||||||
loadingSeed: boolean;
|
|
||||||
recoveryPhrase: string;
|
|
||||||
hasPassword: boolean | null;
|
|
||||||
passwordHash: string;
|
passwordHash: string;
|
||||||
passwordValid: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class SessionSeedModalInner extends React.Component<{}, State> {
|
const Password = (props: PasswordProps) => {
|
||||||
constructor(props: any) {
|
const { setPasswordValid, passwordHash } = props;
|
||||||
super(props);
|
const i18n = window.i18n;
|
||||||
|
const [error, setError] = useState('');
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
this.state = {
|
const onClose = () => dispatch(recoveryPhraseModal(null));
|
||||||
error: '',
|
|
||||||
loadingPassword: true,
|
|
||||||
loadingSeed: true,
|
|
||||||
recoveryPhrase: '',
|
|
||||||
hasPassword: null,
|
|
||||||
passwordHash: '',
|
|
||||||
passwordValid: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
autoBind(this);
|
const confirmPassword = () => {
|
||||||
}
|
const passwordValue = jQuery('#seed-input-password').val();
|
||||||
|
const isPasswordValid = PasswordUtil.matchesHash(passwordValue as string, passwordHash);
|
||||||
|
|
||||||
public componentDidMount() {
|
if (!passwordValue) {
|
||||||
setTimeout(() => ($('#seed-input-password') as any).focus(), 100);
|
setError('noGivenPassword');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void this.checkHasPassword();
|
if (passwordHash && !isPasswordValid) {
|
||||||
void this.getRecoveryPhrase();
|
setError('invalidPassword');
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
public render() {
|
|
||||||
const i18n = window.i18n;
|
|
||||||
|
|
||||||
const { hasPassword, passwordValid } = this.state;
|
|
||||||
const loading = this.state.loadingPassword || this.state.loadingSeed;
|
|
||||||
const onClose = () => window.inboxStore?.dispatch(recoveryPhraseModal(null));
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{!loading && (
|
|
||||||
<SessionWrapperModal
|
|
||||||
title={i18n('showRecoveryPhrase')}
|
|
||||||
onClose={onClose}
|
|
||||||
showExitIcon={true}
|
|
||||||
>
|
|
||||||
<SpacerSM />
|
|
||||||
|
|
||||||
{hasPassword && !passwordValid ? (
|
|
||||||
<>{this.renderPasswordView()}</>
|
|
||||||
) : (
|
|
||||||
<>{this.renderSeedView()}</>
|
|
||||||
)}
|
|
||||||
</SessionWrapperModal>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private renderPasswordView() {
|
|
||||||
const error = this.state.error;
|
|
||||||
const i18n = window.i18n;
|
|
||||||
|
|
||||||
const onClose = () => window.inboxStore?.dispatch(recoveryPhraseModal(null));
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<p>{i18n('showRecoveryPhrasePasswordRequest')}</p>
|
|
||||||
<input
|
|
||||||
type="password"
|
|
||||||
id="seed-input-password"
|
|
||||||
placeholder={i18n('password')}
|
|
||||||
onKeyUp={this.onEnter}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{error && (
|
setPasswordValid(true);
|
||||||
<>
|
setError('');
|
||||||
<SpacerXS />
|
|
||||||
<div className="session-label danger">{error}</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<SpacerLG />
|
window.removeEventListener('keyup', onEnter);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
<div className="session-modal__button-group">
|
const onEnter = (event: any) => {
|
||||||
<SessionButton text={i18n('ok')} onClick={this.confirmPassword} />
|
if (event.key === 'Enter') {
|
||||||
|
confirmPassword();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<p>{i18n('showRecoveryPhrasePasswordRequest')}</p>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
id="seed-input-password"
|
||||||
|
placeholder={i18n('password')}
|
||||||
|
onKeyUp={onEnter}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{error && (
|
||||||
|
<>
|
||||||
|
<SpacerXS />
|
||||||
|
<div className="session-label danger">{error}</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
<SessionButton text={i18n('cancel')} onClick={onClose} />
|
<SpacerLG />
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private renderSeedView() {
|
<div className="session-modal__button-group">
|
||||||
const i18n = window.i18n;
|
<SessionButton text={i18n('ok')} onClick={confirmPassword} />
|
||||||
const bgColor = '#FFFFFF';
|
|
||||||
const fgColor = '#1B1B1B';
|
|
||||||
|
|
||||||
const hexEncodedSeed = mn_decode(this.state.recoveryPhrase, 'english');
|
<SessionButton text={i18n('cancel')} onClick={onClose} />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
interface SeedProps {
|
||||||
<>
|
recoveryPhrase: string;
|
||||||
<div className="session-modal__centered text-center">
|
onClickCopy?: () => any;
|
||||||
<p className="session-modal__description">{i18n('recoveryPhraseSavePromptMain')}</p>
|
}
|
||||||
<SpacerXS />
|
|
||||||
|
|
||||||
<i className="session-modal__text-highlight">{this.state.recoveryPhrase}</i>
|
const Seed = (props: SeedProps) => {
|
||||||
</div>
|
const { recoveryPhrase, onClickCopy } = props;
|
||||||
<SpacerLG />
|
const i18n = window.i18n;
|
||||||
<div className="qr-image">
|
const bgColor = '#FFFFFF';
|
||||||
<QRCode value={hexEncodedSeed} bgColor={bgColor} fgColor={fgColor} level="L" />
|
const fgColor = '#1B1B1B';
|
||||||
</div>
|
const dispatch = useDispatch();
|
||||||
<SpacerLG />
|
|
||||||
<div className="session-modal__button-group">
|
|
||||||
<SessionButton
|
|
||||||
text={i18n('copy')}
|
|
||||||
onClick={() => {
|
|
||||||
this.copyRecoveryPhrase(this.state.recoveryPhrase);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private confirmPassword() {
|
|
||||||
const passwordHash = this.state.passwordHash;
|
|
||||||
const passwordValue = jQuery('#seed-input-password').val();
|
|
||||||
const isPasswordValid = PasswordUtil.matchesHash(passwordValue as string, passwordHash);
|
|
||||||
|
|
||||||
if (!passwordValue) {
|
const hexEncodedSeed = mn_decode(recoveryPhrase, 'english');
|
||||||
this.setState({
|
|
||||||
error: window.i18n('noGivenPassword'),
|
|
||||||
});
|
|
||||||
|
|
||||||
return false;
|
const copyRecoveryPhrase = (recoveryPhraseToCopy: string) => {
|
||||||
|
window.clipboard.writeText(recoveryPhraseToCopy);
|
||||||
|
ToastUtils.pushCopiedToClipBoard();
|
||||||
|
if (onClickCopy) {
|
||||||
|
onClickCopy();
|
||||||
}
|
}
|
||||||
|
dispatch(recoveryPhraseModal(null));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className="session-modal__centered text-center">
|
||||||
|
<p className="session-modal__description">{i18n('recoveryPhraseSavePromptMain')}</p>
|
||||||
|
<SpacerXS />
|
||||||
|
|
||||||
|
<i className="session-modal__text-highlight">{recoveryPhrase}</i>
|
||||||
|
</div>
|
||||||
|
<SpacerLG />
|
||||||
|
<div className="qr-image">
|
||||||
|
<QRCode value={hexEncodedSeed} bgColor={bgColor} fgColor={fgColor} level="L" />
|
||||||
|
</div>
|
||||||
|
<SpacerLG />
|
||||||
|
<div className="session-modal__button-group">
|
||||||
|
<SessionButton
|
||||||
|
text={i18n('copy')}
|
||||||
|
onClick={() => {
|
||||||
|
copyRecoveryPhrase(recoveryPhrase);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
if (passwordHash && !isPasswordValid) {
|
interface ModalInnerProps {
|
||||||
this.setState({
|
onClickOk?: () => any;
|
||||||
error: window.i18n('invalidPassword'),
|
}
|
||||||
});
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({
|
const SessionSeedModalInner = (props: ModalInnerProps) => {
|
||||||
passwordValid: true,
|
const { onClickOk } = props;
|
||||||
error: '',
|
const [loadingPassword, setLoadingPassword] = useState(true);
|
||||||
});
|
const [loadingSeed, setLoadingSeed] = useState(true);
|
||||||
|
const [recoveryPhrase, setRecoveryPhrase] = useState('');
|
||||||
|
const [hasPassword, setHasPassword] = useState<null | boolean>(null);
|
||||||
|
const [passwordValid, setPasswordValid] = useState(false);
|
||||||
|
const [passwordHash, setPasswordHash] = useState('');
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setTimeout(() => ($('#seed-input-password') as any).focus(), 100);
|
||||||
|
void checkHasPassword();
|
||||||
|
void getRecoveryPhrase();
|
||||||
|
}, []);
|
||||||
|
|
||||||
window.removeEventListener('keyup', this.onEnter);
|
const i18n = window.i18n;
|
||||||
|
|
||||||
return true;
|
const onClose = () => dispatch(recoveryPhraseModal(null));
|
||||||
}
|
|
||||||
|
|
||||||
private async checkHasPassword() {
|
const checkHasPassword = async () => {
|
||||||
if (!this.state.loadingPassword) {
|
if (!loadingPassword) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const hash = await getPasswordHash();
|
const hash = await getPasswordHash();
|
||||||
this.setState({
|
setHasPassword(!!hash);
|
||||||
hasPassword: !!hash,
|
setPasswordHash(hash || '');
|
||||||
passwordHash: hash || '',
|
setLoadingPassword(false);
|
||||||
loadingPassword: false,
|
};
|
||||||
});
|
|
||||||
}
|
const getRecoveryPhrase = async () => {
|
||||||
|
if (recoveryPhrase) {
|
||||||
private async getRecoveryPhrase() {
|
|
||||||
if (this.state.recoveryPhrase) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
const newRecoveryPhrase = UserUtils.getCurrentRecoveryPhrase();
|
||||||
const recoveryPhrase = UserUtils.getCurrentRecoveryPhrase();
|
setRecoveryPhrase(newRecoveryPhrase);
|
||||||
|
setLoadingSeed(false);
|
||||||
this.setState({
|
|
||||||
recoveryPhrase,
|
|
||||||
loadingSeed: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
};
|
||||||
|
|
||||||
private copyRecoveryPhrase(recoveryPhrase: string) {
|
return (
|
||||||
window.clipboard.writeText(recoveryPhrase);
|
<>
|
||||||
|
{!loadingSeed && (
|
||||||
ToastUtils.pushCopiedToClipBoard();
|
<SessionWrapperModal
|
||||||
window.inboxStore?.dispatch(recoveryPhraseModal(null));
|
title={i18n('showRecoveryPhrase')}
|
||||||
}
|
onClose={onClose}
|
||||||
|
showExitIcon={true}
|
||||||
private onEnter(event: any) {
|
>
|
||||||
if (event.key === 'Enter') {
|
<SpacerSM />
|
||||||
this.confirmPassword();
|
|
||||||
}
|
{hasPassword && !passwordValid ? (
|
||||||
}
|
<Password passwordHash={passwordHash} setPasswordValid={setPasswordValid} />
|
||||||
}
|
) : (
|
||||||
|
<Seed recoveryPhrase={recoveryPhrase} onClickCopy={onClickOk} />
|
||||||
export const SessionSeedModal = withTheme(SessionSeedModalInner);
|
)}
|
||||||
|
</SessionWrapperModal>
|
||||||
|
)}
|
||||||
|
:
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SessionSeedModal = SessionSeedModalInner;
|
||||||
|
Loading…
Reference in New Issue