Skip to content
Snippets Groups Projects
ReviewPage.jsx 10.2 KiB
Newer Older
import React from 'react'
Audrey Hamelers's avatar
Audrey Hamelers committed
import { Query, Mutation } from 'react-apollo'
import styled from 'styled-components'
Audrey Hamelers's avatar
Audrey Hamelers committed
import ReactHtmlParser from 'react-html-parser'
import { th, lighten } from '@pubsweet/ui-toolkit'
ahamelers's avatar
ahamelers committed
import { Action, Button, ErrorText, H1, H2 } from '@pubsweet/ui'
import {
Audrey Hamelers's avatar
Audrey Hamelers committed
  Loading,
  LoadingIcon,
Audrey Hamelers's avatar
Audrey Hamelers committed
  Buttons,
  Center,
  PreviewPage,
  PreviewPanel,
  EditPanel,
  PanelHeader,
  PanelContent,
  Toggle as Toggles,
} from '../ui'
Audrey Hamelers's avatar
Audrey Hamelers committed
import UploadFiles, { SubmissionTypes, ReviewTypes } from '../upload-files'
import ManuscriptPreview from '../ManuscriptPreview'
import PDFViewer from '../pdf-viewer'
ahamelers's avatar
ahamelers committed
import { GET_MANUSCRIPT } from '../operations'
Audrey Hamelers's avatar
Audrey Hamelers committed
import SubmissionHeader from '../SubmissionHeader'
ahamelers's avatar
ahamelers committed
import HTMLPreview from './HTMLPreview'
import ReviewForm from './ReviewForm'
import Annotator from './Annotator'
import { CONVERT_XML, GET_REVIEWS } from './operations'

const PreviewPageDiv = styled(PreviewPage)`
ahamelers's avatar
ahamelers committed
  & > div > div {
    overflow: auto;
  }
Audrey Hamelers's avatar
Audrey Hamelers committed
  & > div > div > div:last-child {
    height: calc(100% - (${th('gridUnit')} * 19));
ahamelers's avatar
ahamelers committed
  }
  .show-mobile {
    display: none;
  }
ahamelers's avatar
ahamelers committed
  .pad {
    padding: 0 calc(2 * ${th('gridUnit')});
  }
  @media screen and (max-width: 870px) {
    .show-mobile {
ahamelers's avatar
ahamelers committed
      display: inline-block;
    }
    .hide-mobile {
      display: none;
    }
Audrey Hamelers's avatar
Audrey Hamelers committed
  }
`
const PreviewPanelHeader = styled(PanelHeader)`
  height: calc(${th('gridUnit')} * 10);
ahamelers's avatar
ahamelers committed

  @media screen and (max-width: 870px) {
    height: auto;
  }
Audrey Hamelers's avatar
Audrey Hamelers committed
`
const Toggle = styled(Toggles)`
Audrey Hamelers's avatar
Audrey Hamelers committed
  height: calc(${th('gridUnit')} * 7);
ahamelers's avatar
ahamelers committed
  &.hide-mobile button {
    margin-left: calc(${th('gridUnit')} * 3);
  }
  @media screen and (max-width: 870px) {
    height: auto;
    margin-bottom: calc(${th('gridUnit')} * 3);
  }
const PreviewError = styled.div`
  border: ${th('borderWidth')} ${th('borderStyle')} ${th('colorError')};
  background-color: ${lighten('colorError', 40)};
  padding: ${th('gridUnit')} calc(${th('gridUnit')} * 2);
  margin: ${th('gridUnit')} 0;
const AnnotatePDF = Annotator(PDFViewer)
const AnnotateHTML = Annotator(HTMLPreview)

