Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Phone verification #946

Merged
merged 16 commits into from
Jan 22, 2018
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 0 additions & 16 deletions src/renderer/component/userEmailNew/index.js

This file was deleted.

55 changes: 0 additions & 55 deletions src/renderer/component/userEmailNew/view.jsx

This file was deleted.

22 changes: 0 additions & 22 deletions src/renderer/component/userEmailVerify/index.js

This file was deleted.

22 changes: 22 additions & 0 deletions src/renderer/component/userFieldNew/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React from 'react';
import { connect } from 'react-redux';
import { doUserEmailNew, doUserPhoneNew, doUserInviteNew } from 'redux/actions/user';
import {
selectEmailNewIsPending,
selectEmailNewErrorMessage,
selectPhoneNewErrorMessage,
} from 'redux/selectors/user';
import UserFieldNew from './view';

const select = state => ({
isPending: selectEmailNewIsPending(state),
emailErrorMessage: selectEmailNewErrorMessage(state),
phoneErrorMessage: selectPhoneNewErrorMessage(state),
});

const perform = dispatch => ({
addUserEmail: email => dispatch(doUserEmailNew(email)),
addUserPhone: (phone, country_code) => dispatch(doUserPhoneNew(phone, country_code)),
});

export default connect(select, perform)(UserFieldNew);
129 changes: 129 additions & 0 deletions src/renderer/component/userFieldNew/view.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import React from 'react';
import { Form, FormRow, Submit } from 'component/form.js';

class UserFieldNew extends React.PureComponent {
constructor(props) {
super(props);

this.state = {
phone: '',
email: '',
country_code: '+1',
};

this.formatPhone = this.formatPhone.bind(this);
}

formatPhone(value) {
const { country_code } = this.state;
value = value.replace(/\D/g, '');
if (country_code === '+1') {
if (!value) {
return '';
} else if (value.length < 4) {
return value;
} else if (value.length < 7) {
return `(${value.substring(0, 3)}) ${value.substring(3)}`;
}
const fullNumber = `(${value.substring(0, 3)}) ${value.substring(3, 6)}-${value.substring(
6
)}`;
return fullNumber.length <= 14 ? fullNumber : fullNumber.substring(0, 14);
}
return value;
}

formatCountryCode(value) {
if (value) {
return `+${value.replace(/\D/g, '')}`;
}
return '+1';
}

handleChanged(event, fieldType) {
const formatter = {
email: _ => _,
phone: this.formatPhone,
country_code: this.formatCountryCode,
};
this.setState({
[fieldType]: formatter[fieldType](event.target.value),
});
}

handleSubmit() {
const { email, phone, country_code } = this.state;
if (phone) {
this.props.addUserPhone(phone.replace(/\D/g, ''), country_code.replace(/\D/g, ''));
} else {
this.props.addUserEmail(email);
}
}

render() {
const { cancelButton, emailErrorMessage, phoneErrorMessage, isPending, fieldType } = this.props;

return fieldType === 'phone' ? (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a particular reason we use a single component to do two (seemingly) distinct duties, as opposed to UserEmailNew and UserPhoneNew or equivalent?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was in the spirit of DRY a la our coding maxims.

There is less of a case for userFieldNew to be used for two field types, but userFieldVerify is basically the same for both types. I made userFieldNew to be consistent with that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see very much savings here aside from maybe some boilerplate in index.js, and it adds complexity to declaration and handling changes. I'd split these.

<div>
<p>
{__(
'Enter your phone number and we will send you a verification code. We will not share your phone number with third parties.'
)}
</p>
<Form onSubmit={this.handleSubmit.bind(this)}>
<FormRow
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this have to be collected separately as opposed to letting them type a full number? If it is separate field, could it at least be an inline select next to the full number entry?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose it could be a select. Twilio wants a separate country code. I tried to make this as fool proof and intuitive as possible. I think the way it is makes a lot of sense (it was based on a discussion in tech-app), but I am completely open to changing it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@liamcardenas yes, please make the country code a select displayed before the phone number entry.

type="text"
label="Country Code"
name="country_code"
value={this.state.country_code}
errorMessage={phoneErrorMessage}
onChange={event => {
this.handleChanged(event, 'country_code');
}}
/>
<FormRow
type="text"
label="Phone"
placeholder={this.state.country_code === '+1' ? '(555) 555-5555' : '5555555555'}
name="phone"
value={this.state.phone}
errorMessage={phoneErrorMessage}
onChange={event => {
this.handleChanged(event, 'phone');
}}
/>
<div className="form-row-submit">
<Submit label="Submit" disabled={isPending} />
{cancelButton}
</div>
</Form>
</div>
) : (
<div>
<p>
{__("We'll let you know about LBRY updates, security issues, and great new content.")}
</p>
<p>{__("We'll never sell your email, and you can unsubscribe at any time.")}</p>
<Form onSubmit={this.handleSubmit.bind(this)}>
<FormRow
type="text"
label="Email"
placeholder="[email protected]"
name="email"
value={this.state.email}
errorMessage={emailErrorMessage}
onChange={event => {
this.handleChanged(event, 'email');
}}
/>
<div className="form-row-submit">
<Submit label="Submit" disabled={isPending} />
{cancelButton}
</div>
</Form>
</div>
);
}
}

