Skip to content

Commit

Permalink
Implements invitation acceptance
Browse files Browse the repository at this point in the history
  • Loading branch information
dzucconi committed Aug 6, 2018
1 parent 6d8a13b commit 0c18bb1
Show file tree
Hide file tree
Showing 11 changed files with 183 additions and 48 deletions.
6 changes: 3 additions & 3 deletions apps/authentication/Routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ export default () => (
/>

<Route
path="/register/:token"
path="/register/:invitation_token"
render={parseRoute(({ params, query }) => (
<AcceptInvitationPage
{...params}
{...query}
invitation_token={params.invitation_token}
raw_invitation_token={query.invite_token}
/>
))}
/>
Expand Down
5 changes: 3 additions & 2 deletions apps/authentication/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,14 @@ app
.set('views', `${__dirname}/templates`)
.set('view engine', 'jade')

.get(/^\/(sign_up|log_in|forgot|register\/\w+)/, apolloMiddleware, (req, res) => {
.get(/^\/(sign_up|log_in|forgot|register\/\w+)/, apolloMiddleware, (req, res, next) => {
if (req.user && req.user.id) return res.redirect('/');

res.locals.sd.REDIRECT_TO = req.query['redirect-to'] || '/';

return req.apollo.render(withStaticRouter(Routes))
.then(apollo => res.render('index', { apollo }));
.then(apollo => res.render('index', { apollo }))
.catch(next);
})
.get('/me/sign_out', logoutMiddleware, redirectToMiddleware)
.get('/me/refresh', (req, res, next) => {
Expand Down
10 changes: 2 additions & 8 deletions assets/auth.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
import sharify from 'sharify';

import { mountWithApolloProvider } from 'react/apollo';

import withBrowserRouter from 'react/hocs/WithBrowserRouter';

import Routes from 'apps/authentication/Routes';

const { data: { APOLLO } } = sharify;

document.addEventListener('DOMContentLoaded', () => {
const mountPoint = document.getElementById('apolloMount');
mountWithApolloProvider(withBrowserRouter(Routes), APOLLO, mountPoint);
});
document.addEventListener('DOMContentLoaded', () =>
mountWithApolloProvider(withBrowserRouter(Routes), {}, document.getElementById('apolloMount')));
78 changes: 51 additions & 27 deletions react/components/RegistrationForm/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
import { graphql } from 'react-apollo';
import { compose, graphql } from 'react-apollo';
import axios from 'axios';

import mapErrors from 'react/util/mapErrors';
import compactObject from 'react/util/compactObject';

import Button from 'react/components/UI/GenericButton';
import Box from 'react/components/UI/Box';
Expand All @@ -13,25 +14,38 @@ import AuthForm from 'react/components/AuthForm';
import { Checkbox, Label, Input, ErrorMessage } from 'react/components/UI/Inputs';

import registerMutation from 'react/components/RegistrationForm/mutations/register';
import acceptInvitationMutation from 'react/components/RegistrationForm/mutations/acceptInvitation';

const { REDIRECT_TO } = require('sharify').data;

class RegistrationForm extends Component {
static propTypes = {
register: PropTypes.func.isRequired,
acceptInvitation: PropTypes.func.isRequired,
email: PropTypes.string,
raw_invitation_token: PropTypes.string,
}

state = {
mode: 'resting',
email: '',
first_name: '',
last_name: '',
password: '',
password_confirmation: '',
accept_terms: false,
receive_newsletter: false,
attributeErrors: {},
errorMessage: null,
static defaultProps = {
email: null,
raw_invitation_token: null,
}

constructor(props) {
super(props);

this.state = {
mode: 'resting',
email: props.email || '',
first_name: '',
last_name: '',
password: '',
password_confirmation: '',
accept_terms: false,
receive_newsletter: false,
attributeErrors: {},
errorMessage: null,
};
}

handleInput = fieldName => ({ target: { value: fieldValue } }) =>
Expand All @@ -58,7 +72,14 @@ class RegistrationForm extends Component {
handleSubmit = (e) => {
e.preventDefault();

const { register } = this.props;
const {
register,
acceptInvitation,
raw_invitation_token,
} = this.props;

const mutation = raw_invitation_token ?
acceptInvitation : register;

const {
email,
Expand All @@ -72,17 +93,18 @@ class RegistrationForm extends Component {

this.setState({ mode: 'registering' });

return register({
variables: {
email,
first_name,
last_name,
password,
accept_terms,
password_confirmation,
receive_newsletter,
},
})
const variables = compactObject({
email,
first_name,
last_name,
password,
accept_terms,
password_confirmation,
receive_newsletter,
invitation_token: raw_invitation_token,
});

return mutation({ variables })
.then(() => {
this.setState({ mode: 'logging_in' });
return axios.post('/me/sign_in', { email, password });
Expand Down Expand Up @@ -127,6 +149,7 @@ class RegistrationForm extends Component {
onChange={this.handleEmail}
value={email}
errorMessage={attributeErrors.email}
readOnly={!!this.props.email}
required
/>

Expand Down Expand Up @@ -224,6 +247,7 @@ class RegistrationForm extends Component {
}
}

export default graphql(registerMutation, {
name: 'register',
})(RegistrationForm);
export default compose(
graphql(registerMutation, { name: 'register' }),
graphql(acceptInvitationMutation, { name: 'acceptInvitation' }),
)(RegistrationForm);
30 changes: 30 additions & 0 deletions react/components/RegistrationForm/mutations/acceptInvitation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import gql from 'graphql-tag';

export default gql`
mutation acceptInvitationMutation(
$invitation_token: String!
$first_name: String!,
$last_name: String!,
$email: String!,
$password: String!,
$password_confirmation: String!,
$receive_newsletter: Boolean
$receive_tips_emails: Boolean
) {
accept_invitation(input: {
invitation_token: $invitation_token
first_name: $first_name
last_name: $last_name
email: $email
password: $password
password_confirmation: $password_confirmation
receive_newsletter: $receive_newsletter
receive_tips_emails: $receive_tips_emails
}) {
me {
id
email
}
}
}
`;
18 changes: 16 additions & 2 deletions react/components/RegistrationForm/mutations/register.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,22 @@
import gql from 'graphql-tag';

export default gql`
mutation registerMutation($first_name: String!, $last_name: String!, $email: String!, $password: String!, $password_confirmation: String!, $receive_newsletter: Boolean) {
registration(input: {first_name: $first_name, last_name: $last_name, email: $email, password: $password, password_confirmation: $password_confirmation, receive_newsletter: $receive_newsletter}) {
mutation registerMutation(
$first_name: String!,
$last_name: String!,
$email: String!,
$password: String!,
$password_confirmation: String!,
$receive_newsletter: Boolean
) {
registration(input: {
first_name: $first_name,
last_name: $last_name,
email: $email,
password: $password,
password_confirmation: $password_confirmation,
receive_newsletter: $receive_newsletter
}) {
me {
id
}
Expand Down
6 changes: 4 additions & 2 deletions react/components/UI/Box/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import styled from 'styled-components';
import { display, space, width, alignItems, minHeight } from 'styled-system';
import { display, space, width, alignItems, minHeight, justifyContent, flexDirection } from 'styled-system';

export default styled.div`
${display}
${width}
${minHeight}
${space}
${alignItems}
${minHeight}
${justifyContent}
${flexDirection}
`;
1 change: 1 addition & 0 deletions react/components/UI/CenteringBox/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ export default styled(Box).attrs({
width: '100%',
minHeight: '100vh',
alignItems: 'center',
justifyContent: 'center',
})`
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import gql from 'graphql-tag';

export default gql`
fragment Invitee on Invitee {
__typename
id
email
}
`;
56 changes: 52 additions & 4 deletions react/pages/authentication/AcceptInvitationPage/index.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,62 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Query } from 'react-apollo';

import inviteeQuery from 'react/pages/authentication/AcceptInvitationPage/queries/invitee';

import Icon from 'react/components/UI/Icon';
import CenteringBox from 'react/components/UI/CenteringBox';
import Text from 'react/components/UI/Text';
import RegistrationForm from 'react/components/RegistrationForm';

export default class RegistrationPage extends Component {
export default class AcceptInvitationPage extends Component {
static propTypes = {
// `invitation_token` is used to locate the invite
// it is a digest of `raw_invitation_token` and exists on the user record.
invitation_token: PropTypes.string.isRequired,
// `raw_invitation_token` is used to accept the invite
// it is not in the database directly.
// At the moment this is passed as `invite_token` in the
// query string of the URL in the invitation email
raw_invitation_token: PropTypes.string.isRequired,
}

render() {
const { invitation_token, raw_invitation_token } = this.props;

return (
<CenteringBox p={7}>
<RegistrationForm />
</CenteringBox>
<Query query={inviteeQuery} variables={{ invitation_token }}>
{({ loading, error, data }) => {
if (loading) return <div />;

if (error) {
return (
<CenteringBox p={7} flexDirection="column">
<Icon name="ArenaMark" size={7} mb={9} />

<Text f={5} mb={6}>
We cannot find that invitation code.
</Text>

<Text f={2} underlineLinks>
If you believe this is in error please contact
{' '}
<a href="mailto:[email protected]">[email protected]</a>
</Text>
</CenteringBox>
);
}

return (
<CenteringBox p={7}>
<RegistrationForm
email={data.invitee.email}
raw_invitation_token={raw_invitation_token}
/>
</CenteringBox>
);
}}
</Query>
);
}
}
12 changes: 12 additions & 0 deletions react/pages/authentication/AcceptInvitationPage/queries/invitee.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import gql from 'graphql-tag';

import inviteeFragment from 'react/pages/authentication/AcceptInvitationPage/fragments/invitee';

export default gql`
query Invitee($invitation_token: String!) {
invitee(invitation_token: $invitation_token) {
...Invitee
}
}
${inviteeFragment}
`;

0 comments on commit 0c18bb1

Please sign in to comment.