Skip to content

Commit

Permalink
Merge pull request #13 from ohpyupi/develop
Browse files Browse the repository at this point in the history
Release v1.2.0
  • Loading branch information
paneraallday authored Jul 23, 2020
2 parents a5f34dd + 85bbb82 commit 3cbf370
Show file tree
Hide file tree
Showing 50 changed files with 1,193 additions and 250 deletions.
55 changes: 37 additions & 18 deletions app/apollo-client.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,43 @@
import _ from 'lodash';
import { ApolloClient } from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { HttpLink } from 'apollo-link-http';
import { updateNotification } from './queries/notification';
import { setContext } from 'apollo-link-context';
import { updateNotification } from './graphql/notification';
import { typeDefs } from './graphql/typeDefs';
import { updateIdToken } from './graphql/auth';
import { getLocalStorage } from './lib/localStorage';

export const apolloClient = new ApolloClient({
cache: new InMemoryCache(),
resolvers: {
Mutation: {
updateNotification,
},
},
link: new HttpLink({ uri: '/graphql' }),
});
const localStorage = getLocalStorage();

apolloClient.writeData({
data: {
notification: {
__typename: 'Notification',
type: '',
message: '',
export const getApolloClient = async () => {
const httpLink = new HttpLink({ uri: '/graphql' });
const authLink = setContext((req, { headers }) => ({
headers: {
...headers,
authorization: `Bearer ${_.get(getLocalStorage(), 'idToken')}`,
},
}));
const apolloClient = new ApolloClient({
cache: new InMemoryCache(),
typeDefs,
resolvers: {
Mutation: {
updateNotification,
updateIdToken,
},
},
link: authLink.concat(httpLink),
});
apolloClient.writeData({
data: {
notification: {
__typename: 'Notification',
type: '',
message: '',
},
idToken: _.get(localStorage, 'idToken', ''),
},
},
});
});
return apolloClient;
};
73 changes: 47 additions & 26 deletions app/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,33 +13,54 @@ import {
Switch,
} from 'react-router-dom';
import { ApolloProvider } from '@apollo/react-hooks';
import { apolloClient } from './apollo-client';
import { getApolloClient } from './apollo-client';
import { AppInfo } from './containers/app-info';
import { Login } from './containers/auth/login';
import { Signup } from './containers/auth/signup';
import { Confirm } from './containers/auth/confirm';
import { ConfirmEmail } from './containers/auth/confirmEmail';
import { Profile } from './containers/users/profile';
import { Challenge } from './containers/auth/challenge';
import { ConfirmDevice } from './containers/auth/confirmDevice';
import { loadKeystrokeDna } from './services/keystroke-dna';

ReactDOM.render(
<ApolloProvider client={apolloClient}>
<Router basename="/">
<Switch>
<Route path="/app-info">
<AppInfo/>
</Route>
<Route path="/confirm/:token">
<Confirm/>
</Route>
<Route path="/signup">
<Signup/>
</Route>
<Route path="/login">
<Login/>
</Route>
<Route path="/">
<Login/>
</Route>
</Switch>
</Router>
</ApolloProvider>,
document.getElementById('app'),
);
const bootstrap = async () => {
await loadKeystrokeDna({
appId: process.env.KEYSTROKE_DNA_APP_ID,
});
const apolloClient = await getApolloClient();
ReactDOM.render(
<ApolloProvider client={apolloClient}>
<Router basename="/">
<Switch>
<Route path="/app-info">
<AppInfo />
</Route>
<Route path="/user/profile">
<Profile />
</Route>
<Route path="/confirm/email/:code">
<ConfirmEmail />
</Route>
<Route path="/confirm/device/:code">
<ConfirmDevice />
</Route>
<Route path="/signup">
<Signup />
</Route>
<Route path="/challenge">
<Challenge/>
</Route>
<Route path="/login">
<Login />
</Route>
<Route path="/">
<Login />
</Route>
</Switch>
</Router>
</ApolloProvider>,
document.getElementById('app'),
);
};

bootstrap();
7 changes: 4 additions & 3 deletions app/components/button/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,16 @@ import React from 'react';
import PropTypes from 'prop-types';

export const Button = ({
placeholder, type, className,
}) => <div className={`field ${className}`}>
placeholder, type, className, onClick,
}) => <div className='field'>
<p className="control">
<button type={type} className="button is-success">{placeholder}</button>
<button type={type} className={className || 'button is-success'} onClick={onClick}>{placeholder}</button>
</p>
</div>;

Button.propTypes = {
placeholder: PropTypes.string,
type: PropTypes.string,
className: PropTypes.string,
onClick: PropTypes.func,
};
6 changes: 4 additions & 2 deletions app/components/input/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import React from 'react';
import PropTypes from 'prop-types';

export const Input = ({
label, placeholder, type, value, onChange, attrs, className,
label, placeholder, type, value, onChange, attrs, className, disabled, readOnly,
}) => <div className={`field ${className}`}>
<label className="label">{label}</label>
<div className="control">
<input className="input" type={type} placeholder={placeholder} value={value} onChange={onChange} {...attrs} />
<input className="input" type={type} placeholder={placeholder} value={value || ''} onChange={onChange} {...attrs} disabled={disabled} readOnly={readOnly} />
</div>
</div>;

Expand All @@ -18,4 +18,6 @@ Input.propTypes = {
onChange: PropTypes.func,
attrs: PropTypes.any,
className: PropTypes.string,
disabled: PropTypes.bool,
readOnly: PropTypes.bool,
};
2 changes: 1 addition & 1 deletion app/containers/app-info/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import _ from 'lodash';
import React from 'react';
import { useQuery } from '@apollo/react-hooks';
import { ProfileCard } from '../../components/profile-card';
import { GET_APP_INFO } from './gql';
import { GET_APP_INFO } from '../../graphql/app-info';
import { Spinner } from '../../components/spinner';

