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

Change request #22

Merged
merged 1 commit into from
Apr 16, 2021
Merged
Show file tree
Hide file tree
Changes from all 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
4 changes: 4 additions & 0 deletions docker/production.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ services:
- IMAGE_URL=${IMAGE_URL}
- AUTH_URL=${AUTH_URL}
- POSTS_URL=${POSTS_URL}
- USERS_URL=${USERS_URL}
- FEED_DISCOVERY_URL=${FEED_DISCOVERY_URL}
container_name: 'telescope'
restart: unless-stopped
environment:
Expand All @@ -47,6 +49,8 @@ services:
- POSTS_URL
- API_URL
- WEB_URL
- USERS_URL
- FEED_DISCOVERY_URL
- LOG_LEVEL
- FEED_URL
- FEED_URL_INTERVAL_MS
Expand Down
111 changes: 65 additions & 46 deletions src/web/src/components/SignUp/Forms/GitHubAccount.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
/* eslint-disable camelcase */
import { useEffect, useState } from 'react';
import { useEffect, useRef, useState } from 'react';
import Button from '@material-ui/core/Button';
import { createStyles, makeStyles, Theme } from '@material-ui/core';
import { connect } from 'formik';
import useSWR from 'swr';

import { SignUpForm } from '../../../interfaces';
import formModels from '../Schema/FormModel';
import { TextInput, CheckBoxInput } from '../FormFields';
import PostAvatar from '../../Posts/PostAvatar';

const { githubUsername, github: githubModel, githubOwnership } = formModels;
const { githubUsername, github, githubOwnership } = formModels;

