aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authoreug-vs <eugene@eug-vs.xyz>2022-05-04 12:52:25 +0300
committereug-vs <eugene@eug-vs.xyz>2022-05-04 12:52:25 +0300
commitf35c1cf5480f7506442ac4c9170c5e0a1a4a8b15 (patch)
tree94fa0fa38e8a90df08933c05910bea6bed461291 /src
parent8dc803f882f31430abb600fba250fdcf3334d9bf (diff)
downloadchrono-cube-ui-f35c1cf5480f7506442ac4c9170c5e0a1a4a8b15.tar.gz
feat: remove material UI and useless functionality
Diffstat (limited to 'src')
-rw-r--r--src/components/GithubAvatar/GithubAvatar.tsx39
-rw-r--r--src/components/Loading/Loading.tsx33
-rw-r--r--src/components/SolutionCard/SolutionCard.tsx117
-rw-r--r--src/components/Timer.tsx90
-rw-r--r--src/developers.json14
-rw-r--r--src/index.tsx83
-rw-r--r--src/pages/Contribute/Contribute.tsx149
-rw-r--r--src/pages/Profile/Profile.tsx95
-rw-r--r--src/pages/Profile/Registration.tsx85
-rw-r--r--src/pages/Scoreboard/Scoreboard.tsx70
-rw-r--r--src/pages/Timer/Timer.tsx102
-rw-r--r--src/pages/Timer/TimerButton.tsx130
-rw-r--r--src/react-app-env.d.ts1
-rw-r--r--src/requests.ts11
-rw-r--r--src/types.d.ts17
-rw-r--r--src/utils/convertTimeToString.ts18
16 files changed, 111 insertions, 943 deletions
diff --git a/src/components/GithubAvatar/GithubAvatar.tsx b/src/components/GithubAvatar/GithubAvatar.tsx
deleted file mode 100644
index 32ac553..0000000
--- a/src/components/GithubAvatar/GithubAvatar.tsx
+++ /dev/null
@@ -1,39 +0,0 @@
-import React from 'react';
-
-import {
- Avatar,
- Link,
-} from '@material-ui/core';
-
-interface PropTypes {
- username: string;
-}
-
-const githubUrl = 'https://github.com/';
-const getUserGithubUrl = (username: string): string => githubUrl + username;
-
-const GithubAvatar: React.FC<PropTypes> = ({ username }) => {
- if (username === 'anonymous') return <Avatar/>;
-
- const userGithubUrl = getUserGithubUrl(username);
- const avatarUrl = userGithubUrl + '.png';
- const usernameTokens = username.split(/[ ,.\-_#@;]/g);
- const altText = (
- (usernameTokens.length > 1)?
- (usernameTokens[0][0] + usernameTokens[1][0])
- :
- usernameTokens[0][0]
- ).toUpperCase()
-
- return (
- <Link href={userGithubUrl}>
- <Avatar>
- <img src={avatarUrl} alt={altText} />
- </Avatar>
- </Link>
- )
-};
-
-export { getUserGithubUrl };
-export default GithubAvatar;
-
diff --git a/src/components/Loading/Loading.tsx b/src/components/Loading/Loading.tsx
deleted file mode 100644
index a784be1..0000000
--- a/src/components/Loading/Loading.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-import React from 'react';
-
-import {
- Card,
- CardHeader,
-} from '@material-ui/core';
-
-import { makeStyles } from '@material-ui/core/styles';
-import CircularProgress from '@material-ui/core/CircularProgress';
-
-
-const useStyles = makeStyles(theme => ({
- root: {
- padding: theme.spacing(1),
- background: theme.palette.background.elevation2,
- },
-}));
-
-const Loading: React.FC = () => {
- const classes = useStyles();
-
- return (
- <Card className={classes.root}>
- <CardHeader
- avatar={(<CircularProgress color="secondary" />)}
- title="Loading"
- subheader="Please, wait."
- />
- </Card>
- )
-};
-
-export default Loading;
diff --git a/src/components/SolutionCard/SolutionCard.tsx b/src/components/SolutionCard/SolutionCard.tsx
deleted file mode 100644
index 7ec8888..0000000
--- a/src/components/SolutionCard/SolutionCard.tsx
+++ /dev/null
@@ -1,117 +0,0 @@
-import React, { useState } from 'react';
-
-import {
- Typography,
- Card,
- CardHeader,
- CardContent,
- IconButton,
- Grid,
- Menu,
- MenuItem,
-} from '@material-ui/core';
-import { Solution } from '../../types';
-
-import { makeStyles } from '@material-ui/core/styles';
-import TimerIcon from '@material-ui/icons/Timer';
-import MoreVertIcon from '@material-ui/icons/MoreVert';
-import DeleteIcon from '@material-ui/icons/Delete';
-
-import GithubAvatar from '../GithubAvatar/GithubAvatar';
-import { del } from '../../requests';
-
-
-const DATE_FORMAT = {
- month: 'long',
- day: 'numeric',
- year: 'numeric',
- hour: '2-digit',
- minute: '2-digit',
-};
-
-const useStyles = makeStyles(theme => ({
- root: {
- padding: theme.spacing(1),
- background: theme.palette.background.elevation2,
-
- '& .MuiTypography-h3': {
- margin: theme.spacing(2),
- },
- },
- menu: {
- '& ul': {
- background: theme.palette.background.elevation3,
- }
- },
-}));
-
-
-interface PropTypes {
- data: Solution;
- removeThisCard: (id: number) => void;
-}
-
-
-const SolutionCard: React.FC<PropTypes> = ({ data, removeThisCard }) => {
- const classes = useStyles();
- const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);
-
- const author = data.author? data.author.username : 'anonymous';
- const date = new Date(data.date);
-
- const handleOpenMenu = (event: React.MouseEvent<HTMLButtonElement>): void => {
- setAnchorEl(event.currentTarget);
- };
-
- const handleClose = (): void => {
- setAnchorEl(null);
- };
-
- const handleDelete = (): void => {
- del(`solutions/${data.id}/`).then(() => {
- removeThisCard(data.id);
- });
- handleClose();
- };
-
- return (
- <Card className={classes.root}>
- <CardHeader
- avatar={<GithubAvatar username={author} />}
- title={author}
- subheader={date.toLocaleString('default', DATE_FORMAT)}
- action={
- <IconButton onClick={handleOpenMenu}>
- <MoreVertIcon />
- </IconButton>
- }
- />
- <Menu
- anchorEl={anchorEl}
- open={Boolean(anchorEl)}
- keepMounted
- onClose={handleClose}
- className={classes.menu}
- >
- <MenuItem onClick={handleDelete}>
- <DeleteIcon />
- Delete
- </MenuItem>
- </Menu>
- <CardContent>
- <Grid container direction="row" justify="center" alignItems="center">
- <Grid item>
- <TimerIcon/>
- </Grid>
- <Grid item>
- <Typography variant="h3" color="primary">
- { data.result }
- </Typography>
- </Grid>
- </Grid>
- </CardContent>
- </Card>
- )
-};
-
-export default SolutionCard;
diff --git a/src/components/Timer.tsx b/src/components/Timer.tsx
new file mode 100644
index 0000000..c9d7adf
--- /dev/null
+++ b/src/components/Timer.tsx
@@ -0,0 +1,90 @@
+import React, { useState, useEffect } from 'react';
+import convertTimeToString from '../utils/convertTimeToString';
+
+enum Mode {
+ 'idle',
+ 'countdown',
+ 'running' ,
+ 'over',
+};
+
+const helperText = {
+ [Mode.idle]: 'Hold SPACE to start countdown',
+ [Mode.countdown]: 'Release SPACE to begin',
+ [Mode.running]: 'Go fast!',
+ [Mode.over]: 'You are too late!',
+};
+
+// TODO: add to props
+const registerResult = (result: string): void => {
+ console.log(result)
+};
+
+const Timer: React.FC = () => {
+ const KEY_CODE = 32; // Space key
+ const maxCountdown = 15000;
+
+ const [mode, setMode] = useState<Mode>(Mode.idle);
+ const [displayTime, setDisplayTime] = useState<string>('00:00:00');
+
+ useEffect(() => {
+ const timestamp = Date.now();
+
+ if (mode === Mode.countdown) {
+ const repeater = setInterval(() => {
+ const timeDelta = maxCountdown - (Date.now() - timestamp);
+ if (timeDelta <= 0) setMode(Mode.over);
+ setDisplayTime(convertTimeToString(timeDelta));
+ }, 10);
+ return (): void => clearInterval(repeater);
+ }
+
+ if (mode === Mode.running) {
+ const repeater = setInterval(() => {
+ setDisplayTime(convertTimeToString(Date.now() - timestamp));
+ }, 10);
+ return (): void => clearInterval(repeater);
+ }
+
+ if (mode === Mode.over) {
+ setDisplayTime('00:00:00');
+ }
+ }, [mode]);
+
+ const handleKeyPress = (event: KeyboardEvent): void => {
+ event.preventDefault();
+ if (event.keyCode === KEY_CODE && mode === Mode.idle) setMode(Mode.countdown);
+ };
+
+ const handleKeyUp = (event: KeyboardEvent): void => {
+ if (event.keyCode === KEY_CODE) {
+ if (mode === Mode.running) {
+ registerResult(displayTime);
+ setMode(Mode.idle);
+ } else if (mode === Mode.over) {
+ setMode(Mode.idle);
+ } else {
+ setMode(Mode.running);
+ }
+ }
+ };
+
+ useEffect(() => {
+ window.addEventListener('keyup', handleKeyUp);
+ window.addEventListener('keypress', handleKeyPress);
+
+ return () => {
+ window.removeEventListener('keyup', handleKeyUp);
+ window.removeEventListener('keypress', handleKeyPress);
+ };
+ });
+
+ return (
+ <>
+ <span>{ displayTime }</span>
+ <p>{ helperText[mode] }</p>
+ </>
+ );
+};
+
+export default Timer;
diff --git a/src/developers.json b/src/developers.json
deleted file mode 100644
index 00ed2e3..0000000
--- a/src/developers.json
+++ /dev/null
@@ -1,14 +0,0 @@
-[
- {
- "username": "eug-vs",
- "role": "Back-end, front-end, management"
- },
- {
- "username": "asketonim",
- "role": "Front-end, management"
- },
- {
- "username": "soleni",
- "role": "Testing"
- }
-]
diff --git a/src/index.tsx b/src/index.tsx
index 360ca89..2b8d92d 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -1,87 +1,10 @@
-import React, { useState, useEffect } from 'react';
+import React from 'react';
import ReactDOM from 'react-dom';
-import {
- BenzinThemeProvider,
- Header,
-} from 'react-benzin';
-import { User, Solution } from './types';
-
-import 'typeface-roboto';
-
-import Timer from './pages/Timer/Timer';
-import Scoreboard from './pages/Scoreboard/Scoreboard';
-import Contribute from './pages/Contribute/Contribute';
-import Profile from './pages/Profile/Profile';
-
-import TimerIcon from '@material-ui/icons/Timer';
-import AccountCircleIcon from '@material-ui/icons/AccountCircle';
-import AssignmentIcon from '@material-ui/icons/Assignment';
-import GitHubIcon from '@material-ui/icons/GitHub';
-
-import { get } from './requests';
-
+import Timer from './components/Timer';
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 />),
- profile: (<AccountCircleIcon />),
- scoreboard: (<AssignmentIcon />),
- contribute: (<GitHubIcon />),
- };
-
- useEffect(() => {
- const userId = localStorage.getItem('userId');
- if (userId) {
- get('users/').then(response => {
- setUser(response.data.filter((user: User) => user.id === +userId)[0]);
- });
- }
- }, []);
-
- const Page: React.FC<{ page: string }> = ({ page }) => {
- switch (page) {
- case 'app':
- return (
- <Timer
- user={user}
- recentSolutions={recentSolutions}
- setRecentSolutions={setRecentSolutions}
- setPage={setPage}
- />
- );
-
- case 'profile':
- return <Profile user={user} setUser={setUser} />;
-
- case 'scoreboard':
- return <Scoreboard />;
-
- case 'contribute':
- return <Contribute />;
-
- default:
- return <Contribute />;
- }
- };
-
- return (
- <BenzinThemeProvider>
- <Header
- logo={{
- title: 'ChronoCube',
- icon: null
- }}
- contents={headerContents}
- page={page}
- setPage={setPage}/>
- <Page page={page} />
- </BenzinThemeProvider>
- );
+ return (<Timer />);
};
document.body.style.overflow = 'hidden';
diff --git a/src/pages/Contribute/Contribute.tsx b/src/pages/Contribute/Contribute.tsx
deleted file mode 100644
index 67f2b8a..0000000
--- a/src/pages/Contribute/Contribute.tsx
+++ /dev/null
@@ -1,149 +0,0 @@
-import React from 'react';
-
-import {
- Typography,
- Button,
- List,
- ListItem,
- Link,
- Divider,
- makeStyles,
-} from '@material-ui/core';
-
-import TrendingUpIcon from '@material-ui/icons/TrendingUp';
-import BugReportIcon from '@material-ui/icons/BugReport';
-import NewReleasesIcon from '@material-ui/icons/NewReleases';
-
-import { Window, ContentSection } from 'react-benzin';
-import GithubAvatar, { getUserGithubUrl } from '../../components/GithubAvatar/GithubAvatar';
-
-import developers from '../../developers.json';
-
-const useStyles = makeStyles(theme => ({
- mono: {
- padding: theme.spacing(4),
-
- '& .MuiAvatar-root': {
- marginRight: theme.spacing(2),
- width: theme.spacing(6),
- height: theme.spacing(6),
- }
- },
-}));
-
-
-
-
-const Contribute: React.FC = () => {
- const classes = useStyles();
-
- return (
- <Window type="mono">
- <div className={classes.mono}>
- <ContentSection sectionName="Thank You for using ChronoCube!">
- <p>
- ChronoCube is an Open-Source application, and we welcome anyone who desires to help our project!
- </p>
- <Button
- variant="contained"
- color="primary"
- startIcon={<TrendingUpIcon />}
- href="https://github.com/users/Eug-VS/projects/3"
- >
- Track our progress
- </Button>
- </ContentSection>
- <ContentSection sectionName="Technology stack">
- <p> We only use modern and most relevant technologies to achieve the best results! </p>
- <ul>
- <li><Link href="https://www.django-rest-framework.org/">
- Django REST Framework
- </Link></li>
- <li><Link href="https://reactjs.org/">
- React.js
- </Link></li>
- <li><Link href="https://material-ui.com/">
- Material UI components
- </Link></li>
- </ul>
- <p> Special thanks to other Open-Source projects which made ChronoCube possible: </p>
- <ul>
- <li><Link href="https://github.com/bvaughn/react-window">
- react-window
- </Link></li>
- </ul>
- </ContentSection>
- <ContentSection sectionName="How can I contribute to the project?">
- <p> Thank You for considering helping our project! </p>
- <p>
- All the development process is being tracked on
- the <Link href="https://github.com/users/Eug-VS/projects/3">KanBan board</Link>.
- You can always check it to see what is the current state of the project.
- To contribute your code, fork our repository and then open
- a <Link href="https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests">
- Pull Request</Link>. We will carefully review and, hopefully, accept it!
- If you are unfamiliar with this kind of workflow, we recommend
- reading <Link href="https://github.com/features/project-management/">GitHub guidelines</Link>.
- </p>
- <p>
- We always welcome newcomers! If you are unfamiliar with certain technologies or even with the
- development in general, it is great time to start learning something new!
- Our community will kindly assist every your step, and with us you can easily become
- highly-evaluated developer!
- </p>
- <Button
- variant="contained"
- color="primary"
- startIcon={<NewReleasesIcon />}
- href="https://github.com/Eug-VS/chrono-cube/issues/new"
- >
- Suggest feature
- </Button>
- <Button
- variant="contained"
- color="primary"
- startIcon={<BugReportIcon />}
- href="https://github.com/Eug-VS/chrono-cube/issues/new"
- >
- Report a bug
- </Button>
- </ContentSection>
- <ContentSection sectionName="Developers">
- <List>
- {
- developers.map(developer => (
- <div key={developer.username}>
- <ListItem>
- <GithubAvatar username={developer.username} />
- <div>
- <Link href={getUserGithubUrl(developer.username)}>{developer.username}</Link>
- <Typography component="div" color="textSecondary">
- {developer.role}
- </Typography>
- </div>
- </ListItem>
- <Divider variant="middle" />
- </div>
- ))
- }
- <ListItem>
- <GithubAvatar username="anonymous" />
- You can be here!
- </ListItem>
- </List>
- <Button
- variant="contained"
- color="primary"
- size="large"
- href="https://github.com/users/Eug-VS/projects/3"
- >
- Join us!
- </Button>
- </ContentSection>
- </div>
- </Window>
- );
-};
-
-
-export default Contribute;
diff --git a/src/pages/Profile/Profile.tsx b/src/pages/Profile/Profile.tsx
deleted file mode 100644
index 83acb30..0000000
--- a/src/pages/Profile/Profile.tsx
+++ /dev/null
@@ -1,95 +0,0 @@
-import React, { useState, useEffect } from 'react';
-
-import {
- Button,
- makeStyles,
-} from '@material-ui/core';
-
-import Registration from './Registration';
-import {
- Window,
- ContentSection,
- SmartList,
-} from 'react-benzin';
-import { User, Solution, RenderPropTypes } from '../../types';
-
-import SolutionCard from '../../components/SolutionCard/SolutionCard';
-
-import { get } from '../../requests';
-
-
-const useStyles = makeStyles(theme => ({
- primary: {
- padding: theme.spacing(4),
- },
- cell: {
- padding: theme.spacing(5),
- },
-}));
-
-
-interface PropTypes {
- user: User;
- setUser: (user: User) => void;
-}
-
-
-const Profile: React.FC<PropTypes> = ({ user, setUser }) => {
- const classes = useStyles();
-
- const [profileSolutions, setProfileSolutions] = useState<Solution[]>([]);
-
- const handleLogout = (): void => {
- setUser({ username: 'anonymous', id: null });
- localStorage.clear();
- };
-
- useEffect(() => {
- get(`solutions/?author=${user.id}`).then(response => {
- setProfileSolutions(response.data.reverse());
- });
- }, [user]);
-
- const removeSolution = (id: number): void => {
- setProfileSolutions(profileSolutions.filter((solution => solution.id !== id)));
- };
-
- const renderItem: React.FC<RenderPropTypes> = ({ index, style }) => {
- return (
- <div style={style} className={classes.cell}>
- <SolutionCard data={profileSolutions[index]} removeThisCard={removeSolution} />
- </div>
- );
- };
-
- return (
- <>
- <Window type="primary">
- <div className={classes.primary}>
- { user.id? (
- <ContentSection sectionName={`Welcome back, ${user.username}!`}>
- <p> Total amount of solutions: {profileSolutions.length} </p>
- <p> You can always log out from your account! </p>
- <Button variant="contained" color="secondary" onClick={handleLogout}>
- Logout
- </Button>
- </ContentSection>
- ): (
- <Registration setUser={setUser} />
- )
- }
- </div>
- </Window>
- <Window type="secondary" name="History">
- <SmartList
- itemSize={270}
- itemCount={profileSolutions.length}
- renderItem={renderItem}
- />
- </Window>
- </>
- )
-};
-
-
-export default Profile;
diff --git a/src/pages/Profile/Registration.tsx b/src/pages/Profile/Registration.tsx
deleted file mode 100644
index a5e0f3e..0000000
--- a/src/pages/Profile/Registration.tsx
+++ /dev/null
@@ -1,85 +0,0 @@
-import React, {useState} from 'react';
-
-import {
- TextField,
- Button,
- Checkbox,
- FormControlLabel,
- Grid,
-} from '@material-ui/core';
-import { User } from '../../types';
-
-import { ContentSection } from 'react-benzin';
-import { get, post } from '../../requests';
-
-
-interface PropTypes {
- setUser: (user: User) => void;
-}
-
-const Registration: React.FC<PropTypes> = ({ setUser }) => {
-
- 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: React.ChangeEvent<HTMLInputElement>): void => {
- setIsRememberMe(event.target.checked);
- };
-
- const handleSubmit = (): void => {
- if (username !== '') {
- post('users/', { username })
- .then(response => {
- const user = response.data;
- setUser(user);
- if (isRememberMe) {
- localStorage.setItem('userId', user.id);
- }
- })
- .catch(err => {
- get('users/').then(response => {
- const user = response.data.filter((user: User) => user.username === username)[0];
- setUser(user);
- if (isRememberMe) {
- localStorage.setItem('userId', user.id);
- }
- });
- });
- }
- };
-
- return (
- <ContentSection sectionName="Tell us who you are">
- <p> Choose yourself a username to track your progress and compete with others: </p>
- <Grid container direction="column">
- <Grid item>
- <TextField
- variant="outlined"
- color="secondary"
- label="Username"
- value={username}
- onChange={handleChange}
- />
- </Grid>
- <Grid item>
- <FormControlLabel
- control={<Checkbox color="secondary" onChange={handleCheck} />}
- label="Remember me"
- />
- </Grid>
- <Grid item>
- <Button variant="contained" color="secondary" onClick={handleSubmit}>
- Submit!
- </Button>
- </Grid>
- </Grid>
- </ContentSection>
- );
-};
-
-
-export default Registration;
diff --git a/src/pages/Scoreboard/Scoreboard.tsx b/src/pages/Scoreboard/Scoreboard.tsx
deleted file mode 100644
index e4185bd..0000000
--- a/src/pages/Scoreboard/Scoreboard.tsx
+++ /dev/null
@@ -1,70 +0,0 @@
-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';
-
-import { get } from '../../requests';
-
-
-const useStyles = makeStyles(theme => ({
- cell: {
- display: 'flex',
- justifyContent: 'center',
- padding: theme.spacing(4),
-
- '& .MuiCard-root': {
- width: '30%',
- }
- }
-}));
-
-
-const Scoreboard: React.FC = () => {
- const classes = useStyles();
- const [solutions, setSolutions] = useState<Solution[]>([]);
-
- const updateSolutions = (): void => {
- get('scoreboard/').then(response => {
- setSolutions(response.data);
- });
- };
-
- const removeSolution = (id: number): void => {
- updateSolutions();
- };
-
- useEffect(() => {
- setTimeout(updateSolutions, 300);
- }, []);
-
- const renderItem: React.FC<RenderPropTypes> = ({ index, style }) => {
- return (
- <div style={style} className={classes.cell}>
- <SolutionCard data={solutions[index]} removeThisCard={removeSolution}/>
- </div>
- )
- };
-
- return (
- <Window type="mono">
- { solutions.length === 0 &&
- <div className={classes.cell}>
- <Loading/>
- </div>
- }
- <SmartList
- itemSize={300}
- itemCount={solutions.length}
- renderItem={renderItem}
- />
- </Window>
- )
-};
-
-
-export default Scoreboard;
diff --git a/src/pages/Timer/Timer.tsx b/src/pages/Timer/Timer.tsx
deleted file mode 100644
index a890815..0000000
--- a/src/pages/Timer/Timer.tsx
+++ /dev/null
@@ -1,102 +0,0 @@
-import React from 'react';
-
-import { post } from '../../requests';
-
-import {
- Window,
- ContentSection,
- SmartList,
-} from 'react-benzin';
-import { User, Solution, RenderPropTypes } from '../../types';
-
-import TimerButton from './TimerButton';
-import SolutionCard from '../../components/SolutionCard/SolutionCard';
-
-import { Button, makeStyles } from '@material-ui/core';
-
-
-const useStyles = makeStyles(theme => ({
- primary: {
- padding: theme.spacing(4),
- },
- cell: {
- padding: theme.spacing(5),
- },
-}));
-
-
-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: string): void => {
- const solution = { 'author_id': user.id, result };
- post('solutions/', solution).then(response => {
- setRecentSolutions([response.data].concat(recentSolutions));
- });
- };
-
- const handleLearnMore = (): void => {
- setPage('contribute');
- };
-
- const handleLogin = (): void => {
- setPage('profile');
- };
-
- const removeSolution = (id: number): void => {
- setRecentSolutions(recentSolutions.filter((solution => solution.id !== id)));
- };
-
- const renderItem: React.FC<RenderPropTypes> = ({ index, style }) => {
- const solution = recentSolutions[index];
- return (
- <div style={style} className={classes.cell}>
- <SolutionCard data={solution} removeThisCard={removeSolution} />
- </div>
- );
- };
-
- return (
- <>
- <Window type="primary">
- <div className={classes.primary}>
- <ContentSection sectionName="Welcome to ChronoCube!">
- <p>
- ChronoCube is a professional speedcubing timer.
- Share your results publicly - let everyone see your progress and
- achievements!
- Every speedcuber will benefit
- from using it - both amateur and professional!
- </p>
- <Button variant="contained" color="primary" onClick={handleLearnMore}> Learn more </Button>
- </ContentSection>
- {user.id === null &&
- <ContentSection sectionName="Log into an account">
- <p> Tell us your name so we can track your progress</p>
- <Button variant="contained" color="primary" onClick={handleLogin} size="large"> Login </Button>
- </ContentSection>
- }
- <TimerButton registerResult={registerResult} />
- </div>
- </Window>
- <Window type="secondary" name="Recent solutions">
- <SmartList
- itemSize={270}
- itemCount={recentSolutions.length}
- renderItem={renderItem}
- />
- </Window>
- </>
- );
-};
-
-
-export default Timer;
diff --git a/src/pages/Timer/TimerButton.tsx b/src/pages/Timer/TimerButton.tsx
deleted file mode 100644
index 0a3bf38..0000000
--- a/src/pages/Timer/TimerButton.tsx
+++ /dev/null
@@ -1,130 +0,0 @@
-import React, { useState, useEffect } from 'react';
-
-import { Paper, Typography } from '@material-ui/core';
-import { makeStyles } from '@material-ui/core/styles';
-
-const useStyles = makeStyles(theme => ({
- root: {
- textAlign: 'center',
- padding: theme.spacing(5),
- background: theme.palette.background.elevation3,
- marginTop: theme.spacing(10),
- },
-}));
-
-
-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<string>('00:00:00');
- const [mode, setMode] = useState<Mode>('idle');
-
- useEffect(()=> {
- const timestamp = Date.now();
-
- if (mode === 'countdown') {
- const repeater = setInterval(() => {
- const timeDelta = maxCountdown - (Date.now() - timestamp);
- if (timeDelta <= 0) setMode('over');
- setTime(convertTimeToString(timeDelta));
- }, 10);
- return (): void => clearInterval(repeater);
- }
-
- if (mode === 'running') {
- const repeater = setInterval(() => {
- setTime(convertTimeToString(Date.now() - timestamp));
- }, 10);
- return (): void => clearInterval(repeater);
- }
-
- if (mode === 'over') {
- setTime('00:00:00');
- }
- }, [mode]);
-
- const handleKeyPress = (event: KeyboardEvent): void => {
- event.preventDefault();
- if (event.keyCode === SPACE && mode === 'idle' ) setMode('countdown');
- };
-
- const handleKeyUp = (event: KeyboardEvent): void => {
- if (event.keyCode === SPACE) {
- if (mode === 'running') {
- registerResult(time);
- setMode('idle');
- } else if (mode === 'over') {
- setMode('idle');
- } else {
- setMode('running');
- }
- }
- };
-
- useEffect(() => {
- window.addEventListener('keyup', handleKeyUp);
- window.addEventListener('keypress', handleKeyPress);
-
- return (): void => {
- window.removeEventListener('keyup', handleKeyUp);
- window.removeEventListener('keypress', handleKeyPress);
- };
- });
-
- const composeHelperText = (): string => {
- switch (mode) {
- case 'running': return 'Go fast!';
- case 'countdown': return 'Release SPACE to begin';
- case 'over': return 'You are too late!';
- default: return 'Hold SPACE to start countdown';
- }
- };
-
- const helperColor = (): 'primary' | 'secondary' | 'textSecondary' => {
- switch (mode) {
- case 'running': return 'primary';
- case 'over': return 'secondary';
- default: return 'textSecondary';
- }
- };
-
- return (
- <Paper className={classes.root}>
- <Typography variant="h1"> {time} </Typography>
- <Typography variant="h5" color={helperColor()}>
- {composeHelperText()}
- </Typography>
- </Paper>
- );
-};
-
-const convertTimeToString = (timeDelta: number): string => {
- let resultTime = '';
-
- const minute = Math.floor(timeDelta / 60000);
- if (minute < 10) resultTime += '0';
- resultTime += minute + ':';
-
- let second = Math.floor(timeDelta / 1000);
- if (second > 59) second %= 60;
- if (second < 10) resultTime += '0';
- resultTime += second + ':';
-
- const mill = Math.floor((timeDelta % 1000) / 10);
- if (mill < 10) resultTime += '0';
- resultTime += mill;
-
- return resultTime;
-};
-
-
-export default TimerButton;
diff --git a/src/react-app-env.d.ts b/src/react-app-env.d.ts
deleted file mode 100644
index 6431bc5..0000000
--- a/src/react-app-env.d.ts
+++ /dev/null
@@ -1 +0,0 @@
-/// <reference types="react-scripts" />
diff --git a/src/requests.ts b/src/requests.ts
deleted file mode 100644
index 0242ed5..0000000
--- a/src/requests.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-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
deleted file mode 100644
index 949d410..0000000
--- a/src/types.d.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-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;
-}
-
diff --git a/src/utils/convertTimeToString.ts b/src/utils/convertTimeToString.ts
new file mode 100644
index 0000000..098f34a
--- /dev/null
+++ b/src/utils/convertTimeToString.ts
@@ -0,0 +1,18 @@
+export default (timeDelta: number): string => {
+ let resultTime = '';
+
+ const minute = Math.floor(timeDelta / 60000);
+ if (minute < 10) resultTime += '0';
+ resultTime += minute + ':';
+
+ let second = Math.floor(timeDelta / 1000);
+ if (second > 59) second %= 60;
+ if (second < 10) resultTime += '0';
+ resultTime += second + ':';
+
+ const mill = Math.floor((timeDelta % 1000) / 10);
+ if (mill < 10) resultTime += '0';
+ resultTime += mill;
+
+ return resultTime;
+};