imaginerio/narratives

View on GitHub
src/components/Editor/index.jsx

Summary

Maintainability
A
0 mins
Test Coverage
/* eslint-disable jsx-a11y/label-has-associated-control */
import React, { useState, useEffect, useRef } from 'react';
import PropTypes from 'prop-types';
import { useQuery, useMutation } from '@apollo/client';
import { Grid, Segment, Form, Input, Dropdown, Dimmer, Loader } from 'semantic-ui-react';

import {
  GET_SLIDES,
  UPDATE_SLIDE_TITLE,
  UPDATE_SLIDE_DESCRIPTION,
  UPDATE_SLIDE_SIZE,
  UPDATE_SLIDE_MEDIA,
  UPDATE_IMAGE,
} from './graphql';
import { useDraw } from '../../providers/DrawProvider';
import debouncedMutation from '../../providers/debouncedMutation';
import useLocale from '../../hooks/useLocale';

import AtlasContext from '../Atlas/Context';
import Image from '../Image';
import Year from '../Year';
import Layers from '../Layers';
import Search from '../Search';
import Wysiwyg from '../Wysiwyg';
import DrawList from '../DrawList';
import validateMediaUrl from '../../utils/validateMediaUrl';

import styles from './Editor.module.css';

const Editor = ({ slide }) => {
  const [title, setTitle] = useState('');
  const [description, setDescription] = useState('');
  const [media, setMedia] = useState('');
  const [mediaError, setMediaError] = useState(false);
  const [imageMeta, setImageMeta] = useState(null);
  const [size, setSize] = useState('Small');

  const { loading, error, data } = useQuery(GET_SLIDES, {
    variables: { slide },
  });

  const [, dispatch] = useDraw();
  const {
    cardTitle,
    cardDescription,
    size: sizeText,
    fullscreen,
    medium,
    small,
    image,
    externalMedia,
    loadingText,
  } = useLocale();

  useEffect(() => dispatch(['SLIDE', slide]), [slide]);

  useEffect(() => {
    setTitle(loading && !data ? '' : data.Slide.title || '');
    setDescription(loading && !data ? '' : data.Slide.description || '');
    setMedia(loading && !data ? '' : data.Slide.media || '');
    setSize(loading && !data ? 'Small' : data.Slide.size);
    setImageMeta(
      loading && !data
        ? {}
        : {
            imageTitle: data.Slide.imageTitle,
            source: data.Slide.source,
            url: data.Slide.url,
          }
    );
  }, [loading, data]);

  const [updateTitle] = useMutation(UPDATE_SLIDE_TITLE);
  const [updateDescription] = useMutation(UPDATE_SLIDE_DESCRIPTION);
  const [updateSize] = useMutation(UPDATE_SLIDE_SIZE);
  const [updateImage] = useMutation(UPDATE_IMAGE);
  const [updateMedia] = useMutation(UPDATE_SLIDE_MEDIA);

  const titleTimer = useRef();
  const descriptionTimer = useRef();
  const sizeTimer = useRef();
  const imageTimer = useRef();
  const mediaTimer = useRef();

  if (loading)
    return (
      <Dimmer active>
        <Loader size="huge">{loadingText}</Loader>
      </Dimmer>
    );
  if (error) return <p>Error :(</p>;

  return (
    <Grid stretched style={{ height: '100%', margin: 0 }}>
      <Grid.Row style={{ padding: 0 }}>
        <Grid.Column width={6} className={styles.editor}>
          <Form size="large">
            <Form.Field>
              <label>{cardTitle}</label>
              <Input
                value={title}
                onChange={(e, { value }) => {
                  setTitle(value);
                  titleTimer.current = debouncedMutation({
                    slide,
                    timerRef: titleTimer,
                    mutation: updateTitle,
                    values: { title: value },
                  });
                }}
              />
            </Form.Field>
            <Wysiwyg
              label={cardDescription}
              value={description}
              onEditorChange={value => {
                setDescription(value);
                descriptionTimer.current = debouncedMutation({
                  slide,
                  timerRef: descriptionTimer,
                  mutation: updateDescription,
                  values: { description: value },
                });
              }}
            />
            <Form.Field>
              <label>{sizeText}</label>
              <Dropdown
                placeholder="Select Size"
                fluid
                selection
                value={size}
                options={[
                  { text: fullscreen, value: 'Fullscreen' },
                  { text: medium, value: 'Medium' },
                  { text: small, value: 'Small' },
                ]}
                onChange={(e, { value }) => {
                  setSize(value);
                  sizeTimer.current = debouncedMutation({
                    slide,
                    timerRef: sizeTimer,
                    mutation: updateSize,
                    values: { size: value },
                  });
                }}
              />
            </Form.Field>
            <Form.Field>
              <label>{image}</label>
              <Image
                image={imageMeta}
                updateHandler={(id, value) => {
                  setImageMeta({ ...imageMeta, ...value });
                  imageTimer.current = debouncedMutation({
                    slide,
                    timerRef: imageTimer,
                    mutation: updateImage,
                    values: { ...imageMeta, ...value },
                  });
                }}
              />
            </Form.Field>
            <Form.Field error={mediaError}>
              <label>{externalMedia}</label>
              <Input
                placeholder="YouTube, SoundCloud URL"
                icon="linkify"
                iconPosition="left"
                value={media}
                onChange={(e, { value }) => {
                  setMedia(value);
                  if (!value || validateMediaUrl(value)) {
                    mediaTimer.current = debouncedMutation({
                      slide,
                      timerRef: mediaTimer,
                      mutation: updateMedia,
                      values: { media: value },
                    });
                    setMediaError(false);
                  } else {
                    setMediaError(true);
                  }
                }}
              />
            </Form.Field>
            <DrawList slide={slide} />
          </Form>
        </Grid.Column>
        <Grid.Column width={10} style={{ padding: 0 }}>
          <AtlasContext slide={slide} />
          <Segment className={styles.control}>
            <div style={{ float: 'right', width: 85 }}>
              <Layers slide={slide} />
              <Search slide={slide} />
            </div>
            <Year slide={slide} />
          </Segment>
        </Grid.Column>
      </Grid.Row>
    </Grid>
  );
};

Editor.propTypes = {
  slide: PropTypes.string.isRequired,
};

export default Editor;