const useStyles = makeStyles((theme: Theme) =>
createStyles({
Expand Down Expand Up @@ -56,7 +55,7 @@ const useStyles = makeStyles((theme: Theme) =>
inputsContainer: {
width: '100%',
display: 'grid',
gridTemplateColumns: '100%',
gridTemplateColumns: '80% 20%',
'& .MuiFormHelperText-root': {
fontSize: '0.9em',
color: 'black',
Expand All @@ -65,6 +64,22 @@ const useStyles = makeStyles((theme: Theme) =>
color: 'black',
},
},
button: {
height: '35px',
width: '50%',
alignSelf: 'center',
fontSize: '0.8em',
background: '#121D59',
color: '#A0D1FB',
marginLeft: '5%',
'&:hover': {
color: 'black',
border: '1px solid #121D59',
},
'&.Mui-disabled': {
backgroundColor: 'inherit',
},
},
avatarPreview: {
textAlign: 'center',
justifyItems: 'center',
Expand All @@ -89,52 +104,51 @@ const gitHubApiUrl = 'https://api.github.com/users';
const GitHubAccount = connect<{}, SignUpForm>((props) => {
const classes = useStyles();
const { values, setFieldValue } = props.formik;
const [username, setUsername] = useState(values.githubUsername);
const [inputTimeout, setInputTimeout] = useState(setTimeout(() => {}, 0));
const [validating, setValidating] = useState(false);
const [error, setError] = useState('');
const controllerRef = useRef<AbortController | null>();

const { data: github, error } = useSWR(
values.githubUsername ? `${gitHubApiUrl}/${values.githubUsername}` : null,
async (u) => {
const validateGit = async () => {
if (values.githubUsername) {
setValidating(true);
if (controllerRef.current) {
controllerRef.current.abort();
}
controllerRef.current = new AbortController();
try {
const response = await fetch(u);
const response = await fetch(`${gitHubApiUrl}/${values.githubUsername}`, {
signal: controllerRef.current?.signal,
});
if (!response.ok) {
throw new Error(response.statusText);
}
return response.json();
const res = await response.json();

setFieldValue('github', {
username: res.login,
avatarUrl: res.avatar_url,
});
setError('');
} catch (err) {
throw err;
console.error(err, 'Unable to get GitHub profile');

setError('Unable to get GitHub profile');
setFieldValue('github', {}, true);
} finally {
setValidating(false);
controllerRef.current = null;
}
} else {
setError('');
setFieldValue('github', {}, true);
}
);

const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setUsername(e.target.value);
clearTimeout(inputTimeout);

// Update githubUsername 1000ms after input change
setInputTimeout(
setTimeout(() => {
setFieldValue('githubUsername', e.target.value);
}, 1000)
);
};

useEffect(() => {
if (error) {
setFieldValue('github', {}, true);
}

if (github) {
setFieldValue(
'github',
{
username: github.login,
avatarUrl: github.avatar_url,
},
true
);
}
}, [github, error, setFieldValue]);
return () => {
controllerRef.current?.abort();
};
}, []);

return (
<div className={classes.root}>
Expand All @@ -147,15 +161,20 @@ const GitHubAccount = connect<{}, SignUpForm>((props) => {
label={githubUsername.label}
name={githubUsername.name}
error={!!error}
helperText={!!error && githubModel.invalidErrorMsg}
onChange={handleInputChange}
value={username}
helperText={error || github.invalidErrorMsg}
/>
<Button className={classes.button} onClick={validateGit} disabled={validating}>
Get profile
</Button>
</div>
{!error && github && (
{!error && (
<div className={classes.avatarPreview}>
<PostAvatar name={github.login} blog={github.avatar_url} img={github.avatar_url} />
<h2 className={classes.username}>{github.login}</h2>
<PostAvatar
name={values.github.username || values.displayName}
blog={values.github.avatarUrl}
img={values.github?.avatarUrl}
/>
<h2 className={classes.username}>{values.github.username}</h2>
</div>
)}
</div>
Expand Down
29 changes: 14 additions & 15 deletions src/web/src/components/SignUp/Forms/RSSFeeds.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useState, useEffect } from 'react';
import { useState, useEffect, useRef } from 'react';
import { Button, createStyles, makeStyles, Theme } from '@material-ui/core';
import { connect } from 'formik';
import FormControl from '@material-ui/core/FormControl';
Expand Down Expand Up @@ -142,19 +142,18 @@ const RSSFeeds = connect<{}, SignUpForm>((props) => {
const [feedUrls, setFeedUrls] = useState<Array<string>>([]);
const [blogUrlError, setBlogUrlError] = useState('');
const [validating, setValidating] = useState(false);

// A controller to cancel undesired requests
const [controller, setController] = useState(new AbortController());
const controllerRef = useRef<AbortController | null>();

const validateBlog = async () => {
if (!errors.blogUrl && feedDiscoveryServiceUrl) {
if (!errors.blogUrl) {
try {
setValidating(true);
const abortController = new AbortController();
setController(abortController);
const signal = abortController.signal;
const response = await fetch(feedDiscoveryServiceUrl, {
signal,
if (controllerRef.current) {
controllerRef.current.abort();
}
controllerRef.current = new AbortController();
const response = await fetch(`${feedDiscoveryServiceUrl}`, {
signal: controllerRef.current?.signal,
method: 'post',
headers: {
Authorization: `bearer ${token}`,
Expand All @@ -164,19 +163,20 @@ const RSSFeeds = connect<{}, SignUpForm>((props) => {
blogUrl: values.blogUrl,
}),
});
console.log(values.blogUrl);
if (!response.ok) {
throw new Error(response.statusText);
}
const res = await response.json();

setBlogUrlError('');
setFeedUrls(res.feedUrls);
setValidating(false);
} catch (err) {
console.error(err, 'Unable to discover feeds');
setBlogUrlError(`Unable to find RSS link at ${values.blogUrl}`);

setBlogUrlError('Unable to discover feeds');
setFeedUrls([]);
} finally {
controllerRef.current = null;
setValidating(false);
}
} else {
Expand All @@ -197,9 +197,8 @@ const RSSFeeds = connect<{}, SignUpForm>((props) => {
validateBlog();
}

// Abort feed-discovery on component unmounting
return () => {
controller.abort();
controllerRef.current?.abort();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
Expand Down
3 changes: 0 additions & 3 deletions src/web/src/components/SignUp/Schema/FormModel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,16 @@ export default {
displayName: {
name: 'displayName',
label: 'Display Name',
invalidErrorMsg: 'Make sure display name contains 2-16 characters',
},
firstName: {
name: 'firstName',
label: 'First name',
requiredErrorMsg: 'First name is required',
invalidErrorMsg: 'Make sure first name contains 2-16 characters',
},
lastName: {
name: 'lastName',
label: 'Last name',
requiredErrorMsg: 'Last name is required',
invalidErrorMsg: 'Make sure last name contains 2-16 characters',
},
email: {
name: 'email',
Expand Down
23 changes: 5 additions & 18 deletions src/web/src/components/SignUp/Schema/FormSchema.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,28 +14,15 @@ const {
blogOwnership,
} = formModels;

const validateLength = (min: number, max: number) => (val: string | undefined): boolean =>
!!val && val.length >= min && val.length <= max;

const validateCheckBox = (val: boolean | undefined) => !!val;

// Each signup step has one validation schema
export default [
// First step has no validation logic
Yup.object().shape({}),

Yup.object().shape({
[firstName.name]: Yup.string()
.required(`${firstName.requiredErrorMsg}`)
.test('len', firstName.invalidErrorMsg, validateLength(2, 16)),
[lastName.name]: Yup.string()
.required(`${lastName.requiredErrorMsg}`)
.test('len', lastName.invalidErrorMsg, validateLength(2, 16)),
[displayName.name]: Yup.string().test(
'len',
displayName.invalidErrorMsg,
validateLength(2, 16)
),
[firstName.name]: Yup.string().required(`${firstName.requiredErrorMsg}`),
[lastName.name]: Yup.string().required(`${lastName.requiredErrorMsg}`),
[displayName.name]: Yup.string(),
}),

Yup.object().shape({
Expand All @@ -49,7 +36,7 @@ export default [
[githubOwnership.name]: Yup.boolean().test(
'agreed',
githubOwnership.invalidErrorMsg,
validateCheckBox
(val) => !!val
),
}),

Expand All @@ -59,7 +46,7 @@ export default [
[blogOwnership.name]: Yup.boolean().test(
'agreed',
blogOwnership.invalidErrorMsg,
validateCheckBox
(val) => !!val
),
}),

Expand Down
12 changes: 5 additions & 7 deletions src/web/src/pages/signup.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { createStyles, makeStyles, Theme } from '@material-ui/core';
import { useState, useEffect } from 'react';

import Button from '@material-ui/core/Button';
import { Formik, Form, FormikHelpers } from 'formik';
import Button from '@material-ui/core/Button';

import useAuth from '../hooks/use-auth';
import Overview from '../components/SignUp/Forms/Overview';
Expand All @@ -15,7 +14,7 @@ import DynamicImage from '../components/DynamicImage';
import { SignUpForm } from '../interfaces';
import formModels from '../components/SignUp/Schema/FormModel';
import formSchema from '../components/SignUp/Schema/FormSchema';
import { usersServiceUrl, webUrl } from '../config';
import { usersServiceUrl } from '../config';

const {
firstName,
Expand Down Expand Up @@ -136,7 +135,7 @@ const SignUpPage = () => {
const [activeStep, setActiveStep] = useState<number>(0);
const currentSchema = formSchema[activeStep];
const { user, token, login } = useAuth();
const [loggedIn, setLoggedIn] = useState(false);
const [loggedIn, setLoggedIn] = useState(!!user);

const handleNext = () => {
setActiveStep(activeStep + 1);
Expand Down Expand Up @@ -164,6 +163,7 @@ const SignUpPage = () => {
github,
feeds,
};
// TODO Update register URL
const response = await fetch(`${usersServiceUrl}/${user?.id}`, {
method: 'post',
headers: {
Expand All @@ -174,14 +174,12 @@ const SignUpPage = () => {
});

if (!response.ok) {
alert('Unable to create account.');
throw new Error('Unable to post - create account');
throw new Error(response.statusText);
}
login();
return;
} catch (err) {
console.error(err, 'Unable to Post');
window.location.href = `${webUrl}`;
}
} else {
handleNext();
Expand Down