import React from 'react' import { withRouter } from 'react-router-dom' import styled from 'styled-components' import ReactHtmlParser from 'react-html-parser' import { th, lighten } from '@pubsweet/ui-toolkit' import { Action, Button, ErrorText, H2, Icon } from '@pubsweet/ui' import { 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; ` 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', }, { 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 }, []), ) const transformXML = ( project, currentVersion, xmlUri, xsdBUri, xsdGUri, param, ) => new Promise((resolve, reject) => { fetch('/xml/validate/api/post', { method: 'POST', headers: { Accept: 'text/json', Authorization: `Bearer ${window.localStorage.getItem('token')}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ project, currentVersion, xmlUri, xsdBUri, xsdGUri, param, }), }) .then(response => { if (!response.ok) { reject(response.text()) } else { resolve(response.text()) } }) .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, annotations: 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) 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 }) } async processXML() { this.setState({ status: 'Generating previews...' }) const { project, currentVersion } = this.props const { files } = currentVersion const xml = files.find(file => file.type === 'PMC') if (xml) { transformXML(project, currentVersion, xml.url, blueXSD, greenXSD, files) .then(result => { this.setError('') this.props.addXMLFiles(JSON.parse(result), project, currentVersion) this.setState({ status: 'All changes saved.' }) }) .catch(async err => { this.setError(await err) this.setState({ status: 'Something went wrong.', }) }) } } 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() }) } 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 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 && ( <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> )} <Buttons> <div> <Status>{status}</Status> <Button onClick={this.processXML}> Regenerate previews </Button> </div> </Buttons> </div> </Content> </Section> )} {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} /> ) : ( <XMLForm annotations={this.state.annotations} files={originalFiles.filter(file => file.type !== 'manuscript')} {...this.props} /> )} </PanelContent> </EditPanel> </PreviewPageDiv> ) } } export default withRouter(EPMCTagged)