feat: auto email verification
This commit is contained in:
parent
ad90c1f96e
commit
eb7de08ed1
11 changed files with 81 additions and 91 deletions
|
@ -52,7 +52,7 @@
|
||||||
"hast-util-sanitize": "^1.1.2",
|
"hast-util-sanitize": "^1.1.2",
|
||||||
"keytar": "^4.2.1",
|
"keytar": "^4.2.1",
|
||||||
"lbry-redux": "lbryio/lbry-redux#820b6eeadf9e58c1e5027fd4d92808ba817b410e",
|
"lbry-redux": "lbryio/lbry-redux#820b6eeadf9e58c1e5027fd4d92808ba817b410e",
|
||||||
"lbryinc": "lbryio/lbryinc#68c8ce6b7608dabc9180fff9b4841d10e1c62284",
|
"lbryinc": "lbryio/lbryinc#cee70d482cf352f2e66215fa109f4178406228ca",
|
||||||
"localforage": "^1.7.1",
|
"localforage": "^1.7.1",
|
||||||
"mammoth": "^1.4.6",
|
"mammoth": "^1.4.6",
|
||||||
"mime": "^2.3.1",
|
"mime": "^2.3.1",
|
||||||
|
|
|
@ -1,24 +1,22 @@
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import {
|
import {
|
||||||
selectEmailVerifyIsPending,
|
|
||||||
selectEmailToVerify,
|
selectEmailToVerify,
|
||||||
selectEmailVerifyErrorMessage,
|
|
||||||
doUserEmailVerify,
|
|
||||||
doUserEmailVerifyFailure,
|
|
||||||
doUserResendVerificationEmail,
|
doUserResendVerificationEmail,
|
||||||
|
doUserFetch,
|
||||||
|
selectUser,
|
||||||
} from 'lbryinc';
|
} from 'lbryinc';
|
||||||
|
import { doNavigate } from 'redux/actions/navigation';
|
||||||
import UserEmailVerify from './view';
|
import UserEmailVerify from './view';
|
||||||
|
|
||||||
const select = state => ({
|
const select = state => ({
|
||||||
isPending: selectEmailVerifyIsPending(state),
|
|
||||||
email: selectEmailToVerify(state),
|
email: selectEmailToVerify(state),
|
||||||
errorMessage: selectEmailVerifyErrorMessage(state),
|
user: selectUser(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
verifyUserEmail: (code, recaptcha) => dispatch(doUserEmailVerify(code, recaptcha)),
|
|
||||||
verifyUserEmailFailure: error => dispatch(doUserEmailVerifyFailure(error)),
|
|
||||||
resendVerificationEmail: email => dispatch(doUserResendVerificationEmail(email)),
|
resendVerificationEmail: email => dispatch(doUserResendVerificationEmail(email)),
|
||||||
|
doCheckEmailVerified: () => dispatch(doUserFetch(true)),
|
||||||
|
navigate: path => dispatch(doNavigate(path)),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(
|
export default connect(
|
||||||
|
|
|
@ -1,48 +1,37 @@
|
||||||
// @flow
|
// @flow
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
import { Form, FormField, FormRow, Submit } from 'component/common/form';
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
cancelButton: React.Node,
|
cancelButton: React.Node,
|
||||||
errorMessage: ?string,
|
|
||||||
email: string,
|
email: string,
|
||||||
isPending: boolean,
|
|
||||||
onModal?: boolean,
|
|
||||||
verifyUserEmail: (string, string) => void,
|
|
||||||
verifyUserEmailFailure: string => void,
|
|
||||||
resendVerificationEmail: string => void,
|
resendVerificationEmail: string => void,
|
||||||
|
doCheckEmailVerified: () => void,
|
||||||
|
user: {
|
||||||
|
has_verified_email: boolean,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
type State = {
|
class UserEmailVerify extends React.PureComponent<Props> {
|
||||||
code: string,
|
|
||||||
};
|
|
||||||
|
|
||||||
class UserEmailVerify extends React.PureComponent<Props, State> {
|
|
||||||
constructor(props: Props) {
|
constructor(props: Props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
this.emailVerifyCheckInterval = null;
|
||||||
this.state = {
|
|
||||||
code: '',
|
|
||||||
};
|
|
||||||
|
|
||||||
(this: any).handleSubmit = this.handleSubmit.bind(this);
|
|
||||||
(this: any).handleResendVerificationEmail = this.handleResendVerificationEmail.bind(this);
|
(this: any).handleResendVerificationEmail = this.handleResendVerificationEmail.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleCodeChanged(event: SyntheticInputEvent<*>) {
|
componentDidMount() {
|
||||||
this.setState({
|
this.emailVerifyCheckInterval = setInterval(() => this.checkIfVerified(), 5000);
|
||||||
code: String(event.target.value).trim(),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleSubmit() {
|
componentDidUpdate() {
|
||||||
const { code } = this.state;
|
if (this.emailVerifyCheckInterval && this.props.user.has_verified_email) {
|
||||||
try {
|
clearInterval(this.emailVerifyCheckInterval);
|
||||||
const verification = JSON.parse(atob(code));
|
}
|
||||||
this.props.verifyUserEmail(verification.token, verification.recaptcha);
|
}
|
||||||
} catch (error) {
|
|
||||||
this.props.verifyUserEmailFailure('Invalid Verification Token');
|
componentWillUnmount() {
|
||||||
|
if (this.emailVerifyCheckInterval) {
|
||||||
|
clearInterval(this.emailVerifyCheckInterval);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,55 +39,38 @@ class UserEmailVerify extends React.PureComponent<Props, State> {
|
||||||
this.props.resendVerificationEmail(this.props.email);
|
this.props.resendVerificationEmail(this.props.email);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkIfVerified() {
|
||||||
|
const { doCheckEmailVerified } = this.props;
|
||||||
|
doCheckEmailVerified();
|
||||||
|
}
|
||||||
|
|
||||||
|
emailVerifyCheckInterval: ?IntervalID;
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { cancelButton, errorMessage, email, isPending, onModal } = this.props;
|
const { cancelButton, email } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span>
|
<div>
|
||||||
<p>Please enter the verification code emailed to {email}.</p>
|
<p>
|
||||||
|
{__('An email was sent to')} {email}.{' '}
|
||||||
<Form onSubmit={this.handleSubmit}>
|
{__('Follow the link and you will be good to go. This will update automatically.')}
|
||||||
<FormRow padded>
|
</p>
|
||||||
<FormField
|
|
||||||
stretch
|
|
||||||
name="code"
|
|
||||||
type="text"
|
|
||||||
placeholder="eyJyZWNhcHRjaGEiOiIw..."
|
|
||||||
label={__('Verification Code')}
|
|
||||||
error={errorMessage}
|
|
||||||
value={this.state.code}
|
|
||||||
onChange={event => this.handleCodeChanged(event)}
|
|
||||||
/>
|
|
||||||
</FormRow>
|
|
||||||
|
|
||||||
<div className="card__actions">
|
<div className="card__actions">
|
||||||
<Submit label={__('Verify')} disabled={isPending} />
|
|
||||||
{cancelButton}
|
|
||||||
{!onModal && (
|
|
||||||
<Button
|
<Button
|
||||||
button="link"
|
button="primary"
|
||||||
label={__('Resend verification email')}
|
label={__('Resend verification email')}
|
||||||
onClick={this.handleResendVerificationEmail}
|
onClick={this.handleResendVerificationEmail}
|
||||||
/>
|
/>
|
||||||
)}
|
{cancelButton}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p className="help">
|
<p className="help">
|
||||||
{__('Email')} <Button button="link" href="mailto:help@lbry.io" label="help@lbry.io" />{' '}
|
{__('Email')} <Button button="link" href="mailto:help@lbry.io" label="help@lbry.io" /> or
|
||||||
or join our <Button button="link" href="https://chat.lbry.io" label="chat" />{' '}
|
join our <Button button="link" href="https://chat.lbry.io" label="chat" />{' '}
|
||||||
{__('if you encounter any trouble with your code.')}
|
{__('if you encounter any trouble verifying.')}
|
||||||
</p>
|
</p>
|
||||||
{onModal && (
|
|
||||||
<div className="card__actions help">
|
|
||||||
<Button
|
|
||||||
button="link"
|
|
||||||
label={__('Resend verification email')}
|
|
||||||
onClick={this.handleResendVerificationEmail}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
</Form>
|
|
||||||
</span>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,10 @@ const APPPAGEURL = 'lbry://?';
|
||||||
|
|
||||||
autoUpdater.logger = remote.require('electron-log');
|
autoUpdater.logger = remote.require('electron-log');
|
||||||
|
|
||||||
|
if (process.env.LBRY_API_URL) {
|
||||||
|
Lbryio.setLocalApi(process.env.LBRY_API_URL);
|
||||||
|
}
|
||||||
|
|
||||||
// We need to override Lbryio for getting/setting the authToken
|
// We need to override Lbryio for getting/setting the authToken
|
||||||
// We interect with ipcRenderer to get the auth key from a users keyring
|
// We interect with ipcRenderer to get the auth key from a users keyring
|
||||||
// We keep a local variable for authToken beacuse `ipcRenderer.send` does not
|
// We keep a local variable for authToken beacuse `ipcRenderer.send` does not
|
||||||
|
|
|
@ -67,7 +67,9 @@ export class Modal extends React.PureComponent<ModalProps> {
|
||||||
![null, undefined, ''].includes(overlayClassName) ? overlayClassName : 'modal-overlay'
|
![null, undefined, ''].includes(overlayClassName) ? overlayClassName : 'modal-overlay'
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<h1 className="card__title">{title}</h1>
|
<header className="card__header">
|
||||||
|
<h2 className="card__title">{title}</h2>
|
||||||
|
</header>
|
||||||
<div className="card__content">{children}</div>
|
<div className="card__content">{children}</div>
|
||||||
{type === 'custom' ? null : ( // custom modals define their own buttons
|
{type === 'custom' ? null : ( // custom modals define their own buttons
|
||||||
<div className="card__actions">
|
<div className="card__actions">
|
||||||
|
|
|
@ -16,7 +16,7 @@ class ModalAffirmPurchase extends React.PureComponent<Props> {
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.onAffirmPurchase = this.onAffirmPurchase.bind(this);
|
(this: any).onAffirmPurchase = this.onAffirmPurchase.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
onAffirmPurchase() {
|
onAffirmPurchase() {
|
||||||
|
|
|
@ -9,8 +9,12 @@ type Props = {
|
||||||
declineAutoUpdate: () => any,
|
declineAutoUpdate: () => any,
|
||||||
};
|
};
|
||||||
|
|
||||||
class ModalAutoUpdateDownloaded extends React.PureComponent<Props> {
|
type State = {
|
||||||
constructor(props: ModalProps) {
|
disabled: boolean,
|
||||||
|
};
|
||||||
|
|
||||||
|
class ModalAutoUpdateDownloaded extends React.PureComponent<Props, State> {
|
||||||
|
constructor(props: Props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
|
|
|
@ -12,6 +12,15 @@ type Props = {
|
||||||
};
|
};
|
||||||
|
|
||||||
class ModalEmailCollection extends React.PureComponent<Props> {
|
class ModalEmailCollection extends React.PureComponent<Props> {
|
||||||
|
getTitle() {
|
||||||
|
const { user } = this.props;
|
||||||
|
if (user && !user.has_verified_email) {
|
||||||
|
return __('Awaiting Confirmation');
|
||||||
|
}
|
||||||
|
|
||||||
|
return __('Can We Stay In Touch?');
|
||||||
|
}
|
||||||
|
|
||||||
renderInner() {
|
renderInner() {
|
||||||
const { closeModal, email, user } = this.props;
|
const { closeModal, email, user } = this.props;
|
||||||
|
|
||||||
|
@ -34,7 +43,7 @@ class ModalEmailCollection extends React.PureComponent<Props> {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal type="custom" isOpen contentLabel="Email" title={__('Can We Stay In Touch?')}>
|
<Modal type="custom" isOpen contentLabel="Email" title={this.getTitle()}>
|
||||||
<section className="card__content">{this.renderInner()}</section>
|
<section className="card__content">{this.renderInner()}</section>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
|
|
|
@ -31,8 +31,8 @@ import ModalWalletUnlock from 'modal/modalWalletUnlock';
|
||||||
import ModalRewardCode from 'modal/modalRewardCode';
|
import ModalRewardCode from 'modal/modalRewardCode';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
notification: { id: string },
|
modal: { id: string, modalProps: {} },
|
||||||
notificationProps: {},
|
error: string | { message: string },
|
||||||
};
|
};
|
||||||
|
|
||||||
class ModalRouter extends React.PureComponent<Props> {
|
class ModalRouter extends React.PureComponent<Props> {
|
||||||
|
@ -80,6 +80,7 @@ class ModalRouter extends React.PureComponent<Props> {
|
||||||
|
|
||||||
checkShowWelcome(props) {
|
checkShowWelcome(props) {
|
||||||
const { isWelcomeAcknowledged, user } = props;
|
const { isWelcomeAcknowledged, user } = props;
|
||||||
|
|
||||||
if (!isWelcomeAcknowledged && user && !user.is_reward_approved && !user.is_identity_verified) {
|
if (!isWelcomeAcknowledged && user && !user.is_reward_approved && !user.is_identity_verified) {
|
||||||
return MODALS.WELCOME;
|
return MODALS.WELCOME;
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ class AuthPage extends React.PureComponent<Props> {
|
||||||
if (isPending || (user && !user.has_verified_email && !email)) {
|
if (isPending || (user && !user.has_verified_email && !email)) {
|
||||||
return __('Human Proofing');
|
return __('Human Proofing');
|
||||||
} else if (user && !user.has_verified_email) {
|
} else if (user && !user.has_verified_email) {
|
||||||
return __('Confirm Email');
|
return __('Awaiting Confirmation');
|
||||||
} else if (user && !user.is_identity_verified && !user.is_reward_approved) {
|
} else if (user && !user.is_identity_verified && !user.is_reward_approved) {
|
||||||
return __('Final Verification');
|
return __('Final Verification');
|
||||||
}
|
}
|
||||||
|
|
|
@ -5683,9 +5683,9 @@ lbry-redux@lbryio/lbry-redux#84b7d396934d57a37802aadbef71db91230a9404:
|
||||||
reselect "^3.0.0"
|
reselect "^3.0.0"
|
||||||
uuid "^3.3.2"
|
uuid "^3.3.2"
|
||||||
|
|
||||||
lbryinc@lbryio/lbryinc#68c8ce6b7608dabc9180fff9b4841d10e1c62284:
|
lbryinc@lbryio/lbryinc#cee70d482cf352f2e66215fa109f4178406228ca:
|
||||||
version "0.0.1"
|
version "0.0.1"
|
||||||
resolved "https://codeload.github.com/lbryio/lbryinc/tar.gz/68c8ce6b7608dabc9180fff9b4841d10e1c62284"
|
resolved "https://codeload.github.com/lbryio/lbryinc/tar.gz/cee70d482cf352f2e66215fa109f4178406228ca"
|
||||||
dependencies:
|
dependencies:
|
||||||
lbry-redux lbryio/lbry-redux#84b7d396934d57a37802aadbef71db91230a9404
|
lbry-redux lbryio/lbry-redux#84b7d396934d57a37802aadbef71db91230a9404
|
||||||
reselect "^3.0.0"
|
reselect "^3.0.0"
|
||||||
|
|
Loading…
Reference in a new issue