mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
d6de6da781
* [web] Remove unnecessary divs around `fileInfo` * [web] Add file-view SCSS style * [web] Simplify `TPRFileViewInfo` * [web] Add div for action buttons * [web] Misc. simplifications * [web] Add Overleaf logo in bg when selecting multiple files * [web] Add message when multiple files are selected * [web] Add .full-size class * [web] Import styles from LESS file * [web] Update icons, use MaterialIcon * [web] Use OLButton * [web] Add missing space between icons and text * [web] Adjust margins * [web] Fix alert button * [web] Update Alerts * [web] Update `FileViewLoadingIndicator` * [web] Fix test "shows a loading indicator..." This was failing because `LoadingSpinner` is shown after a setTimeout. Maybe we can skip this setTimeout when delay==0 ? * [web] Remove Row/Col around error notifications * [web] Replace `!!` by `Boolean` Co-authored-by: ilkin-overleaf <100852799+ilkin-overleaf@users.noreply.github.com> * [web] Use `alert` class in BS3 only Co-authored-by: Ilkin Ismailov <ilkin.ismailov@overleaf.com> * [web] Update "Go to settings" to OLButton Co-authored-by: Ilkin Ismailov <ilkin.ismailov@overleaf.com> * [web] Use `BootstrapVersionSwitcher` instead of `isBootstrap5` Co-authored-by: Ilkin Ismailov <ilkin.ismailov@overleaf.com> * [web] Align Alert content to the left in BS5 * [web] Remove `leadingIcon` on Refresh buttons * [web] Make the download link be an OLButton * [web] Set `tpr-refresh-error` in BS3 only Co-authored-by: Rebeka Dekany <50901361+rebekadekany@users.noreply.github.com> * [web] Use `var(--white);` instead of `white` Co-authored-by: Rebeka <rebeka.dekany@overleaf.com> * [web] Update OLButton size (small -> sm) --------- Co-authored-by: ilkin-overleaf <100852799+ilkin-overleaf@users.noreply.github.com> Co-authored-by: Ilkin Ismailov <ilkin.ismailov@overleaf.com> Co-authored-by: Rebeka Dekany <50901361+rebekadekany@users.noreply.github.com> Co-authored-by: Rebeka <rebeka.dekany@overleaf.com> GitOrigin-RevId: 04f369c0f1a53d47619a1570648ee58de5050751
209 lines
6.4 KiB
TypeScript
209 lines
6.4 KiB
TypeScript
import { useState, type ElementType } from 'react'
|
|
import { Trans, useTranslation } from 'react-i18next'
|
|
|
|
import Icon from '../../../shared/components/icon'
|
|
import { formatTime, relativeDate } from '../../utils/format-date'
|
|
import { useFileTreeData } from '@/shared/context/file-tree-data-context'
|
|
import { useProjectContext } from '@/shared/context/project-context'
|
|
|
|
import { Nullable } from '../../../../../types/utils'
|
|
import importOverleafModules from '../../../../macros/import-overleaf-module.macro'
|
|
import { LinkedFileIcon } from './file-view-icons'
|
|
import { BinaryFile, hasProvider, LinkedFile } from '../types/binary-file'
|
|
import FileViewRefreshButton from './file-view-refresh-button'
|
|
import FileViewRefreshError from './file-view-refresh-error'
|
|
import { useSnapshotContext } from '@/features/ide-react/context/snapshot-context'
|
|
import MaterialIcon from '@/shared/components/material-icon'
|
|
import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher'
|
|
import OLButton from '@/features/ui/components/ol/ol-button'
|
|
|
|
const tprFileViewInfo = importOverleafModules('tprFileViewInfo') as {
|
|
import: { TPRFileViewInfo: ElementType }
|
|
path: string
|
|
}[]
|
|
|
|
const tprFileViewNotOriginalImporter = importOverleafModules(
|
|
'tprFileViewNotOriginalImporter'
|
|
) as {
|
|
import: { TPRFileViewNotOriginalImporter: ElementType }
|
|
path: string
|
|
}[]
|
|
|
|
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: string) {
|
|
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
|
|
}
|
|
|
|
type FileViewHeaderProps = {
|
|
file: BinaryFile
|
|
}
|
|
|
|
export default function FileViewHeader({ file }: FileViewHeaderProps) {
|
|
const { _id: projectId } = useProjectContext()
|
|
const { fileTreeReadOnly } = useFileTreeData()
|
|
const { fileTreeFromHistory } = useSnapshotContext()
|
|
const { t } = useTranslation()
|
|
|
|
const [refreshError, setRefreshError] = useState<Nullable<string>>(null)
|
|
|
|
let fileInfo
|
|
if (file.linkedFileData) {
|
|
if (hasProvider(file, 'url')) {
|
|
fileInfo = <UrlProvider file={file} />
|
|
} else if (hasProvider(file, 'project_file')) {
|
|
fileInfo = <ProjectFilePathProvider file={file} />
|
|
} else if (hasProvider(file, 'project_output_file')) {
|
|
fileInfo = <ProjectOutputFileProvider file={file} />
|
|
}
|
|
}
|
|
|
|
return (
|
|
<>
|
|
{file.linkedFileData && fileInfo}
|
|
{file.linkedFileData &&
|
|
tprFileViewInfo.map(({ import: { TPRFileViewInfo }, path }) => (
|
|
<TPRFileViewInfo key={path} file={file} />
|
|
))}
|
|
<div className="file-view-buttons">
|
|
{file.linkedFileData && !fileTreeReadOnly && (
|
|
<FileViewRefreshButton
|
|
file={file}
|
|
setRefreshError={setRefreshError}
|
|
/>
|
|
)}
|
|
<OLButton
|
|
variant="secondary"
|
|
download={file.name}
|
|
href={
|
|
fileTreeFromHistory
|
|
? `/project/${projectId}/blob/${file.hash}`
|
|
: `/project/${projectId}/file/${file.id}`
|
|
}
|
|
>
|
|
<BootstrapVersionSwitcher
|
|
bs3={<Icon type="download" fw />}
|
|
bs5={<MaterialIcon type="download" className="align-middle" />}
|
|
/>{' '}
|
|
<span>{t('download')}</span>
|
|
</OLButton>
|
|
</div>
|
|
{file.linkedFileData &&
|
|
tprFileViewNotOriginalImporter.map(
|
|
({ import: { TPRFileViewNotOriginalImporter }, path }) => (
|
|
<TPRFileViewNotOriginalImporter key={path} file={file} />
|
|
)
|
|
)[0]}
|
|
{refreshError && (
|
|
<FileViewRefreshError file={file} refreshError={refreshError} />
|
|
)}
|
|
</>
|
|
)
|
|
}
|
|
|
|
type UrlProviderProps = {
|
|
file: LinkedFile<'url'>
|
|
}
|
|
|
|
function UrlProvider({ file }: UrlProviderProps) {
|
|
return (
|
|
<p>
|
|
<LinkedFileIcon />
|
|
|
|
<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),
|
|
}}
|
|
shouldUnescape
|
|
tOptions={{ interpolation: { escapeValue: true } }}
|
|
/>
|
|
</p>
|
|
)
|
|
}
|
|
|
|
type ProjectFilePathProviderProps = {
|
|
file: LinkedFile<'project_file'>
|
|
}
|
|
|
|
function ProjectFilePathProvider({ file }: ProjectFilePathProviderProps) {
|
|
/* eslint-disable jsx-a11y/anchor-has-content, react/jsx-key */
|
|
return (
|
|
<p>
|
|
<LinkedFileIcon />{' '}
|
|
<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"
|
|
rel="noopener"
|
|
/>,
|
|
]
|
|
}
|
|
values={{
|
|
sourceEntityPath: file.linkedFileData.source_entity_path.slice(1),
|
|
formattedDate: formatTime(file.created),
|
|
relativeDate: relativeDate(file.created),
|
|
}}
|
|
shouldUnescape
|
|
tOptions={{ interpolation: { escapeValue: true } }}
|
|
/>
|
|
</p>
|
|
/* esline-enable jsx-a11y/anchor-has-content, react/jsx-key */
|
|
)
|
|
}
|
|
|
|
type ProjectOutputFileProviderProps = {
|
|
file: LinkedFile<'project_output_file'>
|
|
}
|
|
|
|
function ProjectOutputFileProvider({ file }: ProjectOutputFileProviderProps) {
|
|
return (
|
|
<p>
|
|
<LinkedFileIcon />
|
|
|
|
<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"
|
|
rel="noopener"
|
|
/>,
|
|
]
|
|
}
|
|
values={{
|
|
sourceOutputFilePath: file.linkedFileData.source_output_file_path,
|
|
formattedDate: formatTime(file.created),
|
|
relativeDate: relativeDate(file.created),
|
|
}}
|
|
shouldUnescape
|
|
tOptions={{ interpolation: { escapeValue: true } }}
|
|
/>
|
|
</p>
|
|
)
|
|
}
|