diff --git a/services/web/frontend/js/features/source-editor/components/figure-modal/file-name-input.tsx b/services/web/frontend/js/features/source-editor/components/figure-modal/file-name-input.tsx index 5cc1f47c98..cce93f1d0e 100644 --- a/services/web/frontend/js/features/source-editor/components/figure-modal/file-name-input.tsx +++ b/services/web/frontend/js/features/source-editor/components/figure-modal/file-name-input.tsx @@ -6,9 +6,9 @@ import { useState, } from 'react' import { File, FileOrDirectory } from '../../utils/file' -import useScopeValue from '../../../../shared/hooks/use-scope-value' import { Alert } from 'react-bootstrap' import { useTranslation } from 'react-i18next' +import { useCurrentProjectFolders } from '@/features/source-editor/hooks/use-current-project-folders' type FileNameInputProps = Omit< DetailedHTMLProps, HTMLInputElement>, @@ -57,7 +57,7 @@ export const FileNameInput = ({ }: FileNameInputProps) => { const { t } = useTranslation() const [overlap, setOverlap] = useState(false) - const [rootFolder] = useScopeValue('rootFolder') + const { rootFolder } = useCurrentProjectFolders() const { value } = props useEffect(() => { diff --git a/services/web/frontend/js/features/source-editor/components/figure-modal/file-relocator.tsx b/services/web/frontend/js/features/source-editor/components/figure-modal/file-relocator.tsx index 49b1205f00..8befa99a10 100644 --- a/services/web/frontend/js/features/source-editor/components/figure-modal/file-relocator.tsx +++ b/services/web/frontend/js/features/source-editor/components/figure-modal/file-relocator.tsx @@ -2,7 +2,7 @@ import { useCallback } from 'react' import { FileNameInput } from './file-name-input' import { File } from '../../utils/file' import { Select } from '../../../../shared/components/select' -import { useCurrentProjectFolders } from '../../hooks/useCurrentProjectFolders' +import { useCurrentProjectFolders } from '../../hooks/use-current-project-folders' import { useTranslation } from 'react-i18next' export const FileRelocator = ({ @@ -25,7 +25,7 @@ export const FileRelocator = ({ setNameDirty: (nameDirty: boolean) => void }) => { const { t } = useTranslation() - const [folders, rootFile] = useCurrentProjectFolders() + const { folders, rootFile } = useCurrentProjectFolders() const nameChanged = useCallback( (e: React.ChangeEvent) => { diff --git a/services/web/frontend/js/features/source-editor/components/figure-modal/file-sources/figure-modal-other-project-source.tsx b/services/web/frontend/js/features/source-editor/components/figure-modal/file-sources/figure-modal-other-project-source.tsx index 168f1158cd..f997c78d83 100644 --- a/services/web/frontend/js/features/source-editor/components/figure-modal/file-sources/figure-modal-other-project-source.tsx +++ b/services/web/frontend/js/features/source-editor/components/figure-modal/file-sources/figure-modal-other-project-source.tsx @@ -14,7 +14,7 @@ import { useProjectOutputFiles, } from '../../../../file-tree/hooks/use-project-output-files' import { Button } from 'react-bootstrap' -import { useCurrentProjectFolders } from '../../../hooks/useCurrentProjectFolders' +import { useCurrentProjectFolders } from '../../../hooks/use-current-project-folders' import { File, isImageEntity } from '../../../utils/file' import { postJSON } from '../../../../../infrastructure/fetch-json' import { useProjectContext } from '../../../../../shared/context/project-context' @@ -39,7 +39,7 @@ export const FigureModalOtherProjectSource: FC = () => { const [nameDirty, setNameDirty] = useState(false) const [name, setName] = useState('') const [folder, setFolder] = useState(null) - const [, rootFile] = useCurrentProjectFolders() + const { rootFile } = useCurrentProjectFolders() const [file, setFile] = useState(null) const FileSelector = usingOutputFiles ? SelectFromProjectOutputFiles diff --git a/services/web/frontend/js/features/source-editor/components/figure-modal/file-sources/figure-modal-project-source.tsx b/services/web/frontend/js/features/source-editor/components/figure-modal/file-sources/figure-modal-project-source.tsx index 2bc035c115..10dba3131d 100644 --- a/services/web/frontend/js/features/source-editor/components/figure-modal/file-sources/figure-modal-project-source.tsx +++ b/services/web/frontend/js/features/source-editor/components/figure-modal/file-sources/figure-modal-project-source.tsx @@ -1,13 +1,13 @@ import { FC, useMemo } from 'react' -import useScopeValue from '../../../../../shared/hooks/use-scope-value' import { Select } from '../../../../../shared/components/select' import { useFigureModalContext } from '../figure-modal-context' -import { FileOrDirectory, filterFiles, isImageFile } from '../../../utils/file' +import { filterFiles, isImageFile } from '../../../utils/file' import { useTranslation } from 'react-i18next' +import { useCurrentProjectFolders } from '@/features/source-editor/hooks/use-current-project-folders' export const FigureModalCurrentProjectSource: FC = () => { const { t } = useTranslation() - const [rootFolder] = useScopeValue('rootFolder') + const { rootFolder } = useCurrentProjectFolders() const files = useMemo( () => filterFiles(rootFolder)?.filter(isImageFile), [rootFolder] diff --git a/services/web/frontend/js/features/source-editor/components/figure-modal/file-sources/figure-modal-upload-source.tsx b/services/web/frontend/js/features/source-editor/components/figure-modal/file-sources/figure-modal-upload-source.tsx index 74c10efb50..87b7280661 100644 --- a/services/web/frontend/js/features/source-editor/components/figure-modal/file-sources/figure-modal-upload-source.tsx +++ b/services/web/frontend/js/features/source-editor/components/figure-modal/file-sources/figure-modal-upload-source.tsx @@ -1,6 +1,6 @@ import { FC, useCallback, useEffect, useState } from 'react' import { useFigureModalContext } from '../figure-modal-context' -import { useCurrentProjectFolders } from '../../../hooks/useCurrentProjectFolders' +import { useCurrentProjectFolders } from '../../../hooks/use-current-project-folders' import { File } from '../../../utils/file' import { Dashboard, useUppy } from '@uppy/react' import '@uppy/core/dist/style.css' @@ -33,7 +33,7 @@ export const FigureModalUploadFileSource: FC = () => { const view = useCodeMirrorViewContext() const { dispatch, pastedImageData } = useFigureModalContext() const { _id: projectId } = useProjectContext() - const [, rootFolder] = useCurrentProjectFolders() + const { rootFile } = useCurrentProjectFolders() const [folder, setFolder] = useState(null) const [nameDirty, setNameDirty] = useState(false) // Files are immutable, so this will point to a (possibly) old version of the file @@ -77,7 +77,7 @@ export const FigureModalUploadFileSource: FC = () => { if (!uploadResult.successful) { throw new Error('Upload failed') } - const uploadFolder = folder ?? rootFolder + const uploadFolder = folder ?? rootFile return uploadFolder.path === '' && uploadFolder.name === 'rootFolder' ? `${name}` : `${uploadFolder.path ? uploadFolder.path + '/' : ''}${ @@ -86,7 +86,7 @@ export const FigureModalUploadFileSource: FC = () => { }, }) }, - [dispatch, rootFolder, uppy, view] + [dispatch, rootFile, uppy, view] ) useEffect(() => { @@ -130,7 +130,7 @@ export const FigureModalUploadFileSource: FC = () => { xhrUpload: { ...(file as any).xhrUpload, endpoint: `/project/${projectId}/upload?folder_id=${ - (folder ?? rootFolder).id + (folder ?? rootFile).id }`, }, }) @@ -179,7 +179,7 @@ export const FigureModalUploadFileSource: FC = () => { }, [ uppy, folder, - rootFolder, + rootFile, name, nameDirty, dispatchUploadAction, @@ -245,7 +245,7 @@ export const FigureModalUploadFileSource: FC = () => { name={name} nameDisabled={!file && !nameDirty} onFolderChanged={item => - dispatchUploadAction(name, file, item ?? rootFolder) + dispatchUploadAction(name, file, item ?? rootFile) } onNameChanged={name => dispatchUploadAction(name, file, folder)} setFolder={setFolder} diff --git a/services/web/frontend/js/features/source-editor/components/figure-modal/file-sources/figure-modal-url-source.tsx b/services/web/frontend/js/features/source-editor/components/figure-modal/file-sources/figure-modal-url-source.tsx index c6aa8cbe44..41c3318f5c 100644 --- a/services/web/frontend/js/features/source-editor/components/figure-modal/file-sources/figure-modal-url-source.tsx +++ b/services/web/frontend/js/features/source-editor/components/figure-modal/file-sources/figure-modal-url-source.tsx @@ -3,7 +3,7 @@ import { useFigureModalContext } from '../figure-modal-context' import { postJSON } from '../../../../../infrastructure/fetch-json' import { useProjectContext } from '../../../../../shared/context/project-context' import { File } from '../../../utils/file' -import { useCurrentProjectFolders } from '../../../hooks/useCurrentProjectFolders' +import { useCurrentProjectFolders } from '../../../hooks/use-current-project-folders' import { FileRelocator } from '../file-relocator' import { useTranslation } from 'react-i18next' import { useCodeMirrorViewContext } from '../../codemirror-editor' @@ -44,7 +44,7 @@ export const FigureModalUrlSource: FC = () => { const [nameDirty, setNameDirty] = useState(false) const [name, setName] = useState('') const { _id: projectId } = useProjectContext() - const [, rootFile] = useCurrentProjectFolders() + const { rootFile } = useCurrentProjectFolders() const [folder, setFolder] = useState(rootFile) const { dispatch, getPath } = useFigureModalContext() diff --git a/services/web/frontend/js/features/source-editor/hooks/use-current-project-folders.ts b/services/web/frontend/js/features/source-editor/hooks/use-current-project-folders.ts new file mode 100644 index 0000000000..bc156180f2 --- /dev/null +++ b/services/web/frontend/js/features/source-editor/hooks/use-current-project-folders.ts @@ -0,0 +1,51 @@ +import { useMemo } from 'react' +import { File, FileOrDirectory, filterFolders } from '../utils/file' +import { useFileTreeData } from '@/shared/context/file-tree-data-context' +import { Folder } from '../../../../../types/folder' +import { Doc } from '../../../../../types/doc' +import { FileRef } from '../../../../../types/file-ref' + +function docAdapter(doc: Doc): FileOrDirectory { + return { + id: doc._id, + name: doc.name, + type: 'doc', + } +} + +function fileRefAdapter(fileRef: FileRef): FileOrDirectory { + return { + id: fileRef._id, + name: fileRef.name, + type: 'file', + } +} + +function folderAdapter(folder: Folder): FileOrDirectory { + return { + id: folder._id, + name: folder.name, + type: 'folder', + children: folder.docs + .map(docAdapter) + .concat( + folder.fileRefs.map(fileRefAdapter), + folder.folders.map(folderAdapter) + ), + } +} + +export const useCurrentProjectFolders: () => { + folders: File[] | undefined + rootFile: File + rootFolder: FileOrDirectory +} = () => { + const { fileTreeData } = useFileTreeData() + + return useMemo(() => { + const rootFolder = folderAdapter(fileTreeData) + const rootFile = { ...rootFolder, path: '' } + const folders = filterFolders(rootFolder) + return { folders, rootFile, rootFolder } + }, [fileTreeData]) +} diff --git a/services/web/frontend/js/features/source-editor/hooks/useCurrentProjectFolders.ts b/services/web/frontend/js/features/source-editor/hooks/useCurrentProjectFolders.ts deleted file mode 100644 index 01569e2dac..0000000000 --- a/services/web/frontend/js/features/source-editor/hooks/useCurrentProjectFolders.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { useMemo } from 'react' -import useScopeValue from '../../../shared/hooks/use-scope-value' -import { File, FileOrDirectory, filterFolders } from '../utils/file' - -export const useCurrentProjectFolders: () => [ - File[] | undefined, - File -] = () => { - const [rootFolder] = useScopeValue('rootFolder') - const rootFile = { ...rootFolder, path: '' } - const folders = useMemo(() => filterFolders(rootFolder), [rootFolder]) - return [folders, rootFile] -} diff --git a/services/web/test/frontend/features/source-editor/helpers/mock-scope.ts b/services/web/test/frontend/features/source-editor/helpers/mock-scope.ts index f4d9fa6a19..02a4c65ab5 100644 --- a/services/web/test/frontend/features/source-editor/helpers/mock-scope.ts +++ b/services/web/test/frontend/features/source-editor/helpers/mock-scope.ts @@ -1,45 +1,12 @@ import { docId, mockDoc } from './mock-doc' -import { Folder } from '../../../../../types/folder' import { sleep } from '../../../helpers/sleep' +import { Folder } from '../../../../../types/folder' export const rootFolderId = '012345678901234567890123' export const figuresFolderId = '123456789012345678901234' export const figureId = '234567890123456789012345' export const mockScope = (content?: string) => { return { - rootFolder: { - id: rootFolderId, - name: 'rootFolder', - selected: false, - children: [ - { - id: docId, - name: 'test.tex', - selected: false, - type: 'doc', - }, - { - id: figuresFolderId, - name: 'figures', - selected: false, - type: 'folder', - children: [ - { - id: figureId, - name: 'frog.jpg', - selected: false, - type: 'file', - }, - { - id: 'fake-figure-id', - name: 'unicorn.png', - selected: false, - type: 'file', - }, - ], - }, - ], - }, settings: { fontSize: 12, fontFamily: 'monaco', @@ -66,7 +33,37 @@ export const mockScope = (content?: string) => { _id: 'test-project', name: 'Test Project', spellCheckLanguage: 'en', - rootFolder: [] as Folder[], + rootFolder: [ + { + _id: rootFolderId, + name: 'rootFolder', + docs: [ + { + _id: docId, + name: 'test.tex', + }, + ], + folders: [ + { + _id: figuresFolderId, + name: 'figures', + docs: [], + folders: [], + fileRefs: [ + { + _id: figureId, + name: 'frog.jpg', + }, + { + _id: 'fake-figure-id', + name: 'unicorn.png', + }, + ], + }, + ], + fileRefs: [], + }, + ] as Folder[], features: { trackChanges: true, },