Skip to content
Snippets Groups Projects
ReviewPage.jsx 13.3 KiB
Newer Older
import React from 'react'
import { withRouter } from 'react-router-dom'
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'
import { Action, Button, ErrorText, H2, Icon } from '@pubsweet/ui'
import {
Audrey Hamelers's avatar
Audrey Hamelers committed
  Buttons,
  Center,
  PreviewPage,
  PreviewPanel,
  EditPanel,
  PanelHeader,
  PanelContent,
  EditSection as Section,
  SectionContent as Content,
  SectionHeader as Header,
} from '../ui'
import Annotator from '../Annotator'
import UploadFiles from '../upload-files'
import HTMLPreview from './HTMLPreview'
import ManuscriptPreview from '../ManuscriptPreview'
import XMLForm from './XMLForm'
import PDFViewer from '../pdf-viewer'
import blueXSD from '../../assets/xsl/xsd/publishing/journalpublishing3.xsd'
import greenXSD from '../../assets/xsl/xsd/archiving/archivearticle3.xsd'

const PreviewPageDiv = styled(PreviewPage)`
  .show-mobile {
    display: none;
  }
  .preview-buttons {
    align-items: center;
    button {
      height: 60%;
      &.selected {
        background-color: transparent;
        color: #000;
        border-bottom: ${th('borderWidth')} ${th('borderStyle')} transparent;
      }
    }
  }
  @media screen and (max-width: 870px) {
    .show-mobile {
      display: block;
    }
    .hide-mobile {
      display: none;
    }
    .preview-buttons {
      align-items: stretch;
      padding: ${th('gridUnit')} 0;
      button {
        min-width: 0;
        font-size: ${th('fontSizeBaseSmall')};
      }
    }
  }
`
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;
Audrey Hamelers's avatar
Audrey Hamelers committed
const Status = styled.span`
  margin-right: calc(${th('gridUnit')} * 2);
  font-style: italic;
  color: #999;
`

const AnnotatePDF = Annotator(PDFViewer)
const AnnotateHTML = Annotator(HTMLPreview)

const XMLFileTypes = [
  {
    label: '',
    value: '',
  },
  {
    label: 'Supplementary file',
    value: 'supplementary file',
  },
  {
    label: 'Movie in text',
    value: 'movie_in_text',
  },
  {
    label: 'Movie figure',
    value: 'movie_figure',
  },
  {
    label: 'PMC XML from tagger',
    value: 'PMC',
  },
  {
    label: 'Supplementary file from tagger',
    value: 'supplement_tag',
  },
  {
    label: 'PMCfinal XML',
    value: 'PMCfinal',
  },
  {
    label: 'PMC PDF (for pmc-load)',
    value: 'pdf4load',
  },
  {
    label: 'PMC PDF (generated)',
    value: 'pdf4print',
  },
  {
    label: 'Thumbnail Image (GIF)',
    value: 'IMGsmall',
  },
  {
    label: 'Fullscreen Image (JPG)',
    value: 'IMGview',
  },
  {
    label: 'Printsize Image (TIF)',
    value: 'IMGprint',
  },
Audrey Hamelers's avatar
Audrey Hamelers committed
  {
    label: 'Web Preview',
    value: 'tempHTML',
  },
]

const originalFileTypes = [
  'manuscript',
  'figure',
  'table',
  'supplementary file',
]

const allFileTypes = XMLFileTypes.concat(
  originalFileTypes.reduce((orgf, type) => {
    if (!XMLFileTypes.some(existing => existing.value === type)) {
      orgf.push({
        label: type,
        value: type,
      })
    }
    return orgf
  }, []),
)