const allFileTypes = SubmissionTypes.concat(
  ReviewTypes.reduce((all, t) => {
    if (!SubmissionTypes.some(existing => existing.value === t.value)) {
      all.push(t)
    return all
Audrey Hamelers's avatar
Audrey Hamelers committed
class Review extends React.Component {
  state = {
    pane: this.props.currentUser.admin ? 'files' : 'web',
    error: '',
    showManuscript: false,
    showAll: false,
Audrey Hamelers's avatar
Audrey Hamelers committed
  }
  render() {
    const { manuscript, currentUser, reviews } = this.props
    const [review] = reviews
    const { files: allfiles, meta, status } = manuscript
Audrey Hamelers's avatar
Audrey Hamelers committed
    const sourceFile = allfiles.find(f => f.type === 'manuscript')
Audrey Hamelers's avatar
Audrey Hamelers committed
    const files = allfiles.filter(
Audrey Hamelers's avatar
Audrey Hamelers committed
      f => !f.type || ReviewTypes.some(rev => rev.value === f.type),
Audrey Hamelers's avatar
Audrey Hamelers committed
    )
    const originalFiles = allfiles.filter(f =>
      SubmissionTypes.some(sub => sub.value === f.type),
Audrey Hamelers's avatar
Audrey Hamelers committed
    )
    const html = files.find(file => file.type === 'tempHTML')
Audrey Hamelers's avatar
Audrey Hamelers committed
    const pdf = files.find(f => f.type === 'pdf4load' || f.type === 'pdf4print')
    const xml = files.find(f => f.type === 'PMC') || null
Audrey Hamelers's avatar
Audrey Hamelers committed
    const xmlError = meta.notes
      ? meta.notes.find(n => n.notesType === 'xml error')
      : ''
    const { pane, error, showManuscript, showAll } = this.state
Audrey Hamelers's avatar
Audrey Hamelers committed
    return (
      <PreviewPageDiv>
ahamelers's avatar
ahamelers committed
        <PreviewPanel
          style={{
            flex: showManuscript && '1 1 750px',
            width: showManuscript && '50%',
          }}
        >
          <div style={{ maxWidth: showManuscript && '750px' }}>
Audrey Hamelers's avatar
Audrey Hamelers committed
            <PreviewPanelHeader>
              <H1>Review web versions of manuscript</H1>
            </PreviewPanelHeader>
            <Toggle>
Audrey Hamelers's avatar
Audrey Hamelers committed
              {currentUser.admin && (
Audrey Hamelers's avatar
Audrey Hamelers committed
                <Action
                  className={pane === 'files' ? 'current' : ''}
Audrey Hamelers's avatar
Audrey Hamelers committed
                  onClick={() => this.setState({ pane: 'files' })}
                >
Audrey Hamelers's avatar
Audrey Hamelers committed
                </Action>
Audrey Hamelers's avatar
Audrey Hamelers committed
              )}
Audrey Hamelers's avatar
Audrey Hamelers committed
              <Action
                className={pane === 'web' ? 'current' : ''}
Audrey Hamelers's avatar
Audrey Hamelers committed
                onClick={() => {
                  this.setState({ pane: 'web' })
                }}
              >
Audrey Hamelers's avatar
Audrey Hamelers committed
              </Action>
              <Action
                className={pane === 'pdf' ? 'current' : ''}
Audrey Hamelers's avatar
Audrey Hamelers committed
                onClick={() => this.setState({ pane: 'pdf' })}
              >
Audrey Hamelers's avatar
Audrey Hamelers committed
              </Action>
              <Action
Audrey Hamelers's avatar
Audrey Hamelers committed
                className={
Audrey Hamelers's avatar
Audrey Hamelers committed
                  pane === 'original' ? 'current show-mobile' : 'show-mobile'
Audrey Hamelers's avatar
Audrey Hamelers committed
                }
                onClick={() => this.setState({ pane: 'original' })}
                primary={pane !== 'original'}
              >
                Submitted file
Audrey Hamelers's avatar
Audrey Hamelers committed
              </Action>
            </Toggle>
Audrey Hamelers's avatar
Audrey Hamelers committed
            <PanelContent>
              {pane === 'files' && (
Audrey Hamelers's avatar
Audrey Hamelers committed
                <Mutation mutation={CONVERT_XML}>
                  {(convertXML, { data }) => {
                    const generatePreviews = async () => {
                      await convertXML({
                        variables: { id: xml.id },
                      })
                    }
                    return (
                      <React.Fragment>
                        <Buttons left top>
                          <Button disabled={!xml} onClick={generatePreviews}>
                            Regenerate previews
                          </Button>
                          <Button
                            onClick={() => this.setState({ showAll: !showAll })}
                          >
                            {`${showAll ? 'Hide' : 'Show'} submission files`}
                          </Button>
                        </Buttons>
                        {xmlError && (
                          <PreviewError>
                            {ReactHtmlParser(xmlError.content)}
                          </PreviewError>
                        )}
                        {error && (
                          <Center>
                            <ErrorText>{error}</ErrorText>
                          </Center>
                        )}
                        <UploadFiles
                          checked
                          files={showAll ? allfiles : files}
                          manuscript={manuscript.id}
                          types={showAll ? allFileTypes : ReviewTypes}
                        />
                        {error && (
                          <Center>
                            <ErrorText>{error}</ErrorText>
                          </Center>
                        )}
                        <Buttons left top>
                          <Button disabled={!xml} onClick={generatePreviews}>
                            Regenerate previews
                          </Button>
                          <Button
                            onClick={() => this.setState({ showAll: !showAll })}
                          >
                            {`${showAll ? 'Hide' : 'Show'} submission files`}
                          </Button>
                        </Buttons>
                      </React.Fragment>
                    )
                  }}
                </Mutation>
Audrey Hamelers's avatar
Audrey Hamelers committed
              )}
              {pane === 'pdf' && pdf && (
                <React.Fragment>
                  {status === 'tagging' ? (
                    <PDFViewer url={pdf.url} />
                  ) : (
                    <AnnotatePDF
                      file={pdf}
                      revId={(review && review.id) || null}
                      userId={currentUser.id}
                    />
                  )}
                </React.Fragment>
Audrey Hamelers's avatar
Audrey Hamelers committed
              )}
              {pane === 'web' && html && (
                <React.Fragment>
                  {status === 'tagging' ? (
                    <HTMLPreview url={html.url} />
                  ) : (
                    <AnnotateHTML
                      file={html}
                      revId={(review && review.id) || null}
                      userId={currentUser.id}
                    />
                  )}
                </React.Fragment>
Audrey Hamelers's avatar
Audrey Hamelers committed
              )}
              {pane === 'original' && sourceFile && (
                <ManuscriptPreview file={sourceFile} />
              )}
            </PanelContent>
          </div>
        </PreviewPanel>
ahamelers's avatar
ahamelers committed
        <EditPanel
          style={{
            flex: showManuscript && '1 1 750px',
            width: showManuscript && '50%',
          }}
        >
          <div style={{ maxWidth: showManuscript && '750px' }}>
Audrey Hamelers's avatar
Audrey Hamelers committed
            <PreviewPanelHeader>
              <H2>Compare &amp; approve</H2>
            </PreviewPanelHeader>
ahamelers's avatar
ahamelers committed
            <Toggle className="hide-mobile">
              <Button
                onClick={() =>
                  this.setState({ showManuscript: !showManuscript })
                }
              >
                {showManuscript
                  ? 'Back to approval form'
ahamelers's avatar
ahamelers committed
                  : 'Compare with submitted file'}
              </Button>
            </Toggle>
            <PanelContent className={showManuscript && 'pad'}>
              {showManuscript && sourceFile ? (
                <ManuscriptPreview file={sourceFile} />
              ) : (
                <ReviewForm
                  annotations={(review && review.annotations) || []}
Audrey Hamelers's avatar
Audrey Hamelers committed
                  files={originalFiles}
ahamelers's avatar
ahamelers committed
                  manuscript={manuscript}
Audrey Hamelers's avatar
Audrey Hamelers committed
                  previews={!!html && !!pdf}
ahamelers's avatar
ahamelers committed
                />
              )}
Audrey Hamelers's avatar
Audrey Hamelers committed
            </PanelContent>
Audrey Hamelers's avatar
Audrey Hamelers committed
          </div>
        </EditPanel>
      </PreviewPageDiv>
    )
  }
}

const ReviewWithHeader = SubmissionHeader(Review)

const ReviewPage = ({ match, ...props }) => (
  <Query
    fetchPolicy="cache-and-network"
    query={GET_MANUSCRIPT}
    variables={{ id: match.params.id }}
  >
    {({ data: { manuscript }, loading: loadingOne, refetch }) => (
      <Query query={GET_REVIEWS} variables={{ manuscriptId: match.params.id }}>
        {({ data, loading: loadingTwo }) => {
          if (loadingOne || !manuscript) {
            return (
              <Loading>
                <LoadingIcon />
              </Loading>
            )
          }
          return (
            <ReviewWithHeader
              manuscript={manuscript}
              match={match}
              reload={refetch}
              reviews={(data && data.reviewsByMId) || []}
              {...props}
            />
          )
        }}
      </Query>
    )}
Audrey Hamelers's avatar
Audrey Hamelers committed
  </Query>
)

export default ReviewPage