export const AppInfo = () => {
Expand Down
108 changes: 108 additions & 0 deletions app/containers/auth/challenge.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import _ from 'lodash';
import React, { useState, useEffect } from 'react';
import { Redirect } from 'react-router-dom';
import { useMutation, useQuery } from '@apollo/react-hooks';
import { Input } from '../../components/input';
import { Button } from '../../components/button';
import { Notification } from '../../components/notification';
import { parseJwtSafe } from '../../lib/utils';
import {
GET_ID_TOKEN,
RESEND_CHALLENGE,
SOLVE_CHALLENGE,
UPDATED_ID_TOKEN,
} from '../../graphql/auth';
import { AUTH_STATUS } from '../../lib/constants';
import { GET_NOTIFICATION, UPDATE_NOTIFICATION } from '../../graphql/notification';

export const Challenge = () => {
const [redirectTo, setRedirectTo] = useState('');
const [challengeCode, setChallengeCode] = useState('');
const { data: { idToken } } = useQuery(GET_ID_TOKEN);
const { data: { notification } } = useQuery(GET_NOTIFICATION);
const [updateNotification] = useMutation(UPDATE_NOTIFICATION);
const [updateIdToken] = useMutation(UPDATED_ID_TOKEN);
const [resendChallenge] = useMutation(RESEND_CHALLENGE, {
onCompleted(result) {
const message = _.get(result, 'resendChallenge.message');
return updateNotification({
type: 'success',
message,
});
},
onError(err) {
const message = _.get(err, 'message', 'Something went wrong');
return updateNotification({
type: 'warning',
message,
});
},
});
const [solveChallenge] = useMutation(SOLVE_CHALLENGE, {
onCompleted(result) {
const newIdToken = _.get(result, 'solveChallenge.idToken');
return updateIdToken({
variables: {
idToken: newIdToken,
},
}).then(() => setRedirectTo('/user/profile'));
},
onError(err) {
const message = _.get(err, 'message', 'Something went wrong');
return updateNotification({
type: 'warning',
message,
});
},
});
const idTokenPayload = parseJwtSafe(idToken);
const email = _.get(idTokenPayload, 'email');
const status = _.get(idTokenPayload, 'status');
const handleCloseErrorMessage = (e) => {
e.preventDefault();
updateNotification({});
};
const handleChallengeCodeChange = (e) => {
e.preventDefault();
setChallengeCode(_.get(e, 'target.value', ''));
};
const handleClickResend = (e) => {
e.preventDefault();
resendChallenge();
};
const handleClickSubmit = (e) => {
e.preventDefault();
solveChallenge({
variables: {
code: challengeCode,
},
});
};
useEffect(() => {
if (!email) {
setRedirectTo('/login');
}
if (status === AUTH_STATUS.LOGGED_IN) {
setRedirectTo('/user/profile');
}
}, []);
if (redirectTo) {
return <Redirect to={redirectTo}/>;
}
return (
<section id="challenge" className="section">
<div className="container">
<h1 className="title">Solve Challenge</h1>
<div className="content">
<form>
{notification.message ? <Notification className="is-warning" onClose={handleCloseErrorMessage} message={notification.message} /> : null}
<Input label="Email" type="text" className="email-field" placeholder="Email" value={email} disabled={true} readOnly={true}/>
<Input label="Challenge Code" type="text" className="challengecode-field" value={challengeCode} placeholder="Challenge Code" onChange={handleChallengeCodeChange}/>
<Button type="button" className="button is-link" placeholder="Submit" onClick={handleClickSubmit}/>
<Button type="button" placeholder="Resend" onClick={handleClickResend}/>
</form>
</div>
</div>
</section>
);
};
59 changes: 59 additions & 0 deletions app/containers/auth/confirmDevice.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import _ from 'lodash';
import React, { useEffect } from 'react';
import { withRouter } from 'react-router-dom';
import { useMutation, useQuery } from '@apollo/react-hooks';
import { GET_NOTIFICATION, UPDATE_NOTIFICATION } from '../../graphql/notification';
import { CONFIRM_DEVICE } from '../../graphql/auth';
import { Spinner } from '../../components/spinner';
import { Notification } from '../../components/notification';

export const ConfirmDevice = withRouter(({
match: { params: { code } },
}) => {
const { data: { notification } } = useQuery(GET_NOTIFICATION);
const [updateNotification] = useMutation(UPDATE_NOTIFICATION);
const [confirmDeviceMutation, { called, loading }] = useMutation(CONFIRM_DEVICE, {
onCompleted(result) {
updateNotification({
variables: {
type: 'success',
message: _.get(result, 'confirmDevice.message'),
},
});
},
onError() {
updateNotification({
variables: {
type: 'warning',
message: 'Invalid token',
},
});
},
});
useEffect(() => {
confirmDeviceMutation({
variables: {
code,
},
});
}, []);
const handleCloseErrorMessage = (e) => {
e.preventDefault();
updateNotification({});
};
if (!called || loading) {
return <Spinner/>;
}
return (
<section id="confirm-device" className="section">
<div className="container">
<h1 className="title">Confirm your device</h1>
<div className="content">
<form>
{notification.message ? <Notification className={`is-${notification.type || 'warning'}`} onClose={handleCloseErrorMessage} message={notification.message} /> : null}
</form>
</div>
</div>
</section>
);
});
Loading

0 comments on commit 3cbf370

Please sign in to comment.