diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/components/Image/BackgroundImage.tsx | 35 | ||||
| -rw-r--r-- | src/components/Image/Image.tsx | 100 | ||||
| -rw-r--r-- | src/components/PollCard/PollCard.tsx | 29 | ||||
| -rw-r--r-- | src/containers/Home/Home.tsx | 7 | 
4 files changed, 148 insertions, 23 deletions
diff --git a/src/components/Image/BackgroundImage.tsx b/src/components/Image/BackgroundImage.tsx new file mode 100644 index 0000000..e749d10 --- /dev/null +++ b/src/components/Image/BackgroundImage.tsx @@ -0,0 +1,35 @@ +import React from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import Image from './Image'; + +interface PropTypes { +  src?: string; +  alt?: string; +} + +const useStyles = makeStyles({ +  root: { +    position: 'absolute', +    width: '100%', +    height: '100%' +  }, +  image: { +    objectFit: 'cover', +    pointerEvents: 'none', +    width: '100%', +    height: '100%' +  } +}); + +const BackgroundImage: React.FC<PropTypes> = ({ src, alt }) => { +  const classes = useStyles(); + +  return ( +    <picture className={classes.root}> +      <Image src={src} alt={alt} className={classes.image} /> +    </picture> +  ); +}; + +export default BackgroundImage; + diff --git a/src/components/Image/Image.tsx b/src/components/Image/Image.tsx new file mode 100644 index 0000000..1898716 --- /dev/null +++ b/src/components/Image/Image.tsx @@ -0,0 +1,100 @@ +import React, { useState, useCallback, useMemo } from 'react'; +import { makeStyles } from '@material-ui/core/styles'; +import CircularProgress from '@material-ui/core/CircularProgress'; +import ErrorIcon from '@material-ui/icons/BrokenImage'; +import grey from '@material-ui/core/colors/grey'; + +interface PropTypes { +  src?: string; +  alt?: string; +  className?: string; +  animationDuration?: number; +  disableLoading?: boolean; +} + +type Status = 'success' | 'loading' | 'error'; + +const useStyles = makeStyles(theme => ({ +  container: { +    width: '100%', +    height: '100%', +    display: 'flex', +    alignItems: 'center', +    justifyContent: 'center' +  }, +  success: { +    opacity: '100%', +    filterBrightness: '100%', +    filterSaturate: '100%' +  }, +  loading: { +    opacity: 0, +    filterBrightness: 0, +    filterSaturate: '20%', +    position: 'absolute' +  }, +  error: { +    display: 'none' +  }, +  errorIcon: { +    color: grey[300], +    width: theme.spacing(6), +    height: theme.spacing(6) +  } +})); + +const Image: React.FC<PropTypes> = React.memo(({ +  src, +  alt, +  className, +  animationDuration = 1000, +  disableLoading = false +}) => { +  const classes = useStyles(); +  const [status, setStatus] = useState<Status>('loading'); + +  const handleLoad = useCallback((): void => { +    setStatus('success'); +  }, [setStatus]); + +  const handleError = useCallback((): void => { +    setStatus('error'); +  }, [setStatus]); + +  const transition = useMemo(() => ` +    filterBrightness ${animationDuration * 0.75}ms cubic-bezier(0.4, 0.0, 0.2, 1), +    filterSaturate ${animationDuration}ms cubic-bezier(0.4, 0.0, 0.2, 1), +    opacity ${animationDuration / 2}ms cubic-bezier(0.4, 0.0, 0.2, 1) +  `, [animationDuration]); + +  const image = ( +    <img +      src={src} +      alt={alt} +      className={`${className} ${classes[status]}`} +      style={{ transition }} +      onLoad={handleLoad} +      onError={handleError} +    /> +  ); + +  return ( +    <> +      {src && image} +      { +        status !== 'success' && ( +          <div className={classes.container}> +            { +              (status === 'error' || !src) +                ? <ErrorIcon className={classes.errorIcon} /> +                : (!disableLoading && <CircularProgress color="primary" />) +            } +          </div> +        ) +      } +    </> +  ); +}); + +export default Image; + diff --git a/src/components/PollCard/PollCard.tsx b/src/components/PollCard/PollCard.tsx index 689e872..a4c2144 100644 --- a/src/components/PollCard/PollCard.tsx +++ b/src/components/PollCard/PollCard.tsx @@ -1,15 +1,12 @@  import React from 'react';  import { makeStyles } from '@material-ui/core/styles'; -import { -  Card, -  CardActionArea, -  CardMedia -} from '@material-ui/core/'; +import { Card, CardActionArea } from '@material-ui/core/';  import { Which, Poll } from 'which-types';  import { useSnackbar } from 'notistack';  import PercentageBar from './PercentageBar';  import UserStrip from '../UserStrip/UserStrip'; +import BackgroundImage from '../Image/BackgroundImage';  import { post } from '../../requests';  import { useAuth } from '../../hooks/useAuth'; @@ -27,12 +24,10 @@ const DATE_FORMAT = {  };  const useStyles = makeStyles(theme => ({ -  images: { +  media: { +    display: 'flex',      height: theme.spacing(50)    }, -  imagesBlock: { -    display: 'flex' -  },    rateLine: {      position: 'relative',      width: '100%', @@ -101,19 +96,13 @@ const PollCard: React.FC<PropTypes> = ({ poll, setPoll }) => {    return (      <Card>        <UserStrip user={author} info={date} /> -      <div className={classes.imagesBlock}> -        <CardActionArea onDoubleClick={handleLeft}> -          <CardMedia -            className={classes.images} -            image={left.url} -          /> +      <div className={classes.media}> +        <CardActionArea onDoubleClick={handleLeft} className={classes.media}> +          <BackgroundImage src={left.url} />            <PercentageBar value={leftPercentage} which="left" like={vote?.which === 'left'} />          </CardActionArea> -        <CardActionArea onDoubleClick={handleRight}> -          <CardMedia -            className={classes.images} -            image={right.url} -          /> +        <CardActionArea onDoubleClick={handleRight} className={classes.media}> +          <BackgroundImage src={right.url} />            <PercentageBar value={rightPercentage} which="right" like={vote?.which === 'right'} />          </CardActionArea>        </div> diff --git a/src/containers/Home/Home.tsx b/src/containers/Home/Home.tsx index c0ca00e..072e2fa 100644 --- a/src/containers/Home/Home.tsx +++ b/src/containers/Home/Home.tsx @@ -13,10 +13,11 @@ import TrendingUpIcon from '@material-ui/icons/TrendingUp';  import { Rating } from '@material-ui/lab';  import { Feedback } from 'which-types'; -import { useAuth } from '../../hooks/useAuth'; -import { useFeedback } from '../../hooks/APIClient';  import ReviewCard from '../../components/ReviewCard/ReviewCard'; +import Image from '../../components/Image/Image';  import ReviewForm from './ReviewForm'; +import { useAuth } from '../../hooks/useAuth'; +import { useFeedback } from '../../hooks/APIClient';  const useStyles = makeStyles(theme => ({    root: { @@ -109,7 +110,7 @@ const Home: React.FC = () => {          <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} /> +              <Image 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" />}  |