Merge pull request #15444 from overleaf/td-remove-root-folder-scope-use

Remove use of root folder scope in figure modal

GitOrigin-RevId: d07247b644d312ef711f5601d3c10a3274e43416
This commit is contained in:
Tim Down 2023-10-26 11:01:27 +01:00 committed by Copybot
parent 13f246a85e
commit c34c95b46d
9 changed files with 101 additions and 66 deletions

View file

@ -6,9 +6,9 @@ import {
useState, useState,
} from 'react' } from 'react'
import { File, FileOrDirectory } from '../../utils/file' import { File, FileOrDirectory } from '../../utils/file'
import useScopeValue from '../../../../shared/hooks/use-scope-value'
import { Alert } from 'react-bootstrap' import { Alert } from 'react-bootstrap'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { useCurrentProjectFolders } from '@/features/source-editor/hooks/use-current-project-folders'
type FileNameInputProps = Omit< type FileNameInputProps = Omit<
DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>,
@ -57,7 +57,7 @@ export const FileNameInput = ({
}: FileNameInputProps) => { }: FileNameInputProps) => {
const { t } = useTranslation() const { t } = useTranslation()
const [overlap, setOverlap] = useState<boolean>(false) const [overlap, setOverlap] = useState<boolean>(false)
const [rootFolder] = useScopeValue<FileOrDirectory>('rootFolder') const { rootFolder } = useCurrentProjectFolders()
const { value } = props const { value } = props
useEffect(() => { useEffect(() => {

View file

@ -2,7 +2,7 @@ import { useCallback } from 'react'
import { FileNameInput } from './file-name-input' import { FileNameInput } from './file-name-input'
import { File } from '../../utils/file' import { File } from '../../utils/file'
import { Select } from '../../../../shared/components/select' import { Select } from '../../../../shared/components/select'
import { useCurrentProjectFolders } from '../../hooks/useCurrentProjectFolders' import { useCurrentProjectFolders } from '../../hooks/use-current-project-folders'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
export const FileRelocator = ({ export const FileRelocator = ({
@ -25,7 +25,7 @@ export const FileRelocator = ({
setNameDirty: (nameDirty: boolean) => void setNameDirty: (nameDirty: boolean) => void
}) => { }) => {
const { t } = useTranslation() const { t } = useTranslation()
const [folders, rootFile] = useCurrentProjectFolders() const { folders, rootFile } = useCurrentProjectFolders()
const nameChanged = useCallback( const nameChanged = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => { (e: React.ChangeEvent<HTMLInputElement>) => {

View file

@ -14,7 +14,7 @@ import {
useProjectOutputFiles, useProjectOutputFiles,
} from '../../../../file-tree/hooks/use-project-output-files' } from '../../../../file-tree/hooks/use-project-output-files'
import { Button } from 'react-bootstrap' 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 { File, isImageEntity } from '../../../utils/file'
import { postJSON } from '../../../../../infrastructure/fetch-json' import { postJSON } from '../../../../../infrastructure/fetch-json'
import { useProjectContext } from '../../../../../shared/context/project-context' import { useProjectContext } from '../../../../../shared/context/project-context'
@ -39,7 +39,7 @@ export const FigureModalOtherProjectSource: FC = () => {
const [nameDirty, setNameDirty] = useState<boolean>(false) const [nameDirty, setNameDirty] = useState<boolean>(false)
const [name, setName] = useState<string>('') const [name, setName] = useState<string>('')
const [folder, setFolder] = useState<File | null>(null) const [folder, setFolder] = useState<File | null>(null)
const [, rootFile] = useCurrentProjectFolders() const { rootFile } = useCurrentProjectFolders()
const [file, setFile] = useState<OutputEntity | Entity | null>(null) const [file, setFile] = useState<OutputEntity | Entity | null>(null)
const FileSelector = usingOutputFiles const FileSelector = usingOutputFiles
? SelectFromProjectOutputFiles ? SelectFromProjectOutputFiles

View file

@ -1,13 +1,13 @@
import { FC, useMemo } from 'react' import { FC, useMemo } from 'react'
import useScopeValue from '../../../../../shared/hooks/use-scope-value'
import { Select } from '../../../../../shared/components/select' import { Select } from '../../../../../shared/components/select'
import { useFigureModalContext } from '../figure-modal-context' 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 { useTranslation } from 'react-i18next'
import { useCurrentProjectFolders } from '@/features/source-editor/hooks/use-current-project-folders'
export const FigureModalCurrentProjectSource: FC = () => { export const FigureModalCurrentProjectSource: FC = () => {
const { t } = useTranslation() const { t } = useTranslation()
const [rootFolder] = useScopeValue<FileOrDirectory>('rootFolder') const { rootFolder } = useCurrentProjectFolders()
const files = useMemo( const files = useMemo(
() => filterFiles(rootFolder)?.filter(isImageFile), () => filterFiles(rootFolder)?.filter(isImageFile),
[rootFolder] [rootFolder]

View file

@ -1,6 +1,6 @@
import { FC, useCallback, useEffect, useState } from 'react' import { FC, useCallback, useEffect, useState } from 'react'
import { useFigureModalContext } from '../figure-modal-context' 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 { File } from '../../../utils/file'
import { Dashboard, useUppy } from '@uppy/react' import { Dashboard, useUppy } from '@uppy/react'
import '@uppy/core/dist/style.css' import '@uppy/core/dist/style.css'
@ -33,7 +33,7 @@ export const FigureModalUploadFileSource: FC = () => {
const view = useCodeMirrorViewContext() const view = useCodeMirrorViewContext()
const { dispatch, pastedImageData } = useFigureModalContext() const { dispatch, pastedImageData } = useFigureModalContext()
const { _id: projectId } = useProjectContext() const { _id: projectId } = useProjectContext()
const [, rootFolder] = useCurrentProjectFolders() const { rootFile } = useCurrentProjectFolders()
const [folder, setFolder] = useState<File | null>(null) const [folder, setFolder] = useState<File | null>(null)
const [nameDirty, setNameDirty] = useState<boolean>(false) const [nameDirty, setNameDirty] = useState<boolean>(false)
// Files are immutable, so this will point to a (possibly) old version of the file // 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) { if (!uploadResult.successful) {
throw new Error('Upload failed') throw new Error('Upload failed')
} }
const uploadFolder = folder ?? rootFolder const uploadFolder = folder ?? rootFile
return uploadFolder.path === '' && uploadFolder.name === 'rootFolder' return uploadFolder.path === '' && uploadFolder.name === 'rootFolder'
? `${name}` ? `${name}`
: `${uploadFolder.path ? uploadFolder.path + '/' : ''}${ : `${uploadFolder.path ? uploadFolder.path + '/' : ''}${
@ -86,7 +86,7 @@ export const FigureModalUploadFileSource: FC = () => {
}, },
}) })
}, },
[dispatch, rootFolder, uppy, view] [dispatch, rootFile, uppy, view]
) )
useEffect(() => { useEffect(() => {
@ -130,7 +130,7 @@ export const FigureModalUploadFileSource: FC = () => {
xhrUpload: { xhrUpload: {
...(file as any).xhrUpload, ...(file as any).xhrUpload,
endpoint: `/project/${projectId}/upload?folder_id=${ endpoint: `/project/${projectId}/upload?folder_id=${
(folder ?? rootFolder).id (folder ?? rootFile).id
}`, }`,
}, },
}) })
@ -179,7 +179,7 @@ export const FigureModalUploadFileSource: FC = () => {
}, [ }, [
uppy, uppy,
folder, folder,
rootFolder, rootFile,
name, name,
nameDirty, nameDirty,
dispatchUploadAction, dispatchUploadAction,
@ -245,7 +245,7 @@ export const FigureModalUploadFileSource: FC = () => {
name={name} name={name}
nameDisabled={!file && !nameDirty} nameDisabled={!file && !nameDirty}
onFolderChanged={item => onFolderChanged={item =>
dispatchUploadAction(name, file, item ?? rootFolder) dispatchUploadAction(name, file, item ?? rootFile)
} }
onNameChanged={name => dispatchUploadAction(name, file, folder)} onNameChanged={name => dispatchUploadAction(name, file, folder)}
setFolder={setFolder} setFolder={setFolder}

View file

@ -3,7 +3,7 @@ import { useFigureModalContext } from '../figure-modal-context'
import { postJSON } from '../../../../../infrastructure/fetch-json' import { postJSON } from '../../../../../infrastructure/fetch-json'
import { useProjectContext } from '../../../../../shared/context/project-context' import { useProjectContext } from '../../../../../shared/context/project-context'
import { File } from '../../../utils/file' import { File } from '../../../utils/file'
import { useCurrentProjectFolders } from '../../../hooks/useCurrentProjectFolders' import { useCurrentProjectFolders } from '../../../hooks/use-current-project-folders'
import { FileRelocator } from '../file-relocator' import { FileRelocator } from '../file-relocator'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { useCodeMirrorViewContext } from '../../codemirror-editor' import { useCodeMirrorViewContext } from '../../codemirror-editor'
@ -44,7 +44,7 @@ export const FigureModalUrlSource: FC = () => {
const [nameDirty, setNameDirty] = useState<boolean>(false) const [nameDirty, setNameDirty] = useState<boolean>(false)
const [name, setName] = useState<string>('') const [name, setName] = useState<string>('')
const { _id: projectId } = useProjectContext() const { _id: projectId } = useProjectContext()
const [, rootFile] = useCurrentProjectFolders() const { rootFile } = useCurrentProjectFolders()
const [folder, setFolder] = useState<File>(rootFile) const [folder, setFolder] = useState<File>(rootFile)
const { dispatch, getPath } = useFigureModalContext() const { dispatch, getPath } = useFigureModalContext()

View file

@ -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])
}

View file

@ -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<FileOrDirectory>('rootFolder')
const rootFile = { ...rootFolder, path: '' }
const folders = useMemo(() => filterFolders(rootFolder), [rootFolder])
return [folders, rootFile]
}

View file

@ -1,45 +1,12 @@
import { docId, mockDoc } from './mock-doc' import { docId, mockDoc } from './mock-doc'
import { Folder } from '../../../../../types/folder'
import { sleep } from '../../../helpers/sleep' import { sleep } from '../../../helpers/sleep'
import { Folder } from '../../../../../types/folder'
export const rootFolderId = '012345678901234567890123' export const rootFolderId = '012345678901234567890123'
export const figuresFolderId = '123456789012345678901234' export const figuresFolderId = '123456789012345678901234'
export const figureId = '234567890123456789012345' export const figureId = '234567890123456789012345'
export const mockScope = (content?: string) => { export const mockScope = (content?: string) => {
return { 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: { settings: {
fontSize: 12, fontSize: 12,
fontFamily: 'monaco', fontFamily: 'monaco',
@ -66,7 +33,37 @@ export const mockScope = (content?: string) => {
_id: 'test-project', _id: 'test-project',
name: 'Test Project', name: 'Test Project',
spellCheckLanguage: 'en', 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: { features: {
trackChanges: true, trackChanges: true,
}, },