diff options
Diffstat (limited to 'src/pages')
-rw-r--r-- | src/pages/FeedPage/FeedPage.tsx | 27 | ||||
-rw-r--r-- | src/pages/FeedPage/PollSubmission.tsx | 92 | ||||
-rw-r--r-- | src/pages/FeedPage/PollSubmissionImage.tsx | 87 | ||||
-rw-r--r-- | src/pages/FeedPage/types.ts | 7 | ||||
-rw-r--r-- | src/pages/HomePage/HomePage.tsx | 203 | ||||
-rw-r--r-- | src/pages/HomePage/ReviewForm.tsx | 74 | ||||
-rw-r--r-- | src/pages/LoginPage/LoginPage.tsx | 103 | ||||
-rw-r--r-- | src/pages/NotificationsPage/NotificationsPage.tsx | 23 | ||||
-rw-r--r-- | src/pages/Page.tsx | 59 | ||||
-rw-r--r-- | src/pages/ProfilePage/Highlight.tsx | 39 | ||||
-rw-r--r-- | src/pages/ProfilePage/MoreMenu.tsx | 72 | ||||
-rw-r--r-- | src/pages/ProfilePage/ProfileInfo.tsx | 166 | ||||
-rw-r--r-- | src/pages/ProfilePage/ProfilePage.tsx | 55 | ||||
-rw-r--r-- | src/pages/RegistrationPage/RegistrationPage.tsx | 96 |
14 files changed, 0 insertions, 1103 deletions
diff --git a/src/pages/FeedPage/FeedPage.tsx b/src/pages/FeedPage/FeedPage.tsx deleted file mode 100644 index da0fb2a..0000000 --- a/src/pages/FeedPage/FeedPage.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import React from 'react'; -import { Poll } from 'which-types'; -import { Container } from '@material-ui/core/'; - -import Feed from '../../components/Feed/Feed'; -import PollSubmission from './PollSubmission'; -import { useAuth } from '../../hooks/useAuth'; -import { useFeed } from '../../hooks/APIClient'; - -const FeedPage: React.FC = () => { - const { data, mutate } = useFeed(); - const { isAuthenticated } = useAuth(); - - const addPoll = (poll: Poll): void => { - mutate([poll, ...data], true); - }; - - return ( - <Container maxWidth="sm" disableGutters> - {isAuthenticated && <PollSubmission addPoll={addPoll} />} - <Feed polls={data} /> - </Container> - ); -}; - -export default FeedPage; - diff --git a/src/pages/FeedPage/PollSubmission.tsx b/src/pages/FeedPage/PollSubmission.tsx deleted file mode 100644 index 347eecc..0000000 --- a/src/pages/FeedPage/PollSubmission.tsx +++ /dev/null @@ -1,92 +0,0 @@ -import React, { useState } from 'react'; -import { makeStyles } from '@material-ui/core/styles'; -import Collapse from '@material-ui/core/Collapse'; -import { - Button, - Card, - ClickAwayListener, - Divider -} from '@material-ui/core'; -import { Poll, Which } from 'which-types'; -import { useSnackbar } from 'notistack'; -import PollSubmissionImage from './PollSubmissionImage'; -import UserStrip from '../../components/UserStrip/UserStrip'; -import { post } from '../../requests'; -import { Contents } from './types'; -import { useAuth } from '../../hooks/useAuth'; - -interface PropTypes{ - addPoll: (poll: Poll) => void; -} - -const useStyles = makeStyles(theme => ({ - root: { - marginBottom: theme.spacing(4) - }, - images: { - height: theme.spacing(50), - display: 'flex' - } -})); - -const emptyContents: Contents = { - left: { url: '' }, - right: { url: '' } -}; - -const PollSubmission: React.FC<PropTypes> = ({ addPoll }) => { - const classes = useStyles(); - const [expanded, setExpanded] = useState(false); - const [contents, setContents] = useState<Contents>(emptyContents); - const { enqueueSnackbar } = useSnackbar(); - const { user } = useAuth(); - - const readyToSubmit = contents.left.url && contents.right.url; - - const setUrl = (which: Which) => (url: string): void => { - setContents({ ...contents, [which]: { url } }); - }; - - const handleClickAway = () => { - setExpanded(false); - }; - - const handleClick = () => { - if (expanded && readyToSubmit) { - post('/polls/', { contents }).then(response => { - addPoll(response.data); - enqueueSnackbar('Your poll has been successfully created!', { - variant: 'success' - }); - }); - setContents({ ...emptyContents }); - } - setExpanded(!expanded); - }; - - return ( - <ClickAwayListener onClickAway={handleClickAway}> - <Card className={classes.root}> - <Collapse in={expanded} timeout="auto" unmountOnExit> - {user && <UserStrip user={user} info="" />} - <Divider /> - <div className={classes.images}> - <PollSubmissionImage url={contents.left.url} setUrl={setUrl('left')} /> - <PollSubmissionImage url={contents.right.url} setUrl={setUrl('right')} /> - </div> - </Collapse> - <Button - color="primary" - disabled={expanded && !readyToSubmit} - variant={expanded ? 'contained' : 'outlined'} - onClick={handleClick} - fullWidth - > - {expanded ? 'Submit' : 'Create a Poll'} - </Button> - </Card> - </ClickAwayListener> - ); -}; - -export default PollSubmission; diff --git a/src/pages/FeedPage/PollSubmissionImage.tsx b/src/pages/FeedPage/PollSubmissionImage.tsx deleted file mode 100644 index 8835989..0000000 --- a/src/pages/FeedPage/PollSubmissionImage.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import React, { useState } from 'react'; -import { makeStyles } from '@material-ui/core/styles'; -import CloudUploadIcon from '@material-ui/icons/CloudUpload'; -import { CardActionArea, CardMedia, Typography } from '@material-ui/core'; -import CancelOutlinedIcon from '@material-ui/icons/CancelOutlined'; - -import UploadImage from '../../components/UploadImage/UploadImage'; - -interface PropTypes { - url: string; - setUrl: (url: string) => void; -} - -const useStyles = makeStyles({ - root: { - display: 'flex', - justifyContent: 'center', - flexDirection: 'column', - alignItems: 'center' - }, - clearIcon: { - opacity: '.5', - fontSize: 50 - }, - media: { - height: '100%', - width: '100%', - display: 'flex', - justifyContent: 'center', - alignItems: 'center' - }, - text: { - textAlign: 'center' - } -}); - - -const PollSubmissionImage: React.FC<PropTypes> = ({ url, setUrl }) => { - const classes = useStyles(); - const [isModalOpen, setIsModalOpen] = useState(false); - const [isMediaHover, setIsMediaHover] = useState(false); - - const handleClick = (): void => { - if (!isModalOpen) { - if (url) setUrl(''); - else setIsModalOpen(!isModalOpen); - } - }; - - const handleMouseEnter = (): void => { - setIsMediaHover(true); - }; - - const handleMouseLeave = (): void => { - setIsMediaHover(false); - }; - - - const Upload = ( - <> - <CloudUploadIcon fontSize="large" color="primary" /> - <Typography variant="h5" className={classes.text}> Upload an image </Typography> - </> - ); - - const Media = ( - <CardMedia - image={url} - className={classes.media} - onMouseEnter={handleMouseEnter} - onMouseLeave={handleMouseLeave} - > - {isMediaHover && <CancelOutlinedIcon className={classes.clearIcon} />} - </CardMedia> - ); - - return ( - <> - <CardActionArea onClick={handleClick} className={classes.root}> - {url ? Media : Upload} - </CardActionArea> - <UploadImage isOpen={isModalOpen} setIsOpen={setIsModalOpen} callback={setUrl} /> - </> - ); -}; - -export default PollSubmissionImage; diff --git a/src/pages/FeedPage/types.ts b/src/pages/FeedPage/types.ts deleted file mode 100644 index 24ace4e..0000000 --- a/src/pages/FeedPage/types.ts +++ /dev/null @@ -1,7 +0,0 @@ -export interface ImageData { - url: string; -} -export interface Contents { - left: ImageData; - right: ImageData; -} diff --git a/src/pages/HomePage/HomePage.tsx b/src/pages/HomePage/HomePage.tsx deleted file mode 100644 index b1dc506..0000000 --- a/src/pages/HomePage/HomePage.tsx +++ /dev/null @@ -1,203 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { useHistory } from 'react-router-dom'; -import { - Typography, - Divider, - Grid, - Button, - Link, - useMediaQuery -} from '@material-ui/core/'; -import { makeStyles, useTheme } from '@material-ui/core/styles'; -import TrendingUpIcon from '@material-ui/icons/TrendingUp'; -import { Rating } from '@material-ui/lab'; -import { Feedback } from 'which-types'; - -import { useAuth } from '../../hooks/useAuth'; -import { get } from '../../requests'; -import ReviewCard from '../../components/ReviewCard/ReviewCard'; -import ReviewForm from './ReviewForm'; - -const useStyles = makeStyles(theme => ({ - root: { - overflow: 'hidden', - padding: theme.spacing(0, 2) - }, - logo: { - width: theme.spacing(20), - height: theme.spacing(20) - }, - score: { - fontWeight: 'bold' - }, - signup: { - marginLeft: theme.spacing(2) - }, - reviews: { - [theme.breakpoints.up('md')]: { - padding: theme.spacing(0, 10) - } - } -})); - -const HomePage: React.FC = () => { - const [feedbacks, setFeedbacks] = useState<Feedback[]>([]); - const classes = useStyles(); - const history = useHistory(); - const { isAuthenticated, user } = useAuth(); - const theme = useTheme(); - const isMobile = useMediaQuery(theme.breakpoints.down('sm')); - - const rating = feedbacks.length && feedbacks.reduce( - (acc: number, feedback: Feedback) => acc + feedback.score, - 0 - ) / feedbacks.length; - - useEffect(() => { - get('/feedback').then(response => { - setFeedbacks(response.data); - }); - }, []); - - const handleLetsGo = () => { - history.push('/feed'); - }; - - const handleSignUp = () => { - history.push('/registration'); - }; - - const GithubLink = <Link href="https://github.com/which-ecosystem">GitHub</Link>; - const TypescriptLink = <Link href="https://www.typescriptlang.org/">Typescript</Link>; - const ReactLink = <Link href="https://reactjs.org/">React</Link>; - const FeathersLink = <Link href="https://feathersjs.com">Feathers</Link>; - const MUILink = <Link href="https://material-ui.com">Material-UI</Link>; - const EmailLink = <Link href="mailto: eug-vs@keemail.me">eug-vs@keemail.me</Link>; - - const Reviews = ( - <div className={classes.reviews}> - {feedbacks.map(feedback => <ReviewCard feedback={feedback} />)} - </div> - ); - - const FeedbackSection = feedbacks.findIndex((feedback: Feedback) => feedback.author._id === user?._id) >= 0 ? ( - <p> - You have already left feedback for this version. - If you have more to say, please open GitHub issue or contact us directly via email: {EmailLink}. - Alternatively, you can just wait for another application patch to come out. - </p> - ) : ( - <> - <p> - Here you can share your thougts about Which with us! - Note that you can ony leave feedback once per application version (there will be plenty of them later). - </p> - {isAuthenticated ? <ReviewForm /> : ( - <> - <p> You must be authorized to leave feedback.</p> - <Button - variant="outlined" - color="primary" - onClick={handleSignUp} - > - sign in - </Button> - </> - )} - </> - ); - - return ( - <div className={classes.root}> - <Grid container spacing={4}> - <Grid item xs={12} md={4}> - <Grid container direction="column" spacing={1} alignItems="center"> - <Grid item> - <img src={`${process.env.PUBLIC_URL}/which-logo-512.png`} alt="logo" className={classes.logo} /> - </Grid> - <Grid item> - {rating !== 0 && <Rating value={rating} readOnly size="large" />} - </Grid> - <Grid item> - {rating !== 0 && ( - <Typography variant="h5" className={classes.score}> - User score: {rating.toFixed(1)} - </Typography> - )} - </Grid> - </Grid> - {isMobile || Reviews} - </Grid> - <Grid item xs={12} md={5}> - <Grid container direction="column" spacing={6}> - <Grid item> - <Typography variant="h4"> Which one to choose? </Typography> - <Divider /> - <Typography> - <p> - Have you ever found yourself stuck between two options, not being able to choose any? - This is exactly the problem we are going to solve! - </p> - <p>Share your minor everyday uncertainties with the whole world and see what others think!</p> - <Button variant="contained" color="primary" size="large" onClick={handleLetsGo}> - {'let\'s go!'} - </Button> - {!isAuthenticated && ( - <Button - variant="outlined" - color="primary" - size="large" - className={classes.signup} - onClick={handleSignUp} - > - sign up - </Button> - )} - </Typography> - </Grid> - <Grid item> - <Typography variant="h4"> About the project </Typography> - <Divider /> - <Typography> - <p> - The project is written in {TypescriptLink} and features {ReactLink}, {FeathersLink}, and {MUILink}. - It is currently open-source and you can visit our {GithubLink} (make sure to star our repositories)! - </p> - <p> - We encourage any developer to check it out. Feel free to open issues and create Pull Requests! - </p> - <p> - All the development process is being tracked on the KanBan board (thanks GitHub). - You can always check it to see what is the current state of the project. - </p> - <Button - variant="outlined" - color="primary" - startIcon={<TrendingUpIcon />} - href="https://github.com/orgs/which-ecosystem/projects/1" - > - track our progress - </Button> - </Typography> - </Grid> - <Grid item> - <Typography variant="h4"> Leave feedback </Typography> - <Divider /> - <Typography> - {FeedbackSection} - </Typography> - </Grid> - {isMobile && ( - <Grid item> - {Reviews} - </Grid> - )} - </Grid> - </Grid> - </Grid> - </div> - ); -}; - -export default HomePage; - diff --git a/src/pages/HomePage/ReviewForm.tsx b/src/pages/HomePage/ReviewForm.tsx deleted file mode 100644 index b626ce2..0000000 --- a/src/pages/HomePage/ReviewForm.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import React, { useState } from 'react'; -import { useHistory } from 'react-router-dom'; -import { makeStyles } from '@material-ui/core/styles'; -import { TextField, Button } from '@material-ui/core'; -import { Rating } from '@material-ui/lab'; -import { useSnackbar } from 'notistack'; - -import { post } from '../../requests'; - -const version = 'v1.0.0'; - -const useStyles = makeStyles(theme => ({ - root: { - display: 'flex', - flexDirection: 'column' - }, - textField: { - margin: theme.spacing(2, 0) - } -})); - -const ReviewForm: React.FC = () => { - const [contents, setContents] = useState<string>(''); - const [score, setScore] = useState<number>(0); - const classes = useStyles(); - const history = useHistory(); - const { enqueueSnackbar } = useSnackbar(); - - const handleSubmit = (): void => { - if (score) { - post('/feedback', { contents, score, version }).then(() => { - enqueueSnackbar('Your feedback has been submitted!', { - variant: 'success' - }); - history.push('/feed'); - }); - } - }; - - const handleChange = (event: React.ChangeEvent<HTMLInputElement>): void => { - setContents(event.target?.value || ''); - }; - - const handleChangeRating = (event: React.ChangeEvent<Record<string, unknown>>, newScore: number | null): void => { - setScore(newScore || 0); - }; - - return ( - <div className={classes.root}> - <Rating value={score} onChange={handleChangeRating} size="large" /> - <TextField - value={contents} - onChange={handleChange} - label="Feedback" - variant="outlined" - className={classes.textField} - rows={4} - multiline - /> - <div> - <Button - variant="contained" - color="primary" - size="large" - onClick={handleSubmit} - > - submit - </Button> - </div> - </div> - ); -}; - -export default ReviewForm; diff --git a/src/pages/LoginPage/LoginPage.tsx b/src/pages/LoginPage/LoginPage.tsx deleted file mode 100644 index 335cbb1..0000000 --- a/src/pages/LoginPage/LoginPage.tsx +++ /dev/null @@ -1,103 +0,0 @@ -import React, { useState, useRef } from 'react'; -import { useHistory } from 'react-router-dom'; -import { makeStyles } from '@material-ui/core/styles'; -import { - TextField, - Button, - FormControlLabel, - Switch -} from '@material-ui/core'; -import { useAuth } from '../../hooks/useAuth'; - -const useStyles = makeStyles(theme => ({ - root: { - '& > *': { - margin: theme.spacing(1), - width: theme.spacing(35) - }, - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - textAlign: 'center' - }, - formHeader: { - textAlign: 'center', - fontSize: 25 - }, - formTransfer: { - display: 'flex', - justifyContent: 'center' - }, - transferButton: { - marginLeft: 10, - color: 'green', - cursor: 'pointer' - } -})); - -const LoginPage: React.FC = () => { - const [error, setError] = useState<boolean>(false); - const [remember, setRemember] = useState<boolean>(true); - const classes = useStyles(); - const nameRef = useRef<HTMLInputElement>(); - const passwordRef = useRef<HTMLInputElement>(); - const { login } = useAuth(); - const history = useHistory(); - - const handleCheck = () => { - setRemember(!remember); - }; - - const handleSubmit = async () => { - const name = nameRef.current?.value?.toLowerCase(); - const password = passwordRef.current?.value; - if (name && password) { - login(name, password, remember).then(success => { - if (success) history.push(`/profile/${name}`); - else setError(true); - }); - } - }; - - const handleRegistration = () => { - history.push('/registration'); - }; - - return ( - <> - <div className={classes.formHeader}>Sign In</div> - <form className={classes.root} noValidate autoComplete="off"> - <TextField - inputRef={nameRef} - error={error} - label="Login" - /> - <TextField - inputRef={passwordRef} - error={error} - helperText={error && 'Invalid credentials'} - label="Password" - type="password" - /> - <FormControlLabel - control={<Switch color="primary" onClick={handleCheck} checked={remember} size="small" />} - label="Remember me" - /> - <Button variant="contained" onClick={handleSubmit}>submit</Button> - </form> - <div className={classes.formTransfer}> - <div>{'Don\'t have an account?'}</div> - <span - onClick={handleRegistration} - className={classes.transferButton} - role="presentation" - > - Sign up - </span> - </div> - </> - ); -}; - -export default LoginPage; - diff --git a/src/pages/NotificationsPage/NotificationsPage.tsx b/src/pages/NotificationsPage/NotificationsPage.tsx deleted file mode 100644 index 064fbd4..0000000 --- a/src/pages/NotificationsPage/NotificationsPage.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import React from 'react'; -import { makeStyles } from '@material-ui/core/styles'; -import { Typography } from '@material-ui/core'; - -const useStyles = makeStyles(theme => ({ - root: { - marginTop: theme.spacing(25), - textAlign: 'center' - } -})); - -const NotificationsPage: React.FC = () => { - const classes = useStyles(); - - return ( - <Typography variant="h4" className={classes.root}> - Sorry, this page is being constructed yet. - </Typography> - ); -}; - -export default NotificationsPage; - diff --git a/src/pages/Page.tsx b/src/pages/Page.tsx deleted file mode 100644 index a77a98e..0000000 --- a/src/pages/Page.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import React, { Suspense } from 'react'; -import { makeStyles, useTheme } from '@material-ui/core/styles'; -import { useMediaQuery } from '@material-ui/core'; -import { SnackbarProvider } from 'notistack'; -import { Switch, Route } from 'react-router-dom'; -import Loading from '../components/Loading/Loading'; - -const ProfilePage = React.lazy(() => import('./ProfilePage/ProfilePage')); -const FeedPage = React.lazy(() => import('./FeedPage/FeedPage')); -const LoginPage = React.lazy(() => import('./LoginPage/LoginPage')); -const RegistrationPage = React.lazy(() => import('./RegistrationPage/RegistrationPage')); -const HomePage = React.lazy(() => import('./HomePage/HomePage')); -const NotificationsPage = React.lazy(() => import('./NotificationsPage/NotificationsPage')); - - -const useStyles = makeStyles(theme => ({ - root: { - [theme.breakpoints.down('sm')]: { - margin: theme.spacing(2, 0, 12, 0) - }, - [theme.breakpoints.up('md')]: { - margin: theme.spacing(15, 5, 8, 5) - } - } -})); - - -const Page: React.FC = () => { - const classes = useStyles(); - const theme = useTheme(); - const isMobile = useMediaQuery(theme.breakpoints.down('sm')); - - return ( - <SnackbarProvider - maxSnack={3} - anchorOrigin={{ - vertical: isMobile ? 'top' : 'bottom', - horizontal: 'right' - }} - > - <div className={classes.root}> - <Suspense fallback={<Loading />}> - <Switch> - <Route exact path="/" component={HomePage} /> - <Route exact path="/login" component={LoginPage} /> - <Route exact path="/registration" component={RegistrationPage} /> - <Route exact path="/feed" component={FeedPage} /> - <Route exact path="/notifications" component={NotificationsPage} /> - <Route path="/profile/:username" component={ProfilePage} /> - </Switch> - </Suspense> - </div> - </SnackbarProvider> - ); -}; - - -export default Page; - diff --git a/src/pages/ProfilePage/Highlight.tsx b/src/pages/ProfilePage/Highlight.tsx deleted file mode 100644 index ebc3f56..0000000 --- a/src/pages/ProfilePage/Highlight.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import React from 'react'; -import { makeStyles } from '@material-ui/core/styles'; - -interface PropTypes { - text: string; - value: string | number; -} - -const useStyles = makeStyles({ - root: { - position: 'relative' - }, - menuButton: { - width: 200, - height: 50, - textAlign: 'center' - }, - menuNumber: { - fontWeight: 800, - color: 'black' - }, - menuText: { - color: 'darkgray' - } -}); - - -const Highlight: React.FC<PropTypes> = ({ text, value }) => { - const classes = useStyles(); - - return ( - <div className={classes.menuButton}> - <div className={classes.menuNumber}>{value}</div> - <div className={classes.menuText}>{text}</div> - </div> - ); -}; - -export default Highlight; diff --git a/src/pages/ProfilePage/MoreMenu.tsx b/src/pages/ProfilePage/MoreMenu.tsx deleted file mode 100644 index 1f41879..0000000 --- a/src/pages/ProfilePage/MoreMenu.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import React from 'react'; -import { useHistory } from 'react-router-dom'; -import IconButton from '@material-ui/core/IconButton'; -import Menu from '@material-ui/core/Menu'; -import MenuItem from '@material-ui/core/MenuItem'; -import MoreHorizIcon from '@material-ui/icons/MoreHoriz'; -import { makeStyles } from '@material-ui/core'; -import { useAuth } from '../../hooks/useAuth'; - -const ITEM_HEIGHT = 48; - -const useStyles = makeStyles({ - moreMenu: { - position: 'absolute', - right: 10, - zIndex: 100 - } -}); - -const MoreMenu: React.FC = () => { - const classes = useStyles(); - const [anchorEl, setAnchorEl] = React.useState<HTMLButtonElement | null>(null); - const { logout } = useAuth(); - const history = useHistory(); - - const open = Boolean(anchorEl); - - const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => { - setAnchorEl(event.currentTarget); - }; - - const handleLogout = () => { - logout(); - history.push('/login'); - }; - - const handleClose = () => { - setAnchorEl(null); - }; - - return ( - <div className={classes.moreMenu}> - <div> - <IconButton - aria-label="more" - aria-controls="long-menu" - aria-haspopup="true" - onClick={handleClick} - > - <MoreHorizIcon /> - </IconButton> - <Menu - id="long-menu" - anchorEl={anchorEl} - keepMounted - open={open} - onClose={handleClose} - PaperProps={{ - style: { - maxHeight: ITEM_HEIGHT * 4.5, - width: '20ch' - } - }} - > - <MenuItem onClick={handleLogout}>Log out</MenuItem> - </Menu> - </div> - </div> - ); -}; - -export default MoreMenu; diff --git a/src/pages/ProfilePage/ProfileInfo.tsx b/src/pages/ProfilePage/ProfileInfo.tsx deleted file mode 100644 index 9eee4c1..0000000 --- a/src/pages/ProfilePage/ProfileInfo.tsx +++ /dev/null @@ -1,166 +0,0 @@ -import React, { useState } from 'react'; -import { Avatar, Badge, Typography } from '@material-ui/core/'; -import { makeStyles } from '@material-ui/core/styles'; -import { User } from 'which-types'; -import CameraAltIcon from '@material-ui/icons/CameraAlt'; -import VerifiedIcon from '@material-ui/icons/CheckCircleOutline'; -import Skeleton from '@material-ui/lab/Skeleton'; -import MoreMenu from './MoreMenu'; -import Highlight from './Highlight'; -import UploadImage from '../../components/UploadImage/UploadImage'; -import { patch } from '../../requests'; -import { useAuth } from '../../hooks/useAuth'; - -interface PropTypes { - savedPolls: number; - totalVotes: number; - userInfo: User | undefined; - setUserInfo: (userInfo: User) => void; -} - -const useStyles = makeStyles(theme => ({ - root: { - position: 'relative' - }, - avatar: { - width: 150, - height: 150, - margin: '0 auto' - }, - name: { - margin: theme.spacing(1, 0), - display: 'flex', - alignItems: 'center', - justifyContent: 'center' - }, - verified: { - marginLeft: theme.spacing(0.5), - width: theme.spacing(3), - height: theme.spacing(3) - }, - profileMenu: { - display: 'flex', - width: '100%', - height: 50, - margin: '50px 0', - borderBottom: '1px solid lightgray' - }, - menuButton: { - width: 200, - height: 50, - textAlign: 'center' - }, - badge: { - width: theme.spacing(5), - height: theme.spacing(5), - borderRadius: '50%', - cursor: 'pointer', - background: '#d3d3d3', - display: 'flex', - alignItems: 'center', - '& svg': { - margin: '0 auto' - } - }, - avatarContainer: { - position: 'relative', - textAlign: 'center' - }, - menuNumber: { - fontWeight: 800, - color: 'black' - }, - menuText: { - color: 'darkgray' - }, - skeleton: { - margin: '10px auto', - borderRadius: 2 - } - -})); - - -const ProfileInfo: React.FC<PropTypes> = ({ - savedPolls, totalVotes, setUserInfo, userInfo -}) => { - const classes = useStyles(); - const [input, setInput] = useState(false); - const { user } = useAuth(); - const dateSince = new Date(userInfo?.createdAt || '').toLocaleDateString(); - - const handleClick = () => { - setInput(!input); - }; - - const patchAvatar = (url: string) => { - const id = user?._id; - patch(`/users/${id}`, { avatarUrl: url }).then(res => { - setUserInfo(res.data); - }); - }; - - return ( - <div className={classes.root}> - { - !userInfo - ? <Skeleton animation="wave" variant="circle" width={150} height={150} className={classes.avatar} /> - : userInfo?._id === user?._id - ? ( - <div> - <MoreMenu /> - <div className={classes.avatarContainer}> - <Badge - overlap="circle" - anchorOrigin={{ - vertical: 'bottom', - horizontal: 'right' - }} - badgeContent={( - <div className={classes.badge}> - <CameraAltIcon onClick={handleClick} /> - </div> - )} - > - <Avatar className={classes.avatar} src={userInfo?.avatarUrl} /> - </Badge> - </div> - <UploadImage isOpen={input} setIsOpen={setInput} callback={patchAvatar} /> - </div> - ) - : <Avatar className={classes.avatar} src={userInfo?.avatarUrl} /> - } - { - !userInfo - ? <Skeleton animation="wave" variant="rect" width={100} height={20} className={classes.skeleton} /> - : ( - <Typography variant="h5" className={classes.name}> - {userInfo?.username} - {userInfo?.verified && <VerifiedIcon className={classes.verified} color="primary" />} - </Typography> - ) - } - <div className={classes.profileMenu}> - { - !userInfo - ? ( - <> - <Skeleton animation="wave" variant="rect" width={170} height={20} className={classes.skeleton} /> - <Skeleton animation="wave" variant="rect" width={170} height={20} className={classes.skeleton} /> - <Skeleton animation="wave" variant="rect" width={170} height={20} className={classes.skeleton} /> - </> - ) - : ( - <> - <Highlight text="Polls" value={savedPolls} /> - <Highlight text="Since" value={dateSince} /> - <Highlight text="Total votes" value={totalVotes} /> - </> - ) - } - </div> - </div> - ); -}; - -export default ProfileInfo; diff --git a/src/pages/ProfilePage/ProfilePage.tsx b/src/pages/ProfilePage/ProfilePage.tsx deleted file mode 100644 index db27d25..0000000 --- a/src/pages/ProfilePage/ProfilePage.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import React, { useEffect, useCallback } from 'react'; -import { useHistory, useParams } from 'react-router-dom'; -import { Poll } from 'which-types'; -import { Container } from '@material-ui/core'; - -import ProfileInfo from './ProfileInfo'; -import Feed from '../../components/Feed/Feed'; -import Loading from '../../components/Loading/Loading'; -import { useAuth } from '../../hooks/useAuth'; -import { useUser, useProfile } from '../../hooks/APIClient'; - - -const ProfilePage: React.FC = () => { - const history = useHistory(); - const { username } = useParams(); - const { user } = useAuth(); - - const { data: userInfo, mutate: setUserInfo } = useUser(username); - const { data: polls, isValidating } = useProfile(userInfo?._id); - - useEffect(() => { - if (!username) { - if (user) history.push(`/profile/${user.username}`); - else history.push('/login'); - } - }, [username, history, user]); - - - const totalVotes = useCallback( - polls.reduce( - (total: number, current: Poll) => { - const { left, right } = current.contents; - return total + left.votes + right.votes; - }, 0 - ), - [polls] - ); - - return ( - <Container maxWidth="sm" disableGutters> - <ProfileInfo - userInfo={userInfo} - setUserInfo={setUserInfo} - savedPolls={polls.length} - totalVotes={totalVotes} - /> - {!polls.length && isValidating - ? <Loading /> - : <Feed polls={polls} /> - } - </Container> - ); -}; - -export default ProfilePage; diff --git a/src/pages/RegistrationPage/RegistrationPage.tsx b/src/pages/RegistrationPage/RegistrationPage.tsx deleted file mode 100644 index 18a9379..0000000 --- a/src/pages/RegistrationPage/RegistrationPage.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import React, { useState, useRef } from 'react'; -import { useHistory } from 'react-router-dom'; -import { makeStyles } from '@material-ui/core/styles'; -import TextField from '@material-ui/core/TextField'; -import Button from '@material-ui/core/Button'; -import { post } from '../../requests'; -import { useAuth } from '../../hooks/useAuth'; - - -const useStyles = makeStyles(theme => ({ - root: { - '& > *': { - margin: theme.spacing(1), - width: theme.spacing(35) - }, - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - textAlign: 'center' - }, - formHeader: { - textAlign: 'center', - fontSize: 25 - }, - formTransfer: { - display: 'flex', - justifyContent: 'center' - }, - transferButton: { - marginLeft: 10, - color: 'green', - cursor: 'pointer' - } -})); - -const RegistrationPage: React.FC = () => { - const [error, setError] = useState<boolean>(false); - const classes = useStyles(); - const usernameRef = useRef<HTMLInputElement>(); - const emailRef = useRef<HTMLInputElement>(); - const passwordRef = useRef<HTMLInputElement>(); - const { login } = useAuth(); - const history = useHistory(); - - const handleSubmit = () => { - const username = usernameRef.current?.value?.toLowerCase(); - const password = passwordRef.current?.value; - const email = emailRef.current?.value; - if (username && password) { - post('/users', { username, password, email }) - .then(() => login(username, password)) - .then(() => history.push(`/profile/${username}`)); - } else setError(true); - }; - - const handleLogin = () => { - history.push('/login'); - }; - - return ( - <> - <div className={classes.formHeader}>Sign Up</div> - <form className={classes.root} noValidate autoComplete="off"> - <TextField - inputRef={usernameRef} - label="Username" - error={error} - helperText={error && 'This field is required!'} - required - /> - <TextField inputRef={emailRef} label="Email" /> - <TextField - inputRef={passwordRef} - label="Password" - type="password" - required - error={error} - helperText={error && 'This field is required!'} - /> - <Button variant="contained" onClick={handleSubmit}>submit</Button> - </form> - <div className={classes.formTransfer}> - <div>Already have an account?</div> - <span - onClick={handleLogin} - className={classes.transferButton} - role="presentation" - > - Log in - </span> - </div> - </> - ); -}; - -export default RegistrationPage; |