aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorEugene Sokolov <eug-vs@keemail.me>2020-03-21 17:59:48 +0300
committerGitHub <noreply@github.com>2020-03-21 17:59:48 +0300
commite89a7a79622d25561dc80a8489ae1f6022aebd73 (patch)
treee115938b26ee6019b4c349f52bd98fc34a4575e9 /src
parentfe750a9aadfb451b9537bb3cdd79581ef5120c07 (diff)
parent7f8ab6802c68c46f988ef012fdc88b09e43a9e54 (diff)
downloadchrono-cube-ui-e89a7a79622d25561dc80a8489ae1f6022aebd73.tar.gz
Merge pull request #48 from eug-vs/typescript
Migrate project to Typescript
Diffstat (limited to 'src')
-rw-r--r--src/components/Loading/Loading.tsx (renamed from src/components/Loading/Loading.js)2
-rw-r--r--src/components/SolutionCard/SolutionCard.tsx (renamed from src/components/SolutionCard/SolutionCard.js)22
-rw-r--r--src/index.tsx (renamed from src/index.js)19
-rw-r--r--src/pages/Contribute/Contribute.tsx (renamed from src/pages/Contribute/Contribute.js)4
-rw-r--r--src/pages/Profile/Profile.tsx (renamed from src/pages/Profile/Profile.js)19
-rw-r--r--src/pages/Profile/Registration.tsx (renamed from src/pages/Profile/Registration/Registration.js)21
-rw-r--r--src/pages/Scoreboard/Scoreboard.tsx (renamed from src/pages/Scoreboard/Scoreboard.js)12
-rw-r--r--src/pages/Timer/Timer.tsx (renamed from src/pages/Timer/Timer.js)26
-rw-r--r--src/pages/Timer/TimerButton.tsx (renamed from src/pages/Timer/TimerButton/TimerButton.js)30
-rw-r--r--src/react-app-env.d.ts1
-rw-r--r--src/requests.js24
-rw-r--r--src/requests.ts11
-rw-r--r--src/types.d.ts17
13 files changed, 127 insertions, 81 deletions
diff --git a/src/components/Loading/Loading.js b/src/components/Loading/Loading.tsx
index e8793cc..a784be1 100644
--- a/src/components/Loading/Loading.js
+++ b/src/components/Loading/Loading.tsx
@@ -16,7 +16,7 @@ const useStyles = makeStyles(theme => ({
},
}));
-const Loading = () => {
+const Loading: React.FC = () => {
const classes = useStyles();
return (
diff --git a/src/components/SolutionCard/SolutionCard.js b/src/components/SolutionCard/SolutionCard.tsx
index fdd4bdf..5adbff9 100644
--- a/src/components/SolutionCard/SolutionCard.js
+++ b/src/components/SolutionCard/SolutionCard.tsx
@@ -11,6 +11,7 @@ import {
Menu,
MenuItem,
} from '@material-ui/core';
+import { Solution } from '../../types';
import { makeStyles } from '@material-ui/core/styles';
import TimerIcon from '@material-ui/icons/Timer';
@@ -44,22 +45,29 @@ const useStyles = makeStyles(theme => ({
},
}));
-const SolutionCard = ({ data, removeThisCard }) => {
+
+interface PropTypes {
+ data: Solution;
+ removeThisCard: (id: number) => void;
+}
+
+
+const SolutionCard: React.FC<PropTypes> = ({ data, removeThisCard }) => {
const classes = useStyles();
- const [anchorEl, setAnchorEl] = useState(null);
+ const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);
const author = data.author? data.author.username : 'anonymous';
const date = new Date(data.date);
- const handleOpenMenu = event => {
+ const handleOpenMenu = (event: React.MouseEvent<HTMLButtonElement>): void => {
setAnchorEl(event.currentTarget);
};
- const handleClose = () => {
+ const handleClose = (): void => {
setAnchorEl(null);
};
- const handleDelete = () => {
+ const handleDelete = (): void => {
del(`solutions/${data.id}/`).then(() => {
removeThisCard(data.id);
});
@@ -77,11 +85,11 @@ const SolutionCard = ({ data, removeThisCard }) => {
}
title={author}
subheader={date.toLocaleString('default', DATE_FORMAT)}
- action={(
+ action={
<IconButton onClick={handleOpenMenu}>
<MoreVertIcon />
</IconButton>
- )}
+ }
/>
<Menu
anchorEl={anchorEl}
diff --git a/src/index.js b/src/index.tsx
index 49c66e7..360ca89 100644
--- a/src/index.js
+++ b/src/index.tsx
@@ -5,6 +5,7 @@ import {
BenzinThemeProvider,
Header,
} from 'react-benzin';
+import { User, Solution } from './types';
import 'typeface-roboto';
@@ -21,11 +22,10 @@ import GitHubIcon from '@material-ui/icons/GitHub';
import { get } from './requests';
-const App = () => {
-
- const [page, setPage] = useState('app');
- const [user, setUser] = useState({ username: 'anonymous', id: null });
- const [recentSolutions, setRecentSolutions] = useState([]);
+const App: React.FC = () => {
+ const [page, setPage] = useState<string>('app');
+ const [user, setUser] = useState<User>({ username: 'anonymous', id: null });
+ const [recentSolutions, setRecentSolutions] = useState<Solution[]>([]);
const headerContents = {
app: (<TimerIcon />),
@@ -35,15 +35,15 @@ const App = () => {
};
useEffect(() => {
- const userId = +localStorage.getItem('userId');
+ const userId = localStorage.getItem('userId');
if (userId) {
get('users/').then(response => {
- setUser(response.data.filter(user => user.id === +userId)[0]);
+ setUser(response.data.filter((user: User) => user.id === +userId)[0]);
});
}
}, []);
- const Page = ({ page }) => {
+ const Page: React.FC<{ page: string }> = ({ page }) => {
switch (page) {
case 'app':
return (
@@ -73,7 +73,8 @@ const App = () => {
<BenzinThemeProvider>
<Header
logo={{
- title: 'ChronoCube'
+ title: 'ChronoCube',
+ icon: null
}}
contents={headerContents}
page={page}
diff --git a/src/pages/Contribute/Contribute.js b/src/pages/Contribute/Contribute.tsx
index aa1c3f7..4c37fb9 100644
--- a/src/pages/Contribute/Contribute.js
+++ b/src/pages/Contribute/Contribute.tsx
@@ -17,6 +17,7 @@ import NewReleasesIcon from '@material-ui/icons/NewReleases';
import { Window, ContentSection } from 'react-benzin';
+import developers from '../../developers.json';
const useStyles = makeStyles(theme => ({
mono: {
@@ -31,10 +32,9 @@ const useStyles = makeStyles(theme => ({
}));
-const developers = require('../../developers.json');
-const Contribute = () => {
+const Contribute: React.FC = () => {
const classes = useStyles();
return (
diff --git a/src/pages/Profile/Profile.js b/src/pages/Profile/Profile.tsx
index 65c3734..83acb30 100644
--- a/src/pages/Profile/Profile.js
+++ b/src/pages/Profile/Profile.tsx
@@ -5,12 +5,13 @@ import {
makeStyles,
} from '@material-ui/core';
-import Registration from './Registration/Registration';
+import Registration from './Registration';
import {
Window,
ContentSection,
SmartList,
} from 'react-benzin';
+import { User, Solution, RenderPropTypes } from '../../types';
import SolutionCard from '../../components/SolutionCard/SolutionCard';
@@ -27,12 +28,18 @@ const useStyles = makeStyles(theme => ({
}));
-const Profile = ({ user, setUser }) => {
+interface PropTypes {
+ user: User;
+ setUser: (user: User) => void;
+}
+
+
+const Profile: React.FC<PropTypes> = ({ user, setUser }) => {
const classes = useStyles();
- const [profileSolutions, setProfileSolutions] = useState([]);
+ const [profileSolutions, setProfileSolutions] = useState<Solution[]>([]);
- const handleLogout = () => {
+ const handleLogout = (): void => {
setUser({ username: 'anonymous', id: null });
localStorage.clear();
};
@@ -43,11 +50,11 @@ const Profile = ({ user, setUser }) => {
});
}, [user]);
- const removeSolution = (id) => {
+ const removeSolution = (id: number): void => {
setProfileSolutions(profileSolutions.filter((solution => solution.id !== id)));
};
- const renderItem = ({ index, style }) => {
+ const renderItem: React.FC<RenderPropTypes> = ({ index, style }) => {
return (
<div style={style} className={classes.cell}>
<SolutionCard data={profileSolutions[index]} removeThisCard={removeSolution} />
diff --git a/src/pages/Profile/Registration/Registration.js b/src/pages/Profile/Registration.tsx
index b2d5503..a5e0f3e 100644
--- a/src/pages/Profile/Registration/Registration.js
+++ b/src/pages/Profile/Registration.tsx
@@ -7,25 +7,30 @@ import {
FormControlLabel,
Grid,
} from '@material-ui/core';
+import { User } from '../../types';
import { ContentSection } from 'react-benzin';
-import { get, post } from '../../../requests';
+import { get, post } from '../../requests';
-const Registration = ({ setUser }) => {
+interface PropTypes {
+ setUser: (user: User) => void;
+}
- const [username, setUsername] = useState('');
- const [isRememberMe, setIsRememberMe] = useState(false);
+const Registration: React.FC<PropTypes> = ({ setUser }) => {
- const handleChange = (event) => {
+ const [username, setUsername] = useState<string>('');
+ const [isRememberMe, setIsRememberMe] = useState<boolean>(false);
+
+ const handleChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
setUsername(event.target.value);
};
- const handleCheck = (event) => {
+ const handleCheck = (event: React.ChangeEvent<HTMLInputElement>): void => {
setIsRememberMe(event.target.checked);
};
- const handleSubmit = () => {
+ const handleSubmit = (): void => {
if (username !== '') {
post('users/', { username })
.then(response => {
@@ -37,7 +42,7 @@ const Registration = ({ setUser }) => {
})
.catch(err => {
get('users/').then(response => {
- const user = response.data.filter(user => user.username === username)[0];
+ const user = response.data.filter((user: User) => user.username === username)[0];
setUser(user);
if (isRememberMe) {
localStorage.setItem('userId', user.id);
diff --git a/src/pages/Scoreboard/Scoreboard.js b/src/pages/Scoreboard/Scoreboard.tsx
index 47c0899..e4185bd 100644
--- a/src/pages/Scoreboard/Scoreboard.js
+++ b/src/pages/Scoreboard/Scoreboard.tsx
@@ -3,6 +3,7 @@ import React, { useEffect, useState } from 'react';
import { makeStyles } from '@material-ui/core/styles';
import { Window, SmartList } from 'react-benzin';
+import { Solution, RenderPropTypes } from '../../types';
import SolutionCard from '../../components/SolutionCard/SolutionCard';
import Loading from '../../components/Loading/Loading';
@@ -22,17 +23,18 @@ const useStyles = makeStyles(theme => ({
}
}));
-const Scoreboard = () => {
+
+const Scoreboard: React.FC = () => {
const classes = useStyles();
- const [solutions, setSolutions] = useState([]);
+ const [solutions, setSolutions] = useState<Solution[]>([]);
- const updateSolutions = () => {
+ const updateSolutions = (): void => {
get('scoreboard/').then(response => {
setSolutions(response.data);
});
};
- const removeSolution = id => {
+ const removeSolution = (id: number): void => {
updateSolutions();
};
@@ -40,7 +42,7 @@ const Scoreboard = () => {
setTimeout(updateSolutions, 300);
}, []);
- const renderItem = ({ index, style }) => {
+ const renderItem: React.FC<RenderPropTypes> = ({ index, style }) => {
return (
<div style={style} className={classes.cell}>
<SolutionCard data={solutions[index]} removeThisCard={removeSolution}/>
diff --git a/src/pages/Timer/Timer.js b/src/pages/Timer/Timer.tsx
index 6020c1b..a890815 100644
--- a/src/pages/Timer/Timer.js
+++ b/src/pages/Timer/Timer.tsx
@@ -7,8 +7,9 @@ import {
ContentSection,
SmartList,
} from 'react-benzin';
+import { User, Solution, RenderPropTypes } from '../../types';
-import TimerButton from './TimerButton/TimerButton';
+import TimerButton from './TimerButton';
import SolutionCard from '../../components/SolutionCard/SolutionCard';
import { Button, makeStyles } from '@material-ui/core';
@@ -23,29 +24,38 @@ const useStyles = makeStyles(theme => ({
},
}));
-const Timer = ({ user, recentSolutions, setRecentSolutions, setPage }) => {
+
+interface PropTypes {
+ user: User;
+ recentSolutions: Solution[];
+ setRecentSolutions: (newRecentSolutions: Solution[]) => void;
+ setPage: (newPage: string) => void;
+}
+
+
+const Timer: React.FC<PropTypes> = ({ user, recentSolutions, setRecentSolutions, setPage }) => {
const classes = useStyles();
- const registerResult = result => {
- const solution = { author_id: user.id, result };
+ const registerResult = (result: string): void => {
+ const solution = { 'author_id': user.id, result };
post('solutions/', solution).then(response => {
setRecentSolutions([response.data].concat(recentSolutions));
});
};
- const handleLearnMore = () => {
+ const handleLearnMore = (): void => {
setPage('contribute');
};
- const handleLogin = () => {
+ const handleLogin = (): void => {
setPage('profile');
};
- const removeSolution = (id) => {
+ const removeSolution = (id: number): void => {
setRecentSolutions(recentSolutions.filter((solution => solution.id !== id)));
};
- const renderItem = ({ index, style }) => {
+ const renderItem: React.FC<RenderPropTypes> = ({ index, style }) => {
const solution = recentSolutions[index];
return (
<div style={style} className={classes.cell}>
diff --git a/src/pages/Timer/TimerButton/TimerButton.js b/src/pages/Timer/TimerButton.tsx
index fdb6b7c..0a3bf38 100644
--- a/src/pages/Timer/TimerButton/TimerButton.js
+++ b/src/pages/Timer/TimerButton.tsx
@@ -12,13 +12,21 @@ const useStyles = makeStyles(theme => ({
},
}));
-const TimerButton = ({ registerResult }) => {
+
+interface PropTypes {
+ registerResult: (result: string) => void;
+}
+
+type Mode = 'idle' | 'countdown' | 'running' | 'over';
+
+
+const TimerButton: React.FC<PropTypes> = ({ registerResult }) => {
const classes = useStyles();
const SPACE = 32;
const maxCountdown = 15000;
- const [time, setTime] = useState('00:00:00');
- const [mode, setMode] = useState('idle');
+ const [time, setTime] = useState<string>('00:00:00');
+ const [mode, setMode] = useState<Mode>('idle');
useEffect(()=> {
const timestamp = Date.now();
@@ -29,14 +37,14 @@ const TimerButton = ({ registerResult }) => {
if (timeDelta <= 0) setMode('over');
setTime(convertTimeToString(timeDelta));
}, 10);
- return () => clearInterval(repeater);
+ return (): void => clearInterval(repeater);
}
if (mode === 'running') {
const repeater = setInterval(() => {
setTime(convertTimeToString(Date.now() - timestamp));
}, 10);
- return () => clearInterval(repeater);
+ return (): void => clearInterval(repeater);
}
if (mode === 'over') {
@@ -44,12 +52,12 @@ const TimerButton = ({ registerResult }) => {
}
}, [mode]);
- const handleKeyPress = event => {
+ const handleKeyPress = (event: KeyboardEvent): void => {
event.preventDefault();
if (event.keyCode === SPACE && mode === 'idle' ) setMode('countdown');
};
- const handleKeyUp = event => {
+ const handleKeyUp = (event: KeyboardEvent): void => {
if (event.keyCode === SPACE) {
if (mode === 'running') {
registerResult(time);
@@ -66,13 +74,13 @@ const TimerButton = ({ registerResult }) => {
window.addEventListener('keyup', handleKeyUp);
window.addEventListener('keypress', handleKeyPress);
- return () => {
+ return (): void => {
window.removeEventListener('keyup', handleKeyUp);
window.removeEventListener('keypress', handleKeyPress);
};
});
- const composeHelperText = () => {
+ const composeHelperText = (): string => {
switch (mode) {
case 'running': return 'Go fast!';
case 'countdown': return 'Release SPACE to begin';
@@ -81,7 +89,7 @@ const TimerButton = ({ registerResult }) => {
}
};
- const helperColor = () => {
+ const helperColor = (): 'primary' | 'secondary' | 'textSecondary' => {
switch (mode) {
case 'running': return 'primary';
case 'over': return 'secondary';
@@ -99,7 +107,7 @@ const TimerButton = ({ registerResult }) => {
);
};
-const convertTimeToString = timeDelta => {
+const convertTimeToString = (timeDelta: number): string => {
let resultTime = '';
const minute = Math.floor(timeDelta / 60000);
diff --git a/src/react-app-env.d.ts b/src/react-app-env.d.ts
new file mode 100644
index 0000000..6431bc5
--- /dev/null
+++ b/src/react-app-env.d.ts
@@ -0,0 +1 @@
+/// <reference types="react-scripts" />
diff --git a/src/requests.js b/src/requests.js
deleted file mode 100644
index ad45dcf..0000000
--- a/src/requests.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import axios from 'axios'
-
-const baseUrl = 'https://eugvs.pythonanywhere.com/';
-const baseApiUrl = baseUrl + 'api/';
-
-export const get = (url) => {
- return axios.get(
- baseApiUrl + url,
- );
-};
-
-export const post = (url, data) => {
- return axios.post(
- baseApiUrl + url,
- data
- );
-};
-
-export const del = (url, data) => {
- return axios.delete(
- baseApiUrl + url,
- data
- )
-};
diff --git a/src/requests.ts b/src/requests.ts
new file mode 100644
index 0000000..0242ed5
--- /dev/null
+++ b/src/requests.ts
@@ -0,0 +1,11 @@
+import axios, { AxiosResponse } from 'axios';
+
+const baseUrl = 'https://eugvs.pythonanywhere.com/';
+const baseApiUrl = baseUrl + 'api/';
+
+export const get = (url: string): Promise<AxiosResponse> => axios.get(baseApiUrl + url);
+
+export const del = (url: string): Promise<AxiosResponse> => axios.delete(baseApiUrl + url);
+
+export const post = (url: string, data: object): Promise<AxiosResponse> => axios.post(baseApiUrl + url, data);
+
diff --git a/src/types.d.ts b/src/types.d.ts
new file mode 100644
index 0000000..949d410
--- /dev/null
+++ b/src/types.d.ts
@@ -0,0 +1,17 @@
+export interface User {
+ username: string;
+ id: number | null;
+}
+
+export interface Solution {
+ id: number;
+ result: string;
+ date: string;
+ author: User;
+}
+
+interface RenderPropTypes {
+ index: number;
+ style: React.CSSProperties;
+}
+