mirror of
https://github.com/overleaf/overleaf.git
synced 2025-04-22 02:18:27 +00:00
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:
parent
ce0d5fd383
commit
faff4f1d7e
10 changed files with 46 additions and 97 deletions
|
@ -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 = {
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 />}
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 }
|
||||
)
|
||||
|
|
|
@ -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',
|
||||
})
|
||||
})
|
||||
|
|
|
@ -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'
|
||||
)
|
||||
|
||||
|
|
|
@ -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'
|
||||
)
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue