Skip to content
This repository has been archived by the owner on Oct 1, 2019. It is now read-only.

Commit

Permalink
Implement "Sign up with" buttons and sign up form autofill
Browse files Browse the repository at this point in the history
  • Loading branch information
voidxnull committed Jul 9, 2017
1 parent b80088c commit d712b29
Show file tree
Hide file tree
Showing 8 changed files with 108 additions and 28 deletions.
2 changes: 1 addition & 1 deletion src/api/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,7 @@ export default class ApiClient {
}

async registerUser(userData: Object): Promise<Success & { user: User }> {
const response = await this.post(`/api/v1/users`, userData);
const response = await this.postJSON(`/api/v1/users`, userData);
return await response.json();
}

Expand Down
11 changes: 9 additions & 2 deletions src/components/login/v2.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import React, { PropTypes } from 'react';
import CSSTransitionGroup from 'react-transition-group/CSSTransitionGroup';
import { connect } from 'react-redux';
import { Link } from 'react-router';
import { Link, browserHistory } from 'react-router';
import classNames from 'classnames';
import isEqual from 'lodash/isEqual';
import t from 't8on';
Expand Down Expand Up @@ -152,7 +152,14 @@ class LoginComponentV2 extends React.Component {
await this.triggers.loginUser(true, user);
} else {
console.error(error); // eslint-disable-line no-console
// TODO: If `user` is not present, redirect to the sign up form and autofill fields using `profile`.
if (profile) {
// Is there a better way? Is it worth using redux here?
window.localStorage.setItem('selectedOauthProfile', JSON.stringify(profile));
browserHistory.push({
...browserHistory.getCurrentLocation(),
hash: '#signup'
});
}
}
}

Expand Down
91 changes: 79 additions & 12 deletions src/components/register/form.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ import classNames from 'classnames';
import debounce from 'debounce-promise';
import { form as inform, from } from 'react-inform';
import { Link } from 'react-router';
import { noop, omit, reduce, transform } from 'lodash';
import { noop, omit, reduce, transform, get } from 'lodash';
import zxcvbn from 'zxcvbn';
import slug from 'slug';

import { API_HOST } from '../../config';
import ApiClient from '../../api/client';
Expand All @@ -30,6 +31,7 @@ import { removeWhitespace as normalizeWhitespace } from '../../utils/lang';
import AltButton from '../alt-button';
import Button from '../button';
import FormField from '../form/field';
import { openOauthPopup } from '../../utils/auth';
import SignupSuccessMessage from './success';

const hiddenStyle = { display: 'none' };
Expand Down Expand Up @@ -131,6 +133,12 @@ export class SignupFormV2 extends React.Component {
this.username.addEventListener('focus', this.handleUsernameFocus);
this.username.addEventListener('blur', this.handleUsernameBlur);
this.username.addEventListener('input', this.handleUsernameInput);

const profile = JSON.parse(window.localStorage.getItem('selectedOauthProfile'));
if (profile) {
this.fillInFormWithProfile(profile);
window.localStorage.removeItem('selectedOauthProfile');
}
}

