From f97989967ee0b88a8c64f226a4b28a79eeef5fd2 Mon Sep 17 00:00:00 2001 From: eug-vs Date: Mon, 10 Aug 2020 11:19:53 +0300 Subject: refactor: remove "Page" from container names --- src/containers/Feed/Feed.tsx | 27 +++ src/containers/Feed/PollSubmission.tsx | 92 ++++++++++ src/containers/Feed/PollSubmissionImage.tsx | 87 +++++++++ src/containers/Feed/types.ts | 7 + src/containers/FeedPage/FeedPage.tsx | 27 --- src/containers/FeedPage/PollSubmission.tsx | 92 ---------- src/containers/FeedPage/PollSubmissionImage.tsx | 87 --------- src/containers/FeedPage/types.ts | 7 - src/containers/Home/Home.tsx | 203 +++++++++++++++++++++ src/containers/Home/ReviewForm.tsx | 74 ++++++++ src/containers/HomePage/HomePage.tsx | 203 --------------------- src/containers/HomePage/ReviewForm.tsx | 74 -------- src/containers/Login/Login.tsx | 103 +++++++++++ src/containers/LoginPage/LoginPage.tsx | 103 ----------- src/containers/Notifications/Notifications.tsx | 23 +++ .../NotificationsPage/NotificationsPage.tsx | 23 --- src/containers/Page/Page.tsx | 24 +-- src/containers/Profile/Highlight.tsx | 39 ++++ src/containers/Profile/MoreMenu.tsx | 72 ++++++++ src/containers/Profile/Profile.tsx | 56 ++++++ src/containers/Profile/ProfileInfo.tsx | 166 +++++++++++++++++ src/containers/ProfilePage/Highlight.tsx | 39 ---- src/containers/ProfilePage/MoreMenu.tsx | 72 -------- src/containers/ProfilePage/ProfileInfo.tsx | 166 ----------------- src/containers/ProfilePage/ProfilePage.tsx | 56 ------ src/containers/Registration/Registration.tsx | 96 ++++++++++ .../RegistrationPage/RegistrationPage.tsx | 96 ---------- 27 files changed, 1057 insertions(+), 1057 deletions(-) create mode 100644 src/containers/Feed/Feed.tsx create mode 100644 src/containers/Feed/PollSubmission.tsx create mode 100644 src/containers/Feed/PollSubmissionImage.tsx create mode 100644 src/containers/Feed/types.ts delete mode 100644 src/containers/FeedPage/FeedPage.tsx delete mode 100644 src/containers/FeedPage/PollSubmission.tsx delete mode 100644 src/containers/FeedPage/PollSubmissionImage.tsx delete mode 100644 src/containers/FeedPage/types.ts create mode 100644 src/containers/Home/Home.tsx create mode 100644 src/containers/Home/ReviewForm.tsx delete mode 100644 src/containers/HomePage/HomePage.tsx delete mode 100644 src/containers/HomePage/ReviewForm.tsx create mode 100644 src/containers/Login/Login.tsx delete mode 100644 src/containers/LoginPage/LoginPage.tsx create mode 100644 src/containers/Notifications/Notifications.tsx delete mode 100644 src/containers/NotificationsPage/NotificationsPage.tsx create mode 100644 src/containers/Profile/Highlight.tsx create mode 100644 src/containers/Profile/MoreMenu.tsx create mode 100644 src/containers/Profile/Profile.tsx create mode 100644 src/containers/Profile/ProfileInfo.tsx delete mode 100644 src/containers/ProfilePage/Highlight.tsx delete mode 100644 src/containers/ProfilePage/MoreMenu.tsx delete mode 100644 src/containers/ProfilePage/ProfileInfo.tsx delete mode 100644 src/containers/ProfilePage/ProfilePage.tsx create mode 100644 src/containers/Registration/Registration.tsx delete mode 100644 src/containers/RegistrationPage/RegistrationPage.tsx diff --git a/src/containers/Feed/Feed.tsx b/src/containers/Feed/Feed.tsx new file mode 100644 index 0000000..e923bdd --- /dev/null +++ b/src/containers/Feed/Feed.tsx @@ -0,0 +1,27 @@ +import React from 'react'; +import { Poll } from 'which-types'; +import { Container } from '@material-ui/core/'; + +import PollsList from '../../components/PollsList/PollsList'; +import PollSubmission from './PollSubmission'; +import { useAuth } from '../../hooks/useAuth'; +import { useFeed } from '../../hooks/APIClient'; + +const Feed: React.FC = () => { + const { data: polls, mutate } = useFeed(); + const { isAuthenticated } = useAuth(); + + const addPoll = (poll: Poll): void => { + mutate([poll, ...polls], true); + }; + + return ( + + {isAuthenticated && } + + + ); +}; + +export default Feed; + diff --git a/src/containers/Feed/PollSubmission.tsx b/src/containers/Feed/PollSubmission.tsx new file mode 100644 index 0000000..347eecc --- /dev/null +++ b/src/containers/Feed/PollSubmission.tsx @@ -0,0 +1,92 @@ +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 = ({ addPoll }) => { + const classes = useStyles(); + const [expanded, setExpanded] = useState(false); + const [contents, setContents] = useState(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 ( + + + + {user && } + +
+ + +
+
+ +
+
+ ); +}; + +export default PollSubmission; diff --git a/src/containers/Feed/PollSubmissionImage.tsx b/src/containers/Feed/PollSubmissionImage.tsx new file mode 100644 index 0000000..8835989 --- /dev/null +++ b/src/containers/Feed/PollSubmissionImage.tsx @@ -0,0 +1,87 @@ +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 = ({ 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 = ( + <> + + Upload an image + + ); + + const Media = ( + + {isMediaHover && } + + ); + + return ( + <> + + {url ? Media : Upload} + + + + ); +}; + +export default PollSubmissionImage; diff --git a/src/containers/Feed/types.ts b/src/containers/Feed/types.ts new file mode 100644 index 0000000..24ace4e --- /dev/null +++ b/src/containers/Feed/types.ts @@ -0,0 +1,7 @@ +export interface ImageData { + url: string; +} +export interface Contents { + left: ImageData; + right: ImageData; +} diff --git a/src/containers/FeedPage/FeedPage.tsx b/src/containers/FeedPage/FeedPage.tsx deleted file mode 100644 index 7445c25..0000000 --- a/src/containers/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 PollsList from '../../components/PollsList/PollsList'; -import PollSubmission from './PollSubmission'; -import { useAuth } from '../../hooks/useAuth'; -import { useFeed } from '../../hooks/APIClient'; - -const FeedPage: React.FC = () => { - const { data: polls, mutate } = useFeed(); - const { isAuthenticated } = useAuth(); - - const addPoll = (poll: Poll): void => { - mutate([poll, ...polls], true); - }; - - return ( - - {isAuthenticated && } - - - ); -}; - -export default FeedPage; - diff --git a/src/containers/FeedPage/PollSubmission.tsx b/src/containers/FeedPage/PollSubmission.tsx deleted file mode 100644 index 347eecc..0000000 --- a/src/containers/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 = ({ addPoll }) => { - const classes = useStyles(); - const [expanded, setExpanded] = useState(false); - const [contents, setContents] = useState(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 ( - - - - {user && } - -
- - -
-
- -
-
- ); -}; - -export default PollSubmission; diff --git a/src/containers/FeedPage/PollSubmissionImage.tsx b/src/containers/FeedPage/PollSubmissionImage.tsx deleted file mode 100644 index 8835989..0000000 --- a/src/containers/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 = ({ 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 = ( - <> - - Upload an image - - ); - - const Media = ( - - {isMediaHover && } - - ); - - return ( - <> - - {url ? Media : Upload} - - - - ); -}; - -export default PollSubmissionImage; diff --git a/src/containers/FeedPage/types.ts b/src/containers/FeedPage/types.ts deleted file mode 100644 index 24ace4e..0000000 --- a/src/containers/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/containers/Home/Home.tsx b/src/containers/Home/Home.tsx new file mode 100644 index 0000000..73fa479 --- /dev/null +++ b/src/containers/Home/Home.tsx @@ -0,0 +1,203 @@ +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 Home: React.FC = () => { + const [feedbacks, setFeedbacks] = useState([]); + 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 = GitHub; + const TypescriptLink = Typescript; + const ReactLink = React; + const FeathersLink = Feathers; + const MUILink = Material-UI; + const EmailLink = eug-vs@keemail.me; + + const Reviews = ( +
+ {feedbacks.map(feedback => )} +
+ ); + + const FeedbackSection = feedbacks.findIndex((feedback: Feedback) => feedback.author._id === user?._id) >= 0 ? ( +

+ 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. +

+ ) : ( + <> +

+ 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). +

+ {isAuthenticated ? : ( + <> +

You must be authorized to leave feedback.

+ + + )} + + ); + + return ( +
+ + + + + logo + + + {rating !== 0 && } + + + {rating !== 0 && ( + + User score: {rating.toFixed(1)} + + )} + + + {isMobile || Reviews} + + + + + Which one to choose? + + +

+ 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! +

+

Share your minor everyday uncertainties with the whole world and see what others think!

+ + {!isAuthenticated && ( + + )} +
+
+ + About the project + + +

+ 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)! +

+

+ We encourage any developer to check it out. Feel free to open issues and create Pull Requests! +

+

+ 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. +

+ +
+
+ + Leave feedback + + + {FeedbackSection} + + + {isMobile && ( + + {Reviews} + + )} +
+
+
+
+ ); +}; + +export default Home; + diff --git a/src/containers/Home/ReviewForm.tsx b/src/containers/Home/ReviewForm.tsx new file mode 100644 index 0000000..b626ce2 --- /dev/null +++ b/src/containers/Home/ReviewForm.tsx @@ -0,0 +1,74 @@ +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(''); + const [score, setScore] = useState(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): void => { + setContents(event.target?.value || ''); + }; + + const handleChangeRating = (event: React.ChangeEvent>, newScore: number | null): void => { + setScore(newScore || 0); + }; + + return ( +
+ + +
+ +
+
+ ); +}; + +export default ReviewForm; diff --git a/src/containers/HomePage/HomePage.tsx b/src/containers/HomePage/HomePage.tsx deleted file mode 100644 index b1dc506..0000000 --- a/src/containers/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([]); - 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 = GitHub; - const TypescriptLink = Typescript; - const ReactLink = React; - const FeathersLink = Feathers; - const MUILink = Material-UI; - const EmailLink = eug-vs@keemail.me; - - const Reviews = ( -
- {feedbacks.map(feedback => )} -
- ); - - const FeedbackSection = feedbacks.findIndex((feedback: Feedback) => feedback.author._id === user?._id) >= 0 ? ( -

- 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. -

- ) : ( - <> -

- 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). -

- {isAuthenticated ? : ( - <> -

You must be authorized to leave feedback.

- - - )} - - ); - - return ( -
- - - - - logo - - - {rating !== 0 && } - - - {rating !== 0 && ( - - User score: {rating.toFixed(1)} - - )} - - - {isMobile || Reviews} - - - - - Which one to choose? - - -

- 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! -

-

Share your minor everyday uncertainties with the whole world and see what others think!

- - {!isAuthenticated && ( - - )} -
-
- - About the project - - -

- 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)! -

