Merge pull request #22178 from overleaf/jpa-file-view-hash-2

[web] migrate file-view to download from history-v1 (via web) 2/2

GitOrigin-RevId: 93172cbbd2d19a55bb27f5b0ca0b494f815a3632
This commit is contained in:
Jakob Ackermann 2024-11-28 09:43:28 +01:00 committed by Copybot
parent ce0d5fd383
commit faff4f1d7e
10 changed files with 46 additions and 97 deletions

View file

@ -33,7 +33,6 @@ import {
} from '../errors'
import { Folder } from '../../../../../types/folder'
import { useReferencesContext } from '@/features/ide-react/context/references-context'
import { useSnapshotContext } from '@/features/ide-react/context/snapshot-context'
type DroppedFile = File & {
relativePath?: string
@ -221,7 +220,6 @@ export const FileTreeActionableProvider: FC = ({ children }) => {
const { _id: projectId } = useProjectContext()
const { fileTreeReadOnly } = useFileTreeData()
const { indexAllReferences } = useReferencesContext()
const { fileTreeFromHistory } = useSnapshotContext()
const [state, dispatch] = useReducer(
fileTreeReadOnly
@ -494,17 +492,14 @@ export const FileTreeActionableProvider: FC = ({ children }) => {
const selectedEntity = findInTree(fileTreeData, selectedEntityId)
if (selectedEntity?.type === 'fileRef') {
if (fileTreeFromHistory) {
return `/project/${projectId}/blob/${selectedEntity.entity.hash}`
}
return `/project/${projectId}/file/${selectedEntityId}`
return `/project/${projectId}/blob/${selectedEntity.entity.hash}?fallback=${selectedEntityId}`
}
if (selectedEntity?.type === 'doc') {
return `/project/${projectId}/doc/${selectedEntityId}/download`
}
}
}, [fileTreeData, projectId, selectedEntityIds, fileTreeFromHistory])
}, [fileTreeData, projectId, selectedEntityIds])
// TODO: wrap in useMemo
const value = {

View file

@ -10,7 +10,6 @@ import {
pathInFolder,
} from '@/features/file-tree/util/path'
import { PreviewPath } from '../../../../../types/preview-path'
import { useSnapshotContext } from '@/features/ide-react/context/snapshot-context'
type FileTreePathContextValue = {
pathInFolder: (id: string) => string | null
@ -25,7 +24,6 @@ export const FileTreePathContext = createContext<
export const FileTreePathProvider: FC = ({ children }) => {
const { fileTreeData }: { fileTreeData: Folder } = useFileTreeData()
const { fileTreeFromHistory } = useSnapshotContext()
const projectId = getMeta('ol-project_id')
const pathInFileTree = useCallback(
@ -39,9 +37,8 @@ export const FileTreePathProvider: FC = ({ children }) => {
)
const previewByPathInFileTree = useCallback(
(path: string) =>
previewByPath(fileTreeData, projectId, path, fileTreeFromHistory),
[fileTreeData, projectId, fileTreeFromHistory]
(path: string) => previewByPath(fileTreeData, projectId, path),
[fileTreeData, projectId]
)
const dirnameInFileTree = useCallback(

View file

@ -106,8 +106,7 @@ export function findEntityByPath(
export function previewByPath(
folder: Folder,
projectId: string,
path: string,
fileTreeFromHistory: boolean
path: string
): PreviewPath | null {
for (const suffix of [
'',
@ -125,9 +124,7 @@ export function previewByPath(
if (result?.type === 'fileRef') {
const { name, _id: id, hash } = result.entity
return {
url: fileTreeFromHistory
? `/project/${projectId}/blob/${hash}`
: `/project/${projectId}/file/${id}`,
url: `/project/${projectId}/blob/${hash}?fallback=${id}`,
extension: name.slice(name.lastIndexOf('.') + 1),
}
}

View file

@ -12,7 +12,6 @@ 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'
@ -53,7 +52,6 @@ type FileViewHeaderProps = {
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)
@ -86,11 +84,7 @@ export default function FileViewHeader({ file }: FileViewHeaderProps) {
<OLButton
variant="secondary"
download={file.name}
href={
fileTreeFromHistory
? `/project/${projectId}/blob/${file.hash}`
: `/project/${projectId}/file/${file.id}`
}
href={`/project/${projectId}/blob/${file.hash}?fallback=${file.id}`}
>
<BootstrapVersionSwitcher
bs3={<Icon type="download" fw />}

View file

@ -1,5 +1,4 @@
import { useProjectContext } from '../../../shared/context/project-context'
import { useSnapshotContext } from '@/features/ide-react/context/snapshot-context'
import { BinaryFile } from '@/features/file-view/types/binary-file'
export default function FileViewImage({
@ -12,15 +11,10 @@ export default function FileViewImage({
onError: () => void
}) {
const { _id: projectId } = useProjectContext()
const { fileTreeFromHistory } = useSnapshotContext()
return (
<img
src={
fileTreeFromHistory
? `/project/${projectId}/blob/${file.hash}`
: `/project/${projectId}/file/${file.id}`
}
src={`/project/${projectId}/blob/${file.hash}?fallback=${file.id}`}
onLoad={onLoad}
onError={onError}
alt={file.name}

View file

@ -3,7 +3,6 @@ import { useProjectContext } from '../../../shared/context/project-context'
import { debugConsole } from '@/utils/debugging'
import useAbortController from '../../../shared/hooks/use-abort-controller'
import { BinaryFile } from '@/features/file-view/types/binary-file'
import { useSnapshotContext } from '@/features/ide-react/context/snapshot-context'
const MAX_FILE_SIZE = 2 * 1024 * 1024
@ -17,7 +16,6 @@ export default function FileViewText({
onError: () => void
}) {
const { _id: projectId } = useProjectContext()
const { fileTreeFromHistory } = useSnapshotContext()
const [textPreview, setTextPreview] = useState('')
const [shouldShowDots, setShouldShowDots] = useState(false)
@ -30,9 +28,7 @@ export default function FileViewText({
if (inFlight) {
return
}
let path = fileTreeFromHistory
? `/project/${projectId}/blob/${file.hash}`
: `/project/${projectId}/file/${file.id}`
const path = `/project/${projectId}/blob/${file.hash}?fallback=${file.id}`
const fetchContentLengthTimeout = setTimeout(
() => fetchContentLengthController.abort(),
10000
@ -45,32 +41,27 @@ export default function FileViewText({
})
.then(fileSize => {
let truncated = false
let maxSize = null
const headers = new Headers()
if (fileSize && Number(fileSize) > MAX_FILE_SIZE) {
truncated = true
maxSize = MAX_FILE_SIZE
}
if (maxSize != null) {
path += `?range=0-${maxSize}`
headers.set('Range', `bytes=0-${MAX_FILE_SIZE}`)
}
fetchDataTimeout = window.setTimeout(
() => fetchDataController.abort(),
60000
)
return fetch(path, { signal: fetchDataController.signal }).then(
response => {
return response.text().then(text => {
if (truncated) {
text = text.replace(/\n.*$/, '')
}
const signal = fetchDataController.signal
return fetch(path, { signal, headers }).then(response => {
return response.text().then(text => {
if (truncated) {
text = text.replace(/\n.*$/, '')
}
setTextPreview(text)
onLoad()
setShouldShowDots(truncated)
})
}
)
setTextPreview(text)
onLoad()
setShouldShowDots(truncated)
})
})
})
.catch(err => {
debugConsole.error('Error fetching file contents', err)
@ -82,7 +73,6 @@ export default function FileViewText({
clearTimeout(fetchDataTimeout)
})
}, [
fileTreeFromHistory,
projectId,
file.id,
file.hash,

View file

@ -21,7 +21,7 @@ extra parameters or packages included.
const setupFetchMock = fetchMock => {
return fetchMock
.head('express:/project/:project_id/file/:file_id', {
.head('express:/project/:project_id/blob/:hash', {
status: 201,
headers: { 'Content-Length': 10000 },
})
@ -41,10 +41,9 @@ const fileData = {
export const FileFromUrl = args => {
useFetchMock(fetchMock =>
setupFetchMock(fetchMock).get(
'express:/project/:project_id/file/:file_id',
{ body: bodies.latex }
)
setupFetchMock(fetchMock).get('express:/project/:project_id/blob/:hash', {
body: bodies.latex,
})
)
return <FileView {...args} />
@ -61,10 +60,9 @@ FileFromUrl.args = {
export const FileFromProjectWithLinkableProjectId = args => {
useFetchMock(fetchMock =>
setupFetchMock(fetchMock).get(
'express:/project/:project_id/file/:file_id',
{ body: bodies.latex }
)
setupFetchMock(fetchMock).get('express:/project/:project_id/blob/:hash', {
body: bodies.latex,
})
)
return <FileView {...args} />
@ -82,10 +80,9 @@ FileFromProjectWithLinkableProjectId.args = {
export const FileFromProjectWithoutLinkableProjectId = args => {
useFetchMock(fetchMock =>
setupFetchMock(fetchMock).get(
'express:/project/:project_id/file/:file_id',
{ body: bodies.latex }
)
setupFetchMock(fetchMock).get('express:/project/:project_id/blob/:hash', {
body: bodies.latex,
})
)
return <FileView {...args} />
@ -103,10 +100,9 @@ FileFromProjectWithoutLinkableProjectId.args = {
export const FileFromProjectOutputWithLinkableProject = args => {
useFetchMock(fetchMock =>
setupFetchMock(fetchMock).get(
'express:/project/:project_id/file/:file_id',
{ body: bodies.latex }
)
setupFetchMock(fetchMock).get('express:/project/:project_id/blob/:hash', {
body: bodies.latex,
})
)
return <FileView {...args} />
@ -124,10 +120,9 @@ FileFromProjectOutputWithLinkableProject.args = {
export const FileFromProjectOutputWithoutLinkableProjectId = args => {
useFetchMock(fetchMock =>
setupFetchMock(fetchMock).get(
'express:/project/:project_id/file/:file_id',
{ body: bodies.latex }
)
setupFetchMock(fetchMock).get('express:/project/:project_id/blob/:hash', {
body: bodies.latex,
})
)
return <FileView {...args} />
@ -165,7 +160,7 @@ ImageFile.args = {
export const TextFile = args => {
useFetchMock(fetchMock =>
setupFetchMock(fetchMock).get(
'express:/project/:project_id/file/:file_id',
'express:/project/:project_id/blob/:hash',
{ body: bodies.text },
{ overwriteRoutes: true }
)
@ -187,7 +182,7 @@ TextFile.args = {
export const UploadedFile = args => {
useFetchMock(fetchMock =>
setupFetchMock(fetchMock).head(
'express:/project/:project_id/file/:file_id',
'express:/project/:project_id/blob/:hash',
{ status: 500 },
{ overwriteRoutes: true }
)

View file

@ -151,23 +151,10 @@ describe('Path utils', function () {
const preview = previewByPath(
rootFolder,
'test-project-id',
'test-folder/example.png',
false
'test-folder/example.png'
)
expect(preview).to.deep.equal({
url: '/project/test-project-id/file/test-file-in-folder',
extension: 'png',
})
})
it('returns handles history file-tree', function () {
const preview = previewByPath(
rootFolder,
'test-project-id',
'test-folder/example.png',
true
)
expect(preview).to.deep.equal({
url: '/project/test-project-id/blob/42',
url: '/project/test-project-id/blob/42?fallback=test-file-in-folder',
extension: 'png',
})
})

View file

@ -23,12 +23,12 @@ describe('<FileViewText/>', function () {
})
it('renders a text view', async function () {
fetchMock.head('express:/project/:project_id/file/:file_id', {
fetchMock.head('express:/project/:project_id/blob/:hash', {
status: 201,
headers: { 'Content-Length': 10000 },
})
fetchMock.get(
'express:/project/:project_id/file/:file_id',
'express:/project/:project_id/blob/:hash',
'Text file content'
)

View file

@ -36,12 +36,12 @@ describe('<FileView/>', function () {
describe('for a text file', function () {
it('shows a loading indicator while the file is loading', async function () {
fetchMock.head('express:/project/:project_id/file/:file_id', {
fetchMock.head('express:/project/:project_id/blob/:hash', {
status: 201,
headers: { 'Content-Length': 10000 },
})
fetchMock.get(
'express:/project/:project_id/file/:file_id',
'express:/project/:project_id/blob/:hash',
'Text file content'
)