aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/components/Header/Header.tsx2
-rw-r--r--src/components/PollCard/PollCard.tsx12
-rw-r--r--src/components/UploadImage/UploadImage.tsx42
-rw-r--r--src/index.tsx2
-rw-r--r--src/pages/FeedPage/FeedPage.tsx20
-rw-r--r--src/pages/FeedPage/PollSubmission.tsx83
-rw-r--r--src/pages/FeedPage/PollSubmissionImage.tsx80
-rw-r--r--src/pages/FeedPage/types.ts7
-rw-r--r--src/pages/ProfilePage/ProfileInfo.tsx15
-rw-r--r--src/types.d.ts1
10 files changed, 229 insertions, 35 deletions
diff --git a/src/components/Header/Header.tsx b/src/components/Header/Header.tsx
index 1825647..d0d9081 100644
--- a/src/components/Header/Header.tsx
+++ b/src/components/Header/Header.tsx
@@ -62,7 +62,7 @@ const Header: React.FC<PropTypes> = ({ navigate, userImage }) => {
</IconButton>
<IconButton onClick={handleProfile}>
{
- userImage !== undefined
+ userImage?.match(/\.(jpeg|jpg|gif|png)$/)
? <Avatar className={classes.avatar} src={userImage} />
: <AccountCircle />
}
diff --git a/src/components/PollCard/PollCard.tsx b/src/components/PollCard/PollCard.tsx
index caa2de1..156315a 100644
--- a/src/components/PollCard/PollCard.tsx
+++ b/src/components/PollCard/PollCard.tsx
@@ -72,8 +72,16 @@ const PollCard: React.FC<PropTypes> = ({ initialPoll, navigate }) => {
const handleLeft = () => handleVote('left');
const handleRight = () => handleVote('right');
- const leftPercentage = Math.round(100 * (left.votes / (left.votes + right.votes)));
- const rightPercentage = 100 - leftPercentage;
+ let leftPercentage;
+ let rightPercentage;
+
+ if (left.votes || right.votes) {
+ leftPercentage = Math.round(100 * (left.votes / (left.votes + right.votes)));
+ rightPercentage = 100 - leftPercentage;
+ } else {
+ leftPercentage = 0;
+ rightPercentage = 0;
+ }
const dominant: Which = left.votes >= right.votes ? 'left' : 'right';
diff --git a/src/components/UploadImage/UploadImage.tsx b/src/components/UploadImage/UploadImage.tsx
index 42ee989..e6f6d05 100644
--- a/src/components/UploadImage/UploadImage.tsx
+++ b/src/components/UploadImage/UploadImage.tsx
@@ -1,4 +1,4 @@
-import React, { useRef } from 'react';
+import React, { useState } from 'react';
import Button from '@material-ui/core/Button';
import TextField from '@material-ui/core/TextField';
import Dialog from '@material-ui/core/Dialog';
@@ -6,39 +6,33 @@ import DialogActions from '@material-ui/core/DialogActions';
import DialogContent from '@material-ui/core/DialogContent';
import DialogContentText from '@material-ui/core/DialogContentText';
import DialogTitle from '@material-ui/core/DialogTitle';
-import { User } from 'which-types';
-import { patch } from '../../requests';
interface PropTypes {
- displayD: boolean;
- setDisplayD: (d: boolean) => void;
- setUserInfo: (a: User) => void;
- setUser: (a: User) => void
+ isOpen: boolean;
+ setIsOpen: (value: boolean) => void;
+ callback: (url: string) => void;
}
-const UploadImage: React.FC<PropTypes> = ({
- displayD, setDisplayD, setUserInfo, setUser
-}) => {
- const urlRef = useRef<HTMLInputElement>(null);
+const UploadImage: React.FC<PropTypes> = ({ setIsOpen, isOpen, callback }) => {
+ const [url, setUrl] = useState('');
const handleClose = () => {
- setDisplayD(false);
+ setIsOpen(false);
};
- const updateAvatar = () => {
- const id = localStorage.getItem('userId');
- const newAvatar = urlRef.current?.value;
- patch(`/users/${id}`, { avatarUrl: newAvatar }).then(res => {
- setUserInfo(res.data);
- setUser(res.data);
- });
- setDisplayD(false);
+ const handleSubmit = () => {
+ callback(url || '');
+ setIsOpen(false);
+ };
+
+ const handleChange = (event:React.ChangeEvent<HTMLInputElement>) => {
+ setUrl(event.target.value);
};
return (
<div>
- <Dialog open={displayD} onClose={handleClose}>
- <DialogTitle id="form-dialog-title">Upload an Image</DialogTitle>
+ <Dialog open={isOpen} onClose={handleClose}>
+ <DialogTitle>Upload an Image</DialogTitle>
<DialogContent>
<DialogContentText>
Unfortunetly we do not support uploading images yet. Please provide a valid URL to your image.
@@ -51,14 +45,14 @@ const UploadImage: React.FC<PropTypes> = ({
type="text"
fullWidth
autoComplete="off"
- inputRef={urlRef}
+ onChange={handleChange}
/>
</DialogContent>
<DialogActions>
<Button onClick={handleClose} color="primary">
Cancel
</Button>
- <Button onClick={updateAvatar} color="primary">
+ <Button onClick={handleSubmit} color="primary" disabled={!url.length}>
Submit
</Button>
</DialogActions>
diff --git a/src/index.tsx b/src/index.tsx
index 49c177b..02f7969 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -104,7 +104,7 @@ const App: React.FC = () => {
setUser={setUser}
/>
) }
- { page.prefix === 'feed' && <FeedPage navigate={navigate} /> }
+ { page.prefix === 'feed' && <FeedPage navigate={navigate} user={user} /> }
{ page.prefix === 'auth' && <AuthPage logIn={logIn} /> }
</div>
<ScrollTopArrow />
diff --git a/src/pages/FeedPage/FeedPage.tsx b/src/pages/FeedPage/FeedPage.tsx
index b7d719e..0017275 100644
--- a/src/pages/FeedPage/FeedPage.tsx
+++ b/src/pages/FeedPage/FeedPage.tsx
@@ -1,14 +1,17 @@
import React, { useState, useEffect } from 'react';
-import { Poll } from 'which-types';
+import { Poll, User } from 'which-types';
import Feed from '../../components/Feed/Feed';
import { get } from '../../requests';
+import PollSubmission from './PollSubmission';
+
interface PropTypes {
navigate: (prefix: string, id: string) => void;
+ user: User | undefined;
}
-const FeedPage: React.FC<PropTypes> = ({ navigate }) => {
+const FeedPage: React.FC<PropTypes> = ({ navigate, user }) => {
const [polls, setPolls] = useState<Poll[]>([]);
useEffect(() => {
@@ -17,7 +20,18 @@ const FeedPage: React.FC<PropTypes> = ({ navigate }) => {
});
}, []);
- return <Feed polls={polls} navigate={navigate} />;
+ const addPoll = (poll: Poll): void => {
+ polls.unshift(poll);
+ setPolls([...polls]);
+ };
+
+
+ return (
+ <>
+ {user && <PollSubmission user={user} addPoll={addPoll} />}
+ <Feed polls={polls} navigate={navigate} />
+ </>
+ );
};
export default FeedPage;
diff --git a/src/pages/FeedPage/PollSubmission.tsx b/src/pages/FeedPage/PollSubmission.tsx
new file mode 100644
index 0000000..16c8350
--- /dev/null
+++ b/src/pages/FeedPage/PollSubmission.tsx
@@ -0,0 +1,83 @@
+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 { User, Poll, Which } from 'which-types';
+import PollSubmissionImage from './PollSubmissionImage';
+import UserStrip from '../../components/UserStrip/UserStrip';
+import { post } from '../../requests';
+import { Contents } from './types';
+
+interface PropTypes{
+ user: User;
+ addPoll: (poll: Poll) => void;
+}
+
+const useStyles = makeStyles(theme => ({
+ root: {
+ height: theme.spacing(50),
+ display: 'flex'
+ }
+}));
+
+const emptyContents: Contents = {
+ left: { url: '' },
+ right: { url: '' }
+};
+
+const PollSubmission: React.FC<PropTypes> = ({ user, addPoll }) => {
+ const classes = useStyles();
+ const [expanded, setExpanded] = useState(false);
+ const [contents, setContents] = useState<Contents>(emptyContents);
+
+ 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/', { authorId: user._id, contents }).then(response => {
+ addPoll(response.data);
+ });
+ setContents({ ...emptyContents });
+ }
+ setExpanded(!expanded);
+ };
+
+ return (
+ <ClickAwayListener onClickAway={handleClickAway}>
+ <Card>
+ <Collapse in={expanded} timeout="auto" unmountOnExit>
+ <UserStrip user={user} info="" navigate={() => {}} />
+ <Divider />
+ <div className={classes.root}>
+ <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
new file mode 100644
index 0000000..1e9fa0e
--- /dev/null
+++ b/src/pages/FeedPage/PollSubmissionImage.tsx
@@ -0,0 +1,80 @@
+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'
+ }
+});
+
+
+const PollSubmissionImage: React.FC<PropTypes> = ({ url, setUrl }) => {
+ const classes = useStyles();
+ const [isModalOpen, setIsModalOpen] = useState(false);
+ const [isMediaHover, setIsMediaHover] = useState(false);
+
+ const handleClick = (): void => {
+ 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"> Upload an image </Typography>
+ <UploadImage isOpen={isModalOpen} setIsOpen={setIsModalOpen} callback={setUrl} />
+ </>
+ );
+
+ 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>
+ );
+};
+
+export default PollSubmissionImage;
diff --git a/src/pages/FeedPage/types.ts b/src/pages/FeedPage/types.ts
new file mode 100644
index 0000000..24ace4e
--- /dev/null
+++ b/src/pages/FeedPage/types.ts
@@ -0,0 +1,7 @@
+export interface ImageData {
+ url: string;
+}
+export interface Contents {
+ left: ImageData;
+ right: ImageData;
+}
diff --git a/src/pages/ProfilePage/ProfileInfo.tsx b/src/pages/ProfilePage/ProfileInfo.tsx
index 2b9227e..9fe5912 100644
--- a/src/pages/ProfilePage/ProfileInfo.tsx
+++ b/src/pages/ProfilePage/ProfileInfo.tsx
@@ -7,6 +7,7 @@ import VerifiedIcon from '@material-ui/icons/CheckCircleOutline';
import MoreMenu from './MoreMenu';
import Highlight from './Highlight';
import UploadImage from '../../components/UploadImage/UploadImage';
+import { patch } from '../../requests';
interface PropTypes {
@@ -82,12 +83,21 @@ const ProfileInfo: React.FC<PropTypes> = ({
const classes = useStyles();
const [input, setInput] = useState(false);
+
const dateSince = new Date(user?.createdAt || '').toLocaleDateString();
const handleClick = () => {
setInput(!input);
};
+ const patchAvatar = (url: string) => {
+ const id = localStorage.getItem('userId');
+ patch(`/users/${id}`, { avatarUrl: url }).then(res => {
+ setUserInfo(res.data);
+ setUser(res.data);
+ });
+ };
+
return (
<div className={classes.root}>
{
@@ -97,7 +107,6 @@ const ProfileInfo: React.FC<PropTypes> = ({
<MoreMenu logOut={logOut} />
<div className={classes.avatarContainer}>
<Badge
- onClick={handleClick}
overlap="circle"
anchorOrigin={{
vertical: 'bottom',
@@ -105,14 +114,14 @@ const ProfileInfo: React.FC<PropTypes> = ({
}}
badgeContent={(
<div className={classes.badge}>
- <CameraAltIcon />
+ <CameraAltIcon onClick={handleClick} />
</div>
)}
>
<Avatar className={classes.avatar} src={user?.avatarUrl} />
</Badge>
</div>
- <UploadImage displayD={input} setDisplayD={setInput} setUserInfo={setUserInfo} setUser={setUser} />
+ <UploadImage isOpen={input} setIsOpen={setInput} callback={patchAvatar} />
</div>
)
: <Avatar className={classes.avatar} src={user?.avatarUrl} />
diff --git a/src/types.d.ts b/src/types.d.ts
index 73346ce..4b1ffd6 100644
--- a/src/types.d.ts
+++ b/src/types.d.ts
@@ -2,4 +2,3 @@ export interface Page {
prefix: string;
id: string;
}
-