-

- We encourage any developer to check it out. Feel free to open issues and create Pull Requests! -

-

- 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. -

- -
-
- - Leave feedback - - - {FeedbackSection} - - - {isMobile && ( - - {Reviews} - - )} -
-
-
-
- ); -}; - -export default HomePage; - diff --git a/src/containers/HomePage/ReviewForm.tsx b/src/containers/HomePage/ReviewForm.tsx deleted file mode 100644 index b626ce2..0000000 --- a/src/containers/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(''); - const [score, setScore] = useState(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): void => { - setContents(event.target?.value || ''); - }; - - const handleChangeRating = (event: React.ChangeEvent>, newScore: number | null): void => { - setScore(newScore || 0); - }; - - return ( -
- - -
- -
-
- ); -}; - -export default ReviewForm; diff --git a/src/containers/Login/Login.tsx b/src/containers/Login/Login.tsx new file mode 100644 index 0000000..bec0db5 --- /dev/null +++ b/src/containers/Login/Login.tsx @@ -0,0 +1,103 @@ +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 Login: React.FC = () => { + const [error, setError] = useState(false); + const [remember, setRemember] = useState(true); + const classes = useStyles(); + const nameRef = useRef(); + const passwordRef = useRef(); + 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 ( + <> +
Sign In
+
+ + + } + label="Remember me" + /> + + +
+
{'Don\'t have an account?'}
+ + Sign up + +
+ + ); +}; + +export default Login; + diff --git a/src/containers/LoginPage/LoginPage.tsx b/src/containers/LoginPage/LoginPage.tsx deleted file mode 100644 index 335cbb1..0000000 --- a/src/containers/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(false); - const [remember, setRemember] = useState(true); - const classes = useStyles(); - const nameRef = useRef(); - const passwordRef = useRef(); - 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 ( - <> -
Sign In
-
- - - } - label="Remember me" - /> - - -
-
{'Don\'t have an account?'}
- - Sign up - -
- - ); -}; - -export default LoginPage; - diff --git a/src/containers/Notifications/Notifications.tsx b/src/containers/Notifications/Notifications.tsx new file mode 100644 index 0000000..0648eb5 --- /dev/null +++ b/src/containers/Notifications/Notifications.tsx @@ -0,0 +1,23 @@ +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 Notifications: React.FC = () => { + const classes = useStyles(); + + return ( + + Sorry, this page is being constructed yet. + + ); +}; + +export default Notifications; + diff --git a/src/containers/NotificationsPage/NotificationsPage.tsx b/src/containers/NotificationsPage/NotificationsPage.tsx deleted file mode 100644 index 064fbd4..0000000 --- a/src/containers/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 ( - - Sorry, this page is being constructed yet. - - ); -}; - -export default NotificationsPage; - diff --git a/src/containers/Page/Page.tsx b/src/containers/Page/Page.tsx index 8a39636..df27b74 100644 --- a/src/containers/Page/Page.tsx +++ b/src/containers/Page/Page.tsx @@ -5,12 +5,12 @@ 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 Profile = React.lazy(() => import('../Profile/Profile')); +const Feed = React.lazy(() => import('../Feed/Feed')); +const Login = React.lazy(() => import('../Login/Login')); +const Registration = React.lazy(() => import('../Registration/Registration')); +const Home = React.lazy(() => import('../Home/Home')); +const Notifications = React.lazy(() => import('../Notifications/Notifications')); const useStyles = makeStyles(theme => ({ @@ -41,12 +41,12 @@ const Page: React.FC = () => {
}> - - - - - - + + + + + +
diff --git a/src/containers/Profile/Highlight.tsx b/src/containers/Profile/Highlight.tsx new file mode 100644 index 0000000..ebc3f56 --- /dev/null +++ b/src/containers/Profile/Highlight.tsx @@ -0,0 +1,39 @@ +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 = ({ text, value }) => { + const classes = useStyles(); + + return ( +
+
{value}
+
{text}
+
+ ); +}; + +export default Highlight; diff --git a/src/containers/Profile/MoreMenu.tsx b/src/containers/Profile/MoreMenu.tsx new file mode 100644 index 0000000..1f41879 --- /dev/null +++ b/src/containers/Profile/MoreMenu.tsx @@ -0,0 +1,72 @@ +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(null); + const { logout } = useAuth(); + const history = useHistory(); + + const open = Boolean(anchorEl); + + const handleClick = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + + const handleLogout = () => { + logout(); + history.push('/login'); + }; + + const handleClose = () => { + setAnchorEl(null); + }; + + return ( +
+
+ + + + + Log out + +
+
+ ); +}; + +export default MoreMenu; diff --git a/src/containers/Profile/Profile.tsx b/src/containers/Profile/Profile.tsx new file mode 100644 index 0000000..af52e5e --- /dev/null +++ b/src/containers/Profile/Profile.tsx @@ -0,0 +1,56 @@ +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 PollsList from '../../components/PollsList/PollsList'; +import Loading from '../../components/Loading/Loading'; +import { useAuth } from '../../hooks/useAuth'; +import { useUser, useProfile } from '../../hooks/APIClient'; + + +const Profile: React.FC = () => { + const history = useHistory(); + const { username } = useParams(); + const { user } = useAuth(); + + const { data: userInfo, mutate: setUserInfo } = useUser(username); + const { data: polls, mutate: mutatePolls, 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 ( + + + { + isValidating && !polls + ? + : + } + + ); +}; + +export default Profile; diff --git a/src/containers/Profile/ProfileInfo.tsx b/src/containers/Profile/ProfileInfo.tsx new file mode 100644 index 0000000..9eee4c1 --- /dev/null +++ b/src/containers/Profile/ProfileInfo.tsx @@ -0,0 +1,166 @@ +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 = ({ + 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 ( +
+ { + !userInfo + ? + : userInfo?._id === user?._id + ? ( +
+ +
+ + +
+ )} + > + + +
+ +
+ ) + : + } + { + !userInfo + ? + : ( + + {userInfo?.username} + {userInfo?.verified && } + + ) + } +
+ { + !userInfo + ? ( + <> + + + + + ) + : ( + <> + + + + + ) + } +
+ + ); +}; + +export default ProfileInfo; diff --git a/src/containers/ProfilePage/Highlight.tsx b/src/containers/ProfilePage/Highlight.tsx deleted file mode 100644 index ebc3f56..0000000 --- a/src/containers/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 = ({ text, value }) => { - const classes = useStyles(); - - return ( -
-
{value}
-
{text}
-
- ); -}; - -export default Highlight; diff --git a/src/containers/ProfilePage/MoreMenu.tsx b/src/containers/ProfilePage/MoreMenu.tsx deleted file mode 100644 index 1f41879..0000000 --- a/src/containers/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(null); - const { logout } = useAuth(); - const history = useHistory(); - - const open = Boolean(anchorEl); - - const handleClick = (event: React.MouseEvent) => { - setAnchorEl(event.currentTarget); - }; - - const handleLogout = () => { - logout(); - history.push('/login'); - }; - - const handleClose = () => { - setAnchorEl(null); - }; - - return ( -
-
- - - - - Log out - -
-
- ); -}; - -export default MoreMenu; diff --git a/src/containers/ProfilePage/ProfileInfo.tsx b/src/containers/ProfilePage/ProfileInfo.tsx deleted file mode 100644 index 9eee4c1..0000000 --- a/src/containers/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 = ({ - 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 ( -
- { - !userInfo - ? - : userInfo?._id === user?._id - ? ( -
- -
- - -
- )} - > - - -
- -
- ) - : - } - { - !userInfo - ? - : ( - - {userInfo?.username} - {userInfo?.verified && } - - ) - } -
- { - !userInfo - ? ( - <> - - - - - ) - : ( - <> - - - - - ) - } -
- - ); -}; - -export default ProfileInfo; diff --git a/src/containers/ProfilePage/ProfilePage.tsx b/src/containers/ProfilePage/ProfilePage.tsx deleted file mode 100644 index 7a1d729..0000000 --- a/src/containers/ProfilePage/ProfilePage.tsx +++ /dev/null @@ -1,56 +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 PollsList from '../../components/PollsList/PollsList'; -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, mutate: mutatePolls, 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 ( - - - { - isValidating && !polls - ? - : - } - - ); -}; - -export default ProfilePage; diff --git a/src/containers/Registration/Registration.tsx b/src/containers/Registration/Registration.tsx new file mode 100644 index 0000000..c681329 --- /dev/null +++ b/src/containers/Registration/Registration.tsx @@ -0,0 +1,96 @@ +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 Registration: React.FC = () => { + const [error, setError] = useState(false); + const classes = useStyles(); + const usernameRef = useRef(); + const emailRef = useRef(); + const passwordRef = useRef(); + 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 ( + <> +
Sign Up
+
+ + + + + +
+
Already have an account?
+ + Log in + +
+ + ); +}; + +export default Registration; diff --git a/src/containers/RegistrationPage/RegistrationPage.tsx b/src/containers/RegistrationPage/RegistrationPage.tsx deleted file mode 100644 index 18a9379..0000000 --- a/src/containers/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(false); - const classes = useStyles(); - const usernameRef = useRef(); - const emailRef = useRef(); - const passwordRef = useRef(); - 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 ( - <> -
Sign Up
-
- - - - - -
-
Already have an account?
- - Log in - -
- - ); -}; - -export default RegistrationPage; -- cgit v1.2.3