|
@@ -1,268 +0,0 @@
|
|
|
-import React, { useState, useEffect, useRef, useCallback } from 'react'
|
|
|
|
|
-import PropTypes from 'prop-types'
|
|
|
|
|
-import Icon from '../../../shared/components/icon'
|
|
|
|
|
-import { formatTime, relativeDate } from '../../utils/format-date'
|
|
|
|
|
-import { Trans, useTranslation } from 'react-i18next'
|
|
|
|
|
-import importOverleafModules from '../../../../macros/import-overleaf-module.macro'
|
|
|
|
|
-import { postJSON } from '../../../infrastructure/fetch-json'
|
|
|
|
|
-
|
|
|
|
|
-const tprLinkedFileInfo = importOverleafModules('tprLinkedFileInfo')
|
|
|
|
|
-const tprLinkedFileRefreshError = importOverleafModules(
|
|
|
|
|
- 'tprLinkedFileRefreshError'
|
|
|
|
|
-)
|
|
|
|
|
-
|
|
|
|
|
-const MAX_URL_LENGTH = 60
|
|
|
|
|
-const FRONT_OF_URL_LENGTH = 35
|
|
|
|
|
-const FILLER = '...'
|
|
|
|
|
-const TAIL_OF_URL_LENGTH = MAX_URL_LENGTH - FRONT_OF_URL_LENGTH - FILLER.length
|
|
|
|
|
-
|
|
|
|
|
-function shortenedUrl(url) {
|
|
|
|
|
- if (!url) {
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
- if (url.length > MAX_URL_LENGTH) {
|
|
|
|
|
- const front = url.slice(0, FRONT_OF_URL_LENGTH)
|
|
|
|
|
- const tail = url.slice(url.length - TAIL_OF_URL_LENGTH)
|
|
|
|
|
- return front + FILLER + tail
|
|
|
|
|
- }
|
|
|
|
|
- return url
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-export default function BinaryFileHeader({ file, storeReferencesKeys }) {
|
|
|
|
|
- const isMounted = useRef(true)
|
|
|
|
|
- const [refreshing, setRefreshing] = useState(false)
|
|
|
|
|
- const [refreshError, setRefreshError] = useState(null)
|
|
|
|
|
- const { t } = useTranslation()
|
|
|
|
|
-
|
|
|
|
|
- useEffect(() => {
|
|
|
|
|
- // set to false on unmount to avoid unmounted component warning when refreshing
|
|
|
|
|
- return () => (isMounted.current = false)
|
|
|
|
|
- }, [])
|
|
|
|
|
-
|
|
|
|
|
- let fileInfo
|
|
|
|
|
- if (file.linkedFileData) {
|
|
|
|
|
- if (file.linkedFileData.provider === 'url') {
|
|
|
|
|
- fileInfo = (
|
|
|
|
|
- <div>
|
|
|
|
|
- <UrlProvider file={file} />
|
|
|
|
|
- </div>
|
|
|
|
|
- )
|
|
|
|
|
- } else if (file.linkedFileData.provider === 'project_file') {
|
|
|
|
|
- fileInfo = (
|
|
|
|
|
- <div>
|
|
|
|
|
- <ProjectFilePathProvider file={file} />
|
|
|
|
|
- </div>
|
|
|
|
|
- )
|
|
|
|
|
- } else if (file.linkedFileData.provider === 'project_output_file') {
|
|
|
|
|
- fileInfo = (
|
|
|
|
|
- <div>
|
|
|
|
|
- <ProjectOutputFileProvider file={file} />
|
|
|
|
|
- </div>
|
|
|
|
|
- )
|
|
|
|
|
- }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- const refreshFile = useCallback(() => {
|
|
|
|
|
- setRefreshing(true)
|
|
|
|
|
- // Replacement of the file handled by the file tree
|
|
|
|
|
- window.expectingLinkedFileRefreshedSocketFor = file.name
|
|
|
|
|
- postJSON(`/project/${window.project_id}/linked_file/${file.id}/refresh`, {
|
|
|
|
|
- disableAutoLoginRedirect: true
|
|
|
|
|
- })
|
|
|
|
|
- .then(() => {
|
|
|
|
|
- if (isMounted.current) {
|
|
|
|
|
- setRefreshing(false)
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
- .catch(err => {
|
|
|
|
|
- if (isMounted.current) {
|
|
|
|
|
- setRefreshing(false)
|
|
|
|
|
- setRefreshError(err.message)
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
- .finally(() => {
|
|
|
|
|
- if (
|
|
|
|
|
- file.linkedFileData.provider === 'mendeley' ||
|
|
|
|
|
- file.linkedFileData.provider === 'zotero' ||
|
|
|
|
|
- file.name.match(/^.*\.bib$/)
|
|
|
|
|
- ) {
|
|
|
|
|
- reindexReferences()
|
|
|
|
|
- }
|
|
|
|
|
- })
|
|
|
|
|
-
|
|
|
|
|
- function reindexReferences() {
|
|
|
|
|
- const opts = {
|
|
|
|
|
- body: { shouldBroadcast: true }
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- postJSON(`/project/${window.project_id}/references/indexAll`, opts)
|
|
|
|
|
- .then(response => {
|
|
|
|
|
- // Later updated by the socket but also updated here for immediate use
|
|
|
|
|
- storeReferencesKeys(response.keys)
|
|
|
|
|
- })
|
|
|
|
|
- .catch(error => {
|
|
|
|
|
- console.log(error)
|
|
|
|
|
- })
|
|
|
|
|
- }
|
|
|
|
|
- }, [file, isMounted, storeReferencesKeys])
|
|
|
|
|
-
|
|
|
|
|
- return (
|
|
|
|
|
- <div className="binary-file-header">
|
|
|
|
|
- {file.linkedFileData && fileInfo}
|
|
|
|
|
- {file.linkedFileData &&
|
|
|
|
|
- tprLinkedFileInfo.map(({ import: { LinkedFileInfo }, path }) => (
|
|
|
|
|
- <LinkedFileInfo key={path} file={file} />
|
|
|
|
|
- ))}
|
|
|
|
|
- {file.linkedFileData && (
|
|
|
|
|
- <button
|
|
|
|
|
- className="btn btn-success"
|
|
|
|
|
- onClick={refreshFile}
|
|
|
|
|
- disabled={refreshing}
|
|
|
|
|
- >
|
|
|
|
|
- <Icon type="refresh" spin={refreshing} modifier="fw" />
|
|
|
|
|
- <span>{refreshing ? t('refreshing') + '...' : t('refresh')}</span>
|
|
|
|
|
- </button>
|
|
|
|
|
- )}
|
|
|
|
|
-
|
|
|
|
|
- <a
|
|
|
|
|
- href={`/project/${window.project_id}/file/${file.id}`}
|
|
|
|
|
- className="btn btn-info"
|
|
|
|
|
- >
|
|
|
|
|
- <Icon type="download" modifier="fw" />
|
|
|
|
|
- <span>{' ' + t('download')}</span>
|
|
|
|
|
- </a>
|
|
|
|
|
- {refreshError && (
|
|
|
|
|
- <div className="row">
|
|
|
|
|
- <br />
|
|
|
|
|
- <div className="alert alert-danger col-md-6 col-md-offset-3">
|
|
|
|
|
- Error: {refreshError}
|
|
|
|
|
- {tprLinkedFileRefreshError.map(
|
|
|
|
|
- ({ import: { LinkedFileRefreshError }, path }) => (
|
|
|
|
|
- <LinkedFileRefreshError key={path} file={file} />
|
|
|
|
|
- )
|
|
|
|
|
- )}
|
|
|
|
|
- </div>
|
|
|
|
|
- </div>
|
|
|
|
|
- )}
|
|
|
|
|
- </div>
|
|
|
|
|
- )
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-BinaryFileHeader.propTypes = {
|
|
|
|
|
- file: PropTypes.shape({
|
|
|
|
|
- id: PropTypes.string,
|
|
|
|
|
- name: PropTypes.string,
|
|
|
|
|
- linkedFileData: PropTypes.object
|
|
|
|
|
- }).isRequired,
|
|
|
|
|
- storeReferencesKeys: PropTypes.func.isRequired
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function UrlProvider({ file }) {
|
|
|
|
|
- return (
|
|
|
|
|
- <p>
|
|
|
|
|
- <Icon
|
|
|
|
|
- type="external-link-square"
|
|
|
|
|
- modifier="rotate-180"
|
|
|
|
|
- classes={{ icon: 'linked-file-icon' }}
|
|
|
|
|
- />
|
|
|
|
|
-
|
|
|
|
|
- <Trans
|
|
|
|
|
- i18nKey="imported_from_external_provider_at_date"
|
|
|
|
|
- components={
|
|
|
|
|
- /* eslint-disable-next-line jsx-a11y/anchor-has-content, react/jsx-key */
|
|
|
|
|
- [<a href={file.linkedFileData.url} />]
|
|
|
|
|
- }
|
|
|
|
|
- values={{
|
|
|
|
|
- shortenedUrl: shortenedUrl(file.linkedFileData.url),
|
|
|
|
|
- formattedDate: formatTime(file.created),
|
|
|
|
|
- relativeDate: relativeDate(file.created)
|
|
|
|
|
- }}
|
|
|
|
|
- />
|
|
|
|
|
- </p>
|
|
|
|
|
- )
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-UrlProvider.propTypes = {
|
|
|
|
|
- file: PropTypes.shape({
|
|
|
|
|
- linkedFileData: PropTypes.object,
|
|
|
|
|
- created: PropTypes.string
|
|
|
|
|
- }).isRequired
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function ProjectFilePathProvider({ file }) {
|
|
|
|
|
- /* eslint-disable jsx-a11y/anchor-has-content, react/jsx-key */
|
|
|
|
|
- return (
|
|
|
|
|
- <p>
|
|
|
|
|
- <Icon
|
|
|
|
|
- type="external-link-square"
|
|
|
|
|
- modifier="rotate-180"
|
|
|
|
|
- classes={{ icon: 'linked-file-icon' }}
|
|
|
|
|
- />
|
|
|
|
|
-
|
|
|
|
|
- <Trans
|
|
|
|
|
- i18nKey="imported_from_another_project_at_date"
|
|
|
|
|
- components={
|
|
|
|
|
- file.linkedFileData.v1_source_doc_id
|
|
|
|
|
- ? [<span />]
|
|
|
|
|
- : [
|
|
|
|
|
- <a
|
|
|
|
|
- href={`/project/${file.linkedFileData.source_project_id}`}
|
|
|
|
|
- target="_blank"
|
|
|
|
|
- />
|
|
|
|
|
- ]
|
|
|
|
|
- }
|
|
|
|
|
- values={{
|
|
|
|
|
- sourceEntityPath: file.linkedFileData.source_entity_path.slice(1),
|
|
|
|
|
- formattedDate: formatTime(file.created),
|
|
|
|
|
- relativeDate: relativeDate(file.created)
|
|
|
|
|
- }}
|
|
|
|
|
- />
|
|
|
|
|
- </p>
|
|
|
|
|
- /* esline-enable jsx-a11y/anchor-has-content, react/jsx-key */
|
|
|
|
|
- )
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-ProjectFilePathProvider.propTypes = {
|
|
|
|
|
- file: PropTypes.shape({
|
|
|
|
|
- linkedFileData: PropTypes.object,
|
|
|
|
|
- created: PropTypes.string
|
|
|
|
|
- }).isRequired
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-function ProjectOutputFileProvider({ file }) {
|
|
|
|
|
- return (
|
|
|
|
|
- <p>
|
|
|
|
|
- <Icon
|
|
|
|
|
- type="external-link-square"
|
|
|
|
|
- modifier="rotate-180"
|
|
|
|
|
- classes={{ icon: 'linked-file-icon' }}
|
|
|
|
|
- />
|
|
|
|
|
-
|
|
|
|
|
- <Trans
|
|
|
|
|
- i18nKey="imported_from_the_output_of_another_project_at_date"
|
|
|
|
|
- components={
|
|
|
|
|
- file.linkedFileData.v1_source_doc_id
|
|
|
|
|
- ? [<span />]
|
|
|
|
|
- : [
|
|
|
|
|
- <a
|
|
|
|
|
- href={`/project/${file.linkedFileData.source_project_id}`}
|
|
|
|
|
- target="_blank"
|
|
|
|
|
- />
|
|
|
|
|
- ]
|
|
|
|
|
- }
|
|
|
|
|
- values={{
|
|
|
|
|
- sourceOutputFilePath: file.linkedFileData.source_output_file_path,
|
|
|
|
|
- formattedDate: formatTime(file.created),
|
|
|
|
|
- relativeDate: relativeDate(file.created)
|
|
|
|
|
- }}
|
|
|
|
|
- />
|
|
|
|
|
- </p>
|
|
|
|
|
- )
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-ProjectOutputFileProvider.propTypes = {
|
|
|
|
|
- file: PropTypes.shape({
|
|
|
|
|
- linkedFileData: PropTypes.object,
|
|
|
|
|
- created: PropTypes.string
|
|
|
|
|
- }).isRequired
|
|
|
|
|
-}
|
|
|