componentWillReceiveProps(nextProps) {
Expand Down Expand Up @@ -254,16 +262,61 @@ export class SignupFormV2 extends React.Component {

await this.props.onSubmit(
true,
values.username,
values.password,
values.email,
normalizeWhitespace(values.firstName),
normalizeWhitespace(values.lastName)
{
username: values.username,
password: values.password,
email: values.email,
firstName: normalizeWhitespace(values.firstName),
lastName: normalizeWhitespace(values.lastName),
providers: values.providers
}
);

return resetValidatorsCache();
};

signUpWith = async (provider) => {
const { profile } = await openOauthPopup(provider, { onlyProfile: true });
this.fillInFormWithProfile(profile);
}

fillInFormWithProfile = async (profile) => {
const email = get(profile, 'emails[0].value');
let firstName = get(profile, 'name.givenName');
let lastName = get(profile, 'name.familyName');
if (!firstName && !lastName && profile.displayName) {
[firstName, lastName] = profile.displayName.split(' ');
}

let username = profile.username;

if (!username && email) {
username = email.split('@')[0];
}

if (!username && profile.displayName) {
username = profile.displayName;
}

username = await client.getAvailableUsername(slug(username.toLowerCase()));

this.usernameManuallyChanged = true;
this.props.form.onValues({
username,
email,
firstName,
lastName,
providers: {
[profile.provider]: profile
}
});
}

handleFacebook = this.signUpWith.bind(this, 'facebook');
handleGoogle = this.signUpWith.bind(this, 'google');
handleTwitter = this.signUpWith.bind(this, 'twitter');
handleGithub = this.signUpWith.bind(this, 'github');

render() {
const { fields, translate: t, format: f, rtl } = this.props;

Expand Down Expand Up @@ -325,17 +378,31 @@ export class SignupFormV2 extends React.Component {
</div>
<div className="form__background--bright form__alt">
<AltButton
icon="github"
onClick={undefined}
icon="facebook-official"
onClick={this.handleFacebook}
>
{f('signup.with', 'Github')}
{f('signup.with', 'Facebook')}
</AltButton>
<AltButton
className="margin--all_top"
icon="facebook-official"
onClick={undefined}
icon="google"
onClick={this.handleGoogle}
>
{f('signup.with', 'Facebook')}
{f('signup.with', 'Google')}
</AltButton>
<AltButton
className="margin--all_top"
icon="twitter"
onClick={this.handleTwitter}
>
{f('signup.with', 'Twitter')}
</AltButton>
<AltButton
className="margin--all_top"
icon="github"
onClick={this.handleGithub}
>
{f('signup.with', 'Github')}
</AltButton>
<Link
className={AltButton.defaultClassName.concat(" margin--all_top")}
Expand Down
14 changes: 7 additions & 7 deletions src/components/register/v1.js
Original file line number Diff line number Diff line change
Expand Up @@ -165,13 +165,13 @@ export class UnwrappedRegister extends React.Component {
}

const htmlForm = event.target;
this.props.onRegisterUser(
fields.username.value,
fields.password.value,
fields.email.value,
htmlForm.querySelector('input[name=firstName]').value,
htmlForm.querySelector('input[name=lastName]').value
);
this.props.onRegisterUser({
username: fields.username.value,
password: fields.password.value,
email: fields.email.value,
firstName: htmlForm.querySelector('input[name=firstName]').value,
lastName: htmlForm.querySelector('input[name=lastName]').value
});
};

handleNameChange = async (event) => {
Expand Down
4 changes: 2 additions & 2 deletions src/components/register/v2.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,13 +108,13 @@ class RegisterComponentV2 extends React.Component {
|| !isEqual(nextState, this.state);
}

handleSubmit = async (isValid, ...args) => {
handleSubmit = async (isValid, attrs) => {
this.props.dispatch(removeAllMessages());
if (!isValid) {
return;
}

await this.triggers.registerUser(...args);
await this.triggers.registerUser(attrs);
};

handleErrors = (formErrors) => {
Expand Down
4 changes: 2 additions & 2 deletions src/triggers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -389,12 +389,12 @@ export class ActionsTrigger {
}
};

registerUser = async (username, password, email, firstName, lastName) => {
registerUser = async (attrs) => {
this.dispatch(a.messages.removeAllMessages());

// FIXME: disable form
try {
const result = await this.client.registerUser({ username, password, email, firstName, lastName });
const result = await this.client.registerUser(attrs);

if (result.success) {
this.dispatch(a.users.registrationSuccess());
Expand Down
8 changes: 7 additions & 1 deletion test/integration/components/register.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,13 @@ describe('UnwpappedAuth page', () => {
wrapper.find('#registerForm').simulate('submit');
await waitForTrue(() => register.props().form.isValid());

return expect(onRegisterUser.calledWith(userAttrs.username, userAttrs.password, userAttrs.email, '', ''), 'to be true');
return expect(onRegisterUser.calledWith({
username: userAttrs.username,
password: userAttrs.password,
email: userAttrs.email,
firstName: '',
lastName: ''
}), 'to be true');
});
});
});
2 changes: 1 addition & 1 deletion test/integration/triggers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ describe('ActionsTrigger', () => {
const store = initState();
const triggers = new ActionsTrigger(client, store.dispatch);

await triggers.registerUser(undefined, undefined, undefined, userAttrs.firstName, userAttrs.lastName);
await triggers.registerUser({ firstName: userAttrs.firstName, lastName: userAttrs.lastName });
expect(
store.getState().getIn(['messages', 0, 'message']),
'to equal',
Expand Down

0 comments on commit d712b29

Please sign in to comment.