Nikos Marinos's avatar
Nikos Marinos committed
const transformXML = (
  project,
  currentVersion,
  xmlUri,
Nikos Marinos's avatar
Nikos Marinos committed
  param,
) =>
Audrey Hamelers's avatar
Audrey Hamelers committed
  new Promise((resolve, reject) => {
Nikos Marinos's avatar
Nikos Marinos committed
    fetch('/xml/validate/api/post', {
      method: 'POST',
      headers: {
Audrey Hamelers's avatar
Audrey Hamelers committed
        Accept: 'text/json',
Nikos Marinos's avatar
Nikos Marinos committed
        Authorization: `Bearer ${window.localStorage.getItem('token')}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
Nikos Marinos's avatar
Nikos Marinos committed
        project,
        currentVersion,
Nikos Marinos's avatar
Nikos Marinos committed
        xmlUri,
Nikos Marinos's avatar
Nikos Marinos committed
        param,
      }),
Nikos Marinos's avatar
Nikos Marinos committed
    })
      .then(response => {
        if (!response.ok) {
Nikos Marinos's avatar
Nikos Marinos committed
        } else {
Audrey Hamelers's avatar
Audrey Hamelers committed
          resolve(response.text())
Nikos Marinos's avatar
Nikos Marinos committed
        }
      })
      .catch(reason => reject(reason))
const getAnnotations = async id => {
  const response = await fetch(`/annotations/file/${id}`, {
    headers: new Headers({
      Authorization: `Bearer ${window.localStorage.getItem('token')}`,
    }),
  })
  const json = await response.json()
  return json.rows
}

class EPMCTagged extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      pane: this.props.currentUser.admin ? 'files' : 'web',
      status: '',
      error: '',
      showManuscript: false,
      showAll: false,
      html: null,
    // this.addManuscriptFiles = addManuscriptFiles.bind(this)
    // this.deleteManuscriptFile = deleteManuscriptFile.bind(this)
    // this.updateManuscriptFiles = updateManuscriptFiles.bind(this)
    // this.setNewManuscript = setNewManuscript.bind(this)
    this.listAnnotations = this.listAnnotations.bind(this)
Audrey Hamelers's avatar
Audrey Hamelers committed
    this.processXML = this.processXML.bind(this)
    this.webPreview = this.webPreview.bind(this)
    this.setError = this.setError.bind(this)
  /* static getDerivedStateFromProps(props, state) {
    const status = state.status === 'Saving...'
    if (status !== props.conversion.converting) {
      return {
        error: props.conversion.error ? props.conversion.error : '',
        status: props.conversion.converting
          ? 'Saving...'
          : 'All changes saved.',
      }
    }
    return null
  componentDidMount() {
    this.listAnnotations()
  }
  setError(error) {
    this.props.setXMLError(error, this.props.project, this.props.currentVersion)
  }
  async listAnnotations() {
    const { currentVersion } = this.props
    const errors = []
    errors.push({
      name: 'Web',
      value: await getAnnotations(`${currentVersion.id}-web`),
    })
    errors.push({
      name: 'PDF',
      value: await getAnnotations(`${currentVersion.id}-pdf`),
    })
    this.setState({ annotations: errors })
  }
Audrey Hamelers's avatar
Audrey Hamelers committed
  async processXML() {
Audrey Hamelers's avatar
Audrey Hamelers committed
    this.setState({ status: 'Generating previews...' })
Nikos Marinos's avatar
Nikos Marinos committed
    const { project, currentVersion } = this.props
Audrey Hamelers's avatar
Audrey Hamelers committed
    const { files } = currentVersion
    const xml = files.find(file => file.type === 'PMC')
    if (xml) {
Audrey Hamelers's avatar
Audrey Hamelers committed
      transformXML(project, currentVersion, xml.url, blueXSD, greenXSD, files)
Nikos Marinos's avatar
Nikos Marinos committed
        .then(result => {
          this.setError('')
Audrey Hamelers's avatar
Audrey Hamelers committed
          this.props.addXMLFiles(JSON.parse(result), project, currentVersion)
          this.setState({ status: 'All changes saved.' })
Nikos Marinos's avatar
Nikos Marinos committed
        .catch(async err => {
Audrey Hamelers's avatar
Audrey Hamelers committed
          this.setError(await err)
          this.setState({
            status: 'Something went wrong.',
          })
Nikos Marinos's avatar
Nikos Marinos committed
  async webPreview() {
    const { currentVersion } = this.props
    const { files } = currentVersion
    const html = files.find(file => file.type === 'tempHTML')
    if (html) {
      const response = await fetch(html.url)
      this.setState({ html: await response.text() })
Nikos Marinos's avatar
Nikos Marinos committed
    } else {
      this.setState({ html: 'Error. No Web Preview generated.' })
    }
  }
  render() {
    const { currentVersion, currentUser, uploadFile } = this.props
    const { files: allfiles, metadata, source } = currentVersion
    const manuscript = allfiles.find(file => file.type === 'manuscript')
    const files = allfiles.filter(
      file => !file.type || XMLFileTypes.some(type => type.value === file.type),
    )
    const originalFiles = allfiles.filter(
      file => !file.type || originalFileTypes.some(type => type === file.type),
    )
    const pdf = files.find(
      file => file.type === 'pdf4load' || file.type === 'pdf4print',
    )
    const xmlError = metadata.notes
      ? metadata.notes.find(n => n.notesType === 'xml error')
      : ''
    const { pane, status, error, showManuscript, showAll, html } = this.state
    return (
      <PreviewPageDiv>
        <PreviewPanel style={{ flex: showManuscript ? 1 : 2 }}>
          <PanelHeader className="preview-buttons">
            {currentUser.admin && (
              <Button
                className={pane === 'files' ? 'selected' : ''}
                onClick={() => this.setState({ pane: 'files' })}
                primary={pane !== 'files'}
                style={{ flex: 1, margin: '0 1px' }}
              >
                XML Files
              </Button>
            )}
            <Button
              className={pane === 'web' ? 'selected' : ''}
              onClick={() => {
                this.webPreview()
                this.setState({ pane: 'web' })
              }}
              primary={pane !== 'web'}
              style={{ flex: 1, margin: '0 1px' }}
            >
              Web Preview
            </Button>
            <Button
              className={pane === 'pdf' ? 'selected' : ''}
              onClick={() => this.setState({ pane: 'pdf' })}
              primary={pane !== 'pdf'}
              style={{ flex: 1, margin: '0 1px' }}
            >
              PDF Preview
            </Button>
            <Button
              className={
                pane === 'original' ? 'selected show-mobile' : 'show-mobile'
              }
              onClick={() => this.setState({ pane: 'original' })}
              primary={pane !== 'original'}
              style={{ flex: 1, margin: '0 1px' }}
            >
              Submitted Manuscript
            </Button>
          </PanelHeader>
          <PanelContent>
            {pane === 'files' && (
              <Section>
                <Header>
                  <H2>XML Files</H2>
                  <Action
                    onClick={() => this.setState({ showAll: !showAll })}
                    style={{ display: 'inline-flex', alignItems: 'center' }}
                  >
                    <Icon color="currentColor">archive</Icon>
                    {`${showAll ? 'Hide' : 'Show'} submission files`}
                  </Action>
                  <Action
Audrey Hamelers's avatar
Audrey Hamelers committed
                    onClick={this.processXML}
                    style={{ display: 'inline-flex', alignItems: 'center' }}
                  >
                    <Icon color="currentColor">refresh-cw</Icon>
                    Regenerate previews
                  </Action>
                </Header>
                <Content>
                  <div>
                    <div style={{ textAlign: 'right' }}>
                      <Status>{status}</Status>
                    </div>
                    {xmlError && (
Audrey Hamelers's avatar
Audrey Hamelers committed
                      <PreviewError>
                        {ReactHtmlParser(xmlError.content)}
                      </PreviewError>
                    {error && (
                      <Center>
                        <ErrorText>{error}</ErrorText>
                      </Center>
                    )}
                    <UploadFiles
                      addFiles={this.addManuscriptFiles}
                      checked
                      deleteFile={this.deleteManuscriptFile}
                      files={showAll ? allfiles : files}
                      newManuscriptFile={this.setNewManuscript}
                      parentStatus={status}
                      types={showAll ? allFileTypes : XMLFileTypes}
                      updateFiles={this.updateManuscriptFiles}
                      uploadFile={uploadFile}
                      version={currentVersion}
                    />
                    {error && (
                      <Center>
                        <ErrorText>{error}</ErrorText>
                      </Center>
                    )}
Audrey Hamelers's avatar
Audrey Hamelers committed
                    <Buttons>
                      <div>
                        <Status>{status}</Status>
                        <Button onClick={this.processXML}>
                          Regenerate previews
                        </Button>
                      </div>
                    </Buttons>
                  </div>
                </Content>
              </Section>
            )}
Audrey Hamelers's avatar
Audrey Hamelers committed
            {pane === 'pdf' && pdf && (
              <AnnotatePDF
                reload={this.listAnnotations()}
                toMark={`${currentVersion.id}-pdf`}
                url={pdf.url}
                user={currentUser.id}
              />
            )}
            {pane === 'web' && html && (
              <AnnotateHTML
                html={html}
                reload={this.listAnnotations()}
                toMark={`${currentVersion.id}-web`}
                user={currentUser.id}
              />
            )}
            {pane === 'original' && manuscript && (
              <ManuscriptPreview file={manuscript} source={source} />
            )}
          </PanelContent>
        </PreviewPanel>
        <EditPanel style={{ minWidth: showManuscript ? 0 : 'auto' }}>
          <PanelHeader className="hide-mobile" style={{ alignItems: 'center' }}>
            <Action
              onClick={() => this.setState({ showManuscript: !showManuscript })}
              style={{ display: 'inline-flex', alignItems: 'center' }}
            >
              <Icon color="currentColor">
                {showManuscript ? 'edit' : 'file-text'}
              </Icon>
              {showManuscript
                ? 'Back to form'
                : 'Submitted manuscript side-by-side view'}
            </Action>
          </PanelHeader>
          <PanelContent>
            {showManuscript && manuscript ? (
              <ManuscriptPreview file={manuscript} source={source} />
            ) : (
                annotations={this.state.annotations}
                files={originalFiles.filter(file => file.type !== 'manuscript')}
                {...this.props}
              />
            )}
          </PanelContent>
        </EditPanel>
      </PreviewPageDiv>
    )
  }
}

export default withRouter(EPMCTagged)