diff options
| author | Eugene Sokolov <eug-vs@keemail.me> | 2020-10-08 20:40:45 +0300 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-10-08 20:40:45 +0300 | 
| commit | bdb4d194307c9755c2083c1a11bb876abebcb6de (patch) | |
| tree | 9aff25b0bd0b47127b66e4db4818468ec70719bf | |
| parent | 45b4094c02301ff854b8b8017437ad9989efa117 (diff) | |
| parent | 2c093ce738cb1281db04a8a3f2b6a35b3aa9b354 (diff) | |
| download | which-ui-bdb4d194307c9755c2083c1a11bb876abebcb6de.tar.gz | |
Merge pull request #102 from which-ecosystem/feat/modal
Modal improvements
| -rw-r--r-- | package-lock.json | 8 | ||||
| -rw-r--r-- | package.json | 1 | ||||
| -rw-r--r-- | src/components/AttachLink/AttachLink.tsx | 2 | ||||
| -rw-r--r-- | src/components/Message/Message.tsx | 9 | ||||
| -rw-r--r-- | src/components/ModalScreen/ModalScreen.tsx | 20 | ||||
| -rw-r--r-- | src/containers/Page/Page.tsx | 8 | ||||
| -rw-r--r-- | src/containers/Page/Router.tsx | 2 | ||||
| -rw-r--r-- | src/containers/PollCreation/ImageInput.tsx | 46 | ||||
| -rw-r--r-- | src/containers/PollCreation/PollCreation.tsx | 52 | ||||
| -rw-r--r-- | src/hooks/APIClient.ts | 4 | 
10 files changed, 86 insertions, 66 deletions
| diff --git a/package-lock.json b/package-lock.json index 03961ee..e25fb85 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6396,14 +6396,6 @@        "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz",        "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ=="      }, -    "history": { -      "version": "5.0.0", -      "resolved": "https://registry.npmjs.org/history/-/history-5.0.0.tgz", -      "integrity": "sha512-3NyRMKIiFSJmIPdq7FxkNMJkQ7ZEtVblOQ38VtKaA0zZMW1Eo6Q6W8oDKEflr1kNNTItSnk4JMCO1deeSgbLLg==", -      "requires": { -        "@babel/runtime": "^7.7.6" -      } -    },      "hmac-drbg": {        "version": "1.0.1",        "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", diff --git a/package.json b/package.json index f45d4e7..2bb2500 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,6 @@      "bluebird": "^3.7.2",      "compressorjs": "^1.0.6",      "formik": "^2.1.5", -    "history": "^5.0.0",      "lodash": "^4.17.15",      "notistack": "^0.9.17",      "react": "^16.13.1", diff --git a/src/components/AttachLink/AttachLink.tsx b/src/components/AttachLink/AttachLink.tsx index 742ba65..7c32635 100644 --- a/src/components/AttachLink/AttachLink.tsx +++ b/src/components/AttachLink/AttachLink.tsx @@ -21,7 +21,7 @@ const AttachLink: React.FC<PropTypes> = ({ callback, children }) => {        color="primary"        startIcon={<LinkIcon />}      > -      Attach a link +      Link      </Button>    ); diff --git a/src/components/Message/Message.tsx b/src/components/Message/Message.tsx index f568552..e5191ed 100644 --- a/src/components/Message/Message.tsx +++ b/src/components/Message/Message.tsx @@ -6,6 +6,7 @@ import { makeStyles } from '@material-ui/core/styles';  interface PropTypes {    tagline?: string;    message?: string; +  noMargin?: boolean;  }  const useStyles = makeStyles(theme => ({ @@ -13,7 +14,9 @@ const useStyles = makeStyles(theme => ({      display: 'flex',      flexDirection: 'column',      justifyContent: 'center', -    alignItems: 'center', +    alignItems: 'center' +  }, +  margin: {      marginTop: theme.spacing(6)    },    content: { @@ -21,11 +24,11 @@ const useStyles = makeStyles(theme => ({    }  })); -const Message: React.FC<PropTypes> = React.memo(({ tagline, message, children }) => { +const Message: React.FC<PropTypes> = React.memo(({ tagline, message, noMargin, children }) => {    const classes = useStyles();    return ( -    <div className={classes.root}> +    <div className={`${classes.root} ${!noMargin && classes.margin}`}>        <div className={classes.content}>          {children}        </div> diff --git a/src/components/ModalScreen/ModalScreen.tsx b/src/components/ModalScreen/ModalScreen.tsx index 81e5c5a..c6f0565 100644 --- a/src/components/ModalScreen/ModalScreen.tsx +++ b/src/components/ModalScreen/ModalScreen.tsx @@ -12,11 +12,14 @@ import {    useTheme  } from '@material-ui/core';  import { makeStyles } from '@material-ui/core/styles'; -import CloseIcon from '@material-ui/icons/Close';  import { TransitionProps } from '@material-ui/core/transitions'; +import CloseIcon from '@material-ui/icons/Close';  interface PropTypes {    title: string; +  actionIcon?: JSX.Element; +  handleAction?: () => void; +  isActionDisabled?: boolean;  }  const useStyles = makeStyles(theme => ({ @@ -24,7 +27,7 @@ const useStyles = makeStyles(theme => ({      backgroundColor: theme.palette.background.default    },    content: { -    padding: theme.spacing(3, 0, 0, 0) +    padding: theme.spacing(6, 0)    },    toolbar: {      display: 'flex', @@ -37,7 +40,7 @@ const Transition = React.forwardRef((    ref: React.Ref<unknown>  ) => <Slide direction="left" ref={ref} {...props} />); -const ModalScreen: React.FC<PropTypes> = ({ title, children }) => { +const ModalScreen: React.FC<PropTypes> = ({ title, actionIcon, handleAction, isActionDisabled, children }) => {    const [isOpen, setIsOpen] = useState<boolean>(true);    const classes = useStyles();    const theme = useTheme(); @@ -47,13 +50,20 @@ const ModalScreen: React.FC<PropTypes> = ({ title, children }) => {    const handleClose = useCallback(() => setIsOpen(false), [setIsOpen]);    const onExited = useCallback(() => history.goBack(), [history]); +  const handleClickAction = useCallback(async () => { +    if (handleAction) await handleAction(); +    return handleClose(); +  }, [handleAction, handleClose]); +    return (      <Dialog        open={isOpen} +      onClose={handleClose}        onExited={onExited}        TransitionComponent={Transition}        PaperProps={{ className: classes.root }}        fullScreen={isMobile} +      maxWidth="md"        fullWidth      >        <AppBar position="static"> @@ -64,8 +74,8 @@ const ModalScreen: React.FC<PropTypes> = ({ title, children }) => {            <Typography variant="h6">              { title }            </Typography> -          <IconButton style={{ opacity: 0, pointerEvents: 'none' }}> -            <CloseIcon /> +          <IconButton onClick={handleClickAction} disabled={isActionDisabled}> +            { actionIcon }            </IconButton>          </Toolbar>        </AppBar> diff --git a/src/containers/Page/Page.tsx b/src/containers/Page/Page.tsx index b7e1938..e60f7da 100644 --- a/src/containers/Page/Page.tsx +++ b/src/containers/Page/Page.tsx @@ -10,10 +10,6 @@ import DynoWaiter from './DynoWaiter';  import Loading from '../../components/Loading/Loading';  import EmptyState from '../../components/EmptyState/EmptyState'; -interface HistoryChange { -  state?: LocationState | null; -} -  const useStyles = makeStyles(theme => ({    root: {      [theme.breakpoints.down('sm')]: { @@ -32,10 +28,10 @@ const ErrorFallback: React.FC = () => (  const Page: React.FC = () => {    const classes = useStyles();    const theme = useTheme(); -  const history = useHistory(); +  const history = useHistory<LocationState>();    const isMobile = useMediaQuery(theme.breakpoints.down('sm')); -  useEffect(() => history.listen((update: HistoryChange) => { +  useEffect(() => history.listen(update => {      if (!update.state?.background) window.scrollTo(0, 0);    }), [history]); diff --git a/src/containers/Page/Router.tsx b/src/containers/Page/Router.tsx index 7c3a418..7c74e9a 100644 --- a/src/containers/Page/Router.tsx +++ b/src/containers/Page/Router.tsx @@ -12,7 +12,7 @@ const Home = React.lazy(() => import('../Home/Home'));  const Notifications = React.lazy(() => import('../Notifications/Notifications'));  export interface LocationState { -  background?: Location; +  background: Location;  }  const Router: React.FC = React.memo(() => { diff --git a/src/containers/PollCreation/ImageInput.tsx b/src/containers/PollCreation/ImageInput.tsx index e807865..181294e 100644 --- a/src/containers/PollCreation/ImageInput.tsx +++ b/src/containers/PollCreation/ImageInput.tsx @@ -17,13 +17,33 @@ interface PropTypes {    progress?: number;  } -const useStyles = makeStyles({ +const useStyles = makeStyles(theme => ({    root: { +    width: '50%', +    display: 'flex' +  }, +  mediaRoot: { +    display: 'flex' +  }, +  uploadRoot: { +    flex: 1, +    display: 'flex', +    [theme.breakpoints.up('md')]: { +      padding: theme.spacing(4) +    }, +    [theme.breakpoints.down('sm')]: { +      padding: theme.spacing(8, 1) +    } +  }, +  outline: { +    padding: theme.spacing(2), +    flex: 1, +    border: '2px dashed gray', +    borderRadius: theme.spacing(1),      display: 'flex',      justifyContent: 'center',      flexDirection: 'column', -    alignItems: 'center', -    width: '50%' +    alignItems: 'center'    },    clearIcon: {      opacity: '.5', @@ -48,7 +68,7 @@ const useStyles = makeStyles({    icon: {      color: 'white'    } -}); +}));  const ImageInput: React.FC<PropTypes> = ({ callback, progress }) => { @@ -69,15 +89,17 @@ const ImageInput: React.FC<PropTypes> = ({ callback, progress }) => {    };    const Upload = ( -    <div className={classes.root}> -      <FileUpload callback={childrenCallback} /> -      <Typography variant="h6"> or </Typography> -      <AttachLink callback={childrenCallback} /> +    <div className={classes.uploadRoot}> +      <div className={classes.outline}> +        <FileUpload callback={childrenCallback} /> +        <Typography variant="h6"> or </Typography> +        <AttachLink callback={childrenCallback} /> +      </div>      </div>    );    const Media = ( -    <CardActionArea onClick={handleClear} className={classes.root} disabled={Boolean(progress)}> +    <CardActionArea onClick={handleClear} className={classes.mediaRoot} disabled={Boolean(progress)}>        <BackgroundImage src={url} />        <div className={`${classes.overlay} ${progress === 100 && classes.invisible}`}>          { @@ -91,7 +113,11 @@ const ImageInput: React.FC<PropTypes> = ({ callback, progress }) => {      </CardActionArea>    ); -  return url ? Media : Upload; +  return ( +    <div className={classes.root}> +      {url ? Media : Upload} +    </div> +  );  };  export default ImageInput; diff --git a/src/containers/PollCreation/PollCreation.tsx b/src/containers/PollCreation/PollCreation.tsx index b7ee00a..68f41d2 100644 --- a/src/containers/PollCreation/PollCreation.tsx +++ b/src/containers/PollCreation/PollCreation.tsx @@ -1,21 +1,17 @@  import React from 'react';  import Bluebird from 'bluebird'; -import { useHistory } from 'react-router-dom';  import { makeStyles } from '@material-ui/core/styles'; -import { -  Button, -  Card, -  Container, -  LinearProgress -} from '@material-ui/core'; +import { Card, Container, LinearProgress } from '@material-ui/core'; +import SendIcon from '@material-ui/icons/Send';  import { useSnackbar } from 'notistack';  import useS3Preupload from './useS3Preupload';  import ImageInput from './ImageInput';  import ModalScreen from '../../components/ModalScreen/ModalScreen'; +import Message from '../../components/Message/Message';  import UserStrip from '../../components/UserStrip/UserStrip';  import { post } from '../../requests'; -import { useFeed } from '../../hooks/APIClient'; +import { useFeed, useProfile } from '../../hooks/APIClient';  import { useAuth } from '../../hooks/useAuth'; @@ -29,10 +25,10 @@ const useStyles = makeStyles(theme => ({  const PollCreation: React.FC = () => {    const classes = useStyles(); -  const history = useHistory();    const { user } = useAuth();    const { enqueueSnackbar } = useSnackbar();    const { mutate: updateFeed } = useFeed(); +  const { mutate: updateProfile } = useProfile(user?.username || '');    const {      file: left,      setFile: setLeft, @@ -46,7 +42,7 @@ const PollCreation: React.FC = () => {      progress: rightProgress    } = useS3Preupload(); -  const handleClick = async () => { +  const handleSubmit = async () => {      try {        const [leftUrl, rightUrl] = await Bluebird.all([resolveLeft(), resolveRight()]); @@ -55,20 +51,23 @@ const PollCreation: React.FC = () => {          right: { url: rightUrl }        }; -      history.push('/feed'); -        post('/polls/', { contents }).then(() => {          updateFeed(); +        updateProfile();          enqueueSnackbar('Your poll has been successfully created!', { variant: 'success' });        });      } catch (error) {        enqueueSnackbar('Failed to create a poll. Please, try again.', { variant: 'error' }); -      history.push('/feed');      }    };    return ( -    <ModalScreen title="Create a poll"> +    <ModalScreen +      title="Create a poll" +      actionIcon={<SendIcon />} +      handleAction={handleSubmit} +      isActionDisabled={!(left && right) || leftProgress > 0 || rightProgress > 0} +    >        <Container maxWidth="sm" disableGutters>          <Card elevation={3}>            {user && <UserStrip user={user} info="" />} @@ -76,22 +75,17 @@ const PollCreation: React.FC = () => {              <ImageInput callback={setLeft} progress={leftProgress} />              <ImageInput callback={setRight} progress={rightProgress} />            </div> -          { -            leftProgress || rightProgress -              ? <LinearProgress color="primary" /> -              : ( -                <Button -                  color="primary" -                  disabled={!(left && right)} -                  variant="contained" -                  onClick={handleClick} -                  fullWidth -                > -                  Submit -                </Button> -              ) -          }          </Card> +        {(leftProgress > 0 || rightProgress > 0) && ( +          <> +            <LinearProgress color="primary" /> +            <Message +              tagline="You can close this window now" +              message="We will upload your images in the background." +              noMargin +            /> +          </> +        )}        </Container>      </ModalScreen>    ); diff --git a/src/hooks/APIClient.ts b/src/hooks/APIClient.ts index cf5d247..68413b0 100644 --- a/src/hooks/APIClient.ts +++ b/src/hooks/APIClient.ts @@ -13,8 +13,8 @@ export const useUser = (username: string | null): Response<User> => {    );  }; -export const useProfile = (id: string): Response<Poll[]> => { -  return useSWR(id && `/profiles/${id}`, fetcher); +export const useProfile = (username: string): Response<Poll[]> => { +  return useSWR(username && `/profiles/${username}`, fetcher);  };  export const useFeed = (): Response<Poll[]> => { | 