export default UserFieldNew;
29 changes: 29 additions & 0 deletions src/renderer/component/userFieldVerify/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React from 'react';
import { connect } from 'react-redux';
import { doUserEmailVerify, doUserPhoneVerify, doUserEmailVerifyFailure } from 'redux/actions/user';
import {
selectEmailVerifyIsPending,
selectEmailToVerify,
selectPhoneToVerify,
selectEmailVerifyErrorMessage,
selectPhoneVerifyErrorMessage,
selectUserCountryCode,
} from 'redux/selectors/user';
import UserFieldVerify from './view';

const select = state => ({
isPending: selectEmailVerifyIsPending(state),
email: selectEmailToVerify(state),
phone: selectPhoneToVerify(state),
countryCode: selectUserCountryCode(state),
emailErrorMessage: selectEmailVerifyErrorMessage(state),
phoneErrorMessage: selectPhoneVerifyErrorMessage(state),
});

const perform = dispatch => ({
verifyUserEmail: (code, recaptcha) => dispatch(doUserEmailVerify(code, recaptcha)),
verifyUserPhone: code => dispatch(doUserPhoneVerify(code)),
verifyUserEmailFailure: error => dispatch(doUserEmailVerifyFailure(error)),
});

export default connect(select, perform)(UserFieldVerify);
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react';
import Link from 'component/link';
import { Form, FormRow, Submit } from 'component/form.js';

class UserEmailVerify extends React.PureComponent {
class UserFieldVerify extends React.PureComponent {
constructor(props) {
super(props);

Expand All @@ -19,19 +19,35 @@ class UserEmailVerify extends React.PureComponent {

handleSubmit() {
const { code } = this.state;
try {
const verification = JSON.parse(atob(code));
this.props.verifyUserEmail(verification.token, verification.recaptcha);
} catch (error) {
this.props.verifyUserEmailFailure('Invalid Verification Token');
const { fieldType } = this.props;
if (fieldType === 'phone') {
this.props.verifyUserPhone(code);
} else {
try {
const verification = JSON.parse(atob(code));
this.props.verifyUserEmail(verification.token, verification.recaptcha);
} catch (error) {
this.props.verifyUserEmailFailure('Invalid Verification Token');
}
}
}

render() {
const { cancelButton, errorMessage, email, isPending } = this.props;
const {
cancelButton,
emailErrorMessage,
phoneErrorMessage,
email,
isPending,
phone,
countryCode,
} = this.props;
return (
<Form onSubmit={this.handleSubmit.bind(this)}>
<p>Please enter the verification code emailed to {email}.</p>
<p>
Please enter the verification code sent to {countryCode ? `+${countryCode}` : ''}
{phone || email}.
</p>
<FormRow
type="text"
label={__('Verification Code')}
Expand All @@ -40,7 +56,7 @@ class UserEmailVerify extends React.PureComponent {
onChange={event => {
this.handleCodeChanged(event);
}}
errorMessage={errorMessage}
errorMessage={emailErrorMessage || phoneErrorMessage}
/>
{/* render help separately so it always shows */}
<div className="form-field__helper">
Expand All @@ -59,4 +75,4 @@ class UserEmailVerify extends React.PureComponent {
}
}

export default UserEmailVerify;
export default UserFieldVerify;
5 changes: 5 additions & 0 deletions src/renderer/component/userVerify/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import {
selectIdentityVerifyErrorMessage,
} from 'redux/selectors/user';
import UserVerify from './view';
import { selectCurrentModal } from 'redux/selectors/app';
import { doOpenModal } from 'redux/actions/app';
import { PHONE_COLLECTION } from 'constants/modal_types';

const select = (state, props) => {
const selectReward = makeSelectRewardByType();
Expand All @@ -17,12 +20,14 @@ const select = (state, props) => {
isPending: selectIdentityVerifyIsPending(state),
errorMessage: selectIdentityVerifyErrorMessage(state),
reward: selectReward(state, { reward_type: rewards.TYPE_NEW_USER }),
modal: selectCurrentModal(state),
};
};

const perform = dispatch => ({
navigate: uri => dispatch(doNavigate(uri)),
verifyUserIdentity: token => dispatch(doUserIdentityVerify(token)),
verifyPhone: () => dispatch(doOpenModal(PHONE_COLLECTION)),
});

export default connect(select, perform)(UserVerify);
Loading