diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/components/Feed/Feed.tsx | 4 | ||||
-rw-r--r-- | src/components/PollCard/PercentageBar.tsx | 12 | ||||
-rw-r--r-- | src/components/PollCard/PollCard.tsx | 79 | ||||
-rw-r--r-- | src/index.tsx | 10 | ||||
-rw-r--r-- | src/pages/AuthPage/SignUpForm.tsx | 9 | ||||
-rw-r--r-- | src/pages/FeedPage/FeedPage.tsx | 3 | ||||
-rw-r--r-- | src/pages/ProfilePage/ProfileInfo.tsx | 4 | ||||
-rw-r--r-- | src/pages/ProfilePage/ProfilePage.tsx | 3 | ||||
-rw-r--r-- | src/requests.ts | 17 | ||||
-rw-r--r-- | src/types.d.ts | 19 |
10 files changed, 95 insertions, 65 deletions
diff --git a/src/components/Feed/Feed.tsx b/src/components/Feed/Feed.tsx index 3b8e16f..d81da99 100644 --- a/src/components/Feed/Feed.tsx +++ b/src/components/Feed/Feed.tsx @@ -1,6 +1,6 @@ import React from 'react'; import { makeStyles } from '@material-ui/core/styles'; -import { Poll } from '../../types'; +import { Poll } from 'which-types'; import PollCard from '../PollCard/PollCard'; interface PropTypes { @@ -21,7 +21,7 @@ const Feed: React.FC<PropTypes> = ({ polls, navigate }) => { return ( <div className={classes.root}> - {polls.map(poll => <PollCard poll={poll} key={poll._id} navigate={navigate} />)} + {polls.map(poll => <PollCard initialPoll={poll} key={poll._id} navigate={navigate} />)} </div> ); }; diff --git a/src/components/PollCard/PercentageBar.tsx b/src/components/PollCard/PercentageBar.tsx index 6a50a9e..a93d7b4 100644 --- a/src/components/PollCard/PercentageBar.tsx +++ b/src/components/PollCard/PercentageBar.tsx @@ -1,9 +1,11 @@ import React from 'react'; import { makeStyles } from '@material-ui/core/styles'; +import LikeIcon from '@material-ui/icons/Favorite'; interface PropTypes { value: number; which: 'left' | 'right'; + like: boolean; } const useStyles = makeStyles({ @@ -12,7 +14,9 @@ const useStyles = makeStyles({ color: 'white', top: '86%', fontSize: 20, - textShadow: '0 0 3px black' + textShadow: '0 0 3px black', + display: 'flex', + alignItems: 'center' }, left: { left: 30 @@ -22,13 +26,13 @@ const useStyles = makeStyles({ } }); -const PercentageBar: React.FC<PropTypes> = ({ value, which }) => { +const PercentageBar: React.FC<PropTypes> = ({ value, which, like }) => { const classes = useStyles(); return ( <div className={`${classes.root} ${classes[which]}`}> - {value} - % + {like && <LikeIcon />} + {`${value}%`} </div> ); }; diff --git a/src/components/PollCard/PollCard.tsx b/src/components/PollCard/PollCard.tsx index baf896f..40f5fd7 100644 --- a/src/components/PollCard/PollCard.tsx +++ b/src/components/PollCard/PollCard.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState } from 'react'; import { makeStyles } from '@material-ui/core/styles'; import { Card, @@ -7,43 +7,73 @@ import { Avatar, CardHeader } from '@material-ui/core/'; -import { Poll } from '../../types'; +import { Which, Poll } from 'which-types'; + import PercentageBar from './PercentageBar'; +import { post } from '../../requests'; interface PropTypes { - poll: Poll; + initialPoll: Poll; navigate: (prefix: string, id: string) => void; } const useStyles = makeStyles(theme => ({ root: { maxWidth: theme.spacing(75), - height: theme.spacing(63), - margin: '20px auto' + height: 488, + margin: '40px auto' }, images: { height: theme.spacing(50), - width: theme.spacing(38) + width: 300 }, imagesBlock: { display: 'flex' }, avatar: { cursor: 'pointer' + }, + rateLine: { + position: 'relative', + width: '100%', + height: theme.spacing(2), + backgroundColor: theme.palette.primary.light + }, + fillRateLine: { + height: theme.spacing(2), + backgroundColor: theme.palette.primary.main, + transitionDuration: '0.5s' } })); - -const PollCard: React.FC<PropTypes> = ({ poll, navigate }) => { +const PollCard: React.FC<PropTypes> = ({ initialPoll, navigate }) => { + const [poll, setPoll] = useState<Poll>(initialPoll); const classes = useStyles(); - const { author, contents } = poll; + const { author, contents: { left, right }, userChoice } = poll; const handleNavigate = () => { navigate('profile', poll.author._id); }; - const leftPercentage = Math.round(100 * (contents.left.votes / (contents.left.votes + contents.right.votes))); - const rightPercentage = 100 - leftPercentage; + const vote = (which: Which) => { + if (userChoice) return; + post('votes/', { which, pollId: poll._id }).then(() => { + poll.contents[which].votes += 1; + poll.userChoice = which; + setPoll({ ...poll }); + }); + }; + + const handleLeft = () => vote('left'); + const handleRight = () => vote('right'); + + const leftPercentage = Math.round(100 * (left.votes / (left.votes + right.votes))); + + const percentage = { + left: leftPercentage, + right: 100 - leftPercentage + }; + const dominant: Which = left.votes >= right.votes ? 'left' : 'right'; return ( <Card className={classes.root}> @@ -52,33 +82,40 @@ const PollCard: React.FC<PropTypes> = ({ poll, navigate }) => { <Avatar aria-label="avatar" src={author.avatarUrl} - alt={author.name[0].toUpperCase()} + alt={author.username[0].toUpperCase()} onClick={handleNavigate} className={classes.avatar} /> )} - title={author.name} + title={author.username} /> <div className={classes.imagesBlock}> - <CardActionArea> + <CardActionArea onDoubleClick={handleLeft}> <CardMedia className={classes.images} - image={contents.left.url} + image={left.url} /> - <PercentageBar value={leftPercentage} which="left" /> + <PercentageBar value={percentage.left} which="left" like={userChoice === 'left'} /> </CardActionArea> - <CardActionArea> + <CardActionArea onDoubleClick={handleRight}> <CardMedia className={classes.images} - image={contents.right.url} + image={right.url} /> - <PercentageBar value={rightPercentage} which="right" /> + <PercentageBar value={percentage.right} which="right" like={userChoice === 'right'} /> </CardActionArea> </div> + <div className={classes.rateLine}> + <div + className={classes.fillRateLine} + style={{ + width: `${percentage[dominant]}%`, + float: dominant + }} + /> + </div> </Card> ); }; - export default PollCard; - diff --git a/src/index.tsx b/src/index.tsx index 1777e90..50b19f7 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -9,11 +9,12 @@ import { CssBaseline } from '@material-ui/core'; import teal from '@material-ui/core/colors/teal'; import 'typeface-roboto'; +import { User } from 'which-types'; import Header from './components/Header/Header'; import ProfilePage from './pages/ProfilePage/ProfilePage'; import FeedPage from './pages/FeedPage/FeedPage'; import AuthPage from './pages/AuthPage/AuthPage'; -import { User, Page } from './types'; +import { Page } from './types'; import { get, post } from './requests'; import ScrollTopArrow from './components/ScrollTopArrow/ScrollTopArrow'; @@ -21,7 +22,8 @@ import ScrollTopArrow from './components/ScrollTopArrow/ScrollTopArrow'; const theme = createMuiTheme({ palette: { primary: { - main: teal[700] + main: teal[700], + light: teal[100] } } }); @@ -53,10 +55,10 @@ const App: React.FC = () => { } }; - const logIn = (name: string, password: string): Promise<boolean> => { + const logIn = (username: string, password: string): Promise<boolean> => { return post('/authentication', { strategy: 'local', - name, + username, password }).then(response => { const me = response.data.user; diff --git a/src/pages/AuthPage/SignUpForm.tsx b/src/pages/AuthPage/SignUpForm.tsx index 2769eb0..0e3d0c7 100644 --- a/src/pages/AuthPage/SignUpForm.tsx +++ b/src/pages/AuthPage/SignUpForm.tsx @@ -31,12 +31,11 @@ const SignUpForm: React.FC<PropTypes> = ({ logIn }) => { const inputRefPassword = useRef<HTMLInputElement>(); const onClick = () => { - const name = inputRef.current?.value; + const username = inputRef.current?.value; const password = inputRefPassword.current?.value; - const newUser = { name, password }; - if (name && password) { - post('/users', newUser).then(() => { - logIn(name, password); + if (username && password) { + post('/users', { username, password }).then(() => { + logIn(username, password); }); } }; diff --git a/src/pages/FeedPage/FeedPage.tsx b/src/pages/FeedPage/FeedPage.tsx index fd75190..937b0a9 100644 --- a/src/pages/FeedPage/FeedPage.tsx +++ b/src/pages/FeedPage/FeedPage.tsx @@ -1,5 +1,6 @@ import React, { useState, useEffect } from 'react'; -import { Poll } from '../../types'; +import { Poll } from 'which-types'; + import Feed from '../../components/Feed/Feed'; import { get } from '../../requests'; diff --git a/src/pages/ProfilePage/ProfileInfo.tsx b/src/pages/ProfilePage/ProfileInfo.tsx index bddecd8..7208ec8 100644 --- a/src/pages/ProfilePage/ProfileInfo.tsx +++ b/src/pages/ProfilePage/ProfileInfo.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { Avatar } from '@material-ui/core/'; import { makeStyles } from '@material-ui/core/styles'; import Button from '@material-ui/core/Button/Button'; -import { User } from '../../types'; +import { User } from 'which-types'; interface PropTypes { user: User | undefined; @@ -41,7 +41,7 @@ const ProfileInfo: React.FC<PropTypes> = ({ user, logOut }) => { <div> <Avatar className={classes.avatar} src={user?.avatarUrl} /> <div className={classes.name}> - {user?.name} + {user?.username} </div> <div className={classes.profileMenu}> <div style={{ borderBottom: '1px solid green', color: 'green' }} className={classes.menuButton}> diff --git a/src/pages/ProfilePage/ProfilePage.tsx b/src/pages/ProfilePage/ProfilePage.tsx index 0f5fb2b..363d4ff 100644 --- a/src/pages/ProfilePage/ProfilePage.tsx +++ b/src/pages/ProfilePage/ProfilePage.tsx @@ -1,5 +1,6 @@ import React, { useState, useEffect } from 'react'; -import { User, Poll } from '../../types'; +import { User, Poll } from 'which-types'; + import ProfileInfo from './ProfileInfo'; import Feed from '../../components/Feed/Feed'; import { get } from '../../requests'; diff --git a/src/requests.ts b/src/requests.ts index 486502d..4cfd37b 100644 --- a/src/requests.ts +++ b/src/requests.ts @@ -1,10 +1,15 @@ -import axios, { AxiosResponse } from 'axios'; +import axios from 'axios'; +import _ from 'lodash'; -type Request = (url: string, data?: Record<string, unknown>) => Promise<AxiosResponse>; +const requests = axios.create({ + baseURL: 'http://localhost:3030' +}); -const baseApiUrl = 'http://localhost:3030'; +requests.interceptors.request.use(config => { + const token = localStorage.getItem('token'); + return _.set(config, 'headers.Authorization', token); +}); -export const get: Request = url => axios.get(baseApiUrl + url); -export const del: Request = url => axios.delete(baseApiUrl + url); -export const post: Request = (url, data) => axios.post(baseApiUrl + url, data); +export const { get, post, put } = requests; +export default requests; diff --git a/src/types.d.ts b/src/types.d.ts index a62eec8..73346ce 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -2,23 +2,4 @@ export interface Page { prefix: string; id: string; } -export interface User { - name: string; - avatarUrl: string; - _id: string; -} - -interface ImageData { - url: string; - votes: number; -} - -export interface Poll { - _id: string; - author: User; - contents: { - left: ImageData; - right: ImageData; - }; -} |