diff --git a/services/web/frontend/js/features/history/context/hooks/use-restore-deleted-file.ts b/services/web/frontend/js/features/history/context/hooks/use-restore-deleted-file.ts index c9b07d9d73..c4bfb672c5 100644 --- a/services/web/frontend/js/features/history/context/hooks/use-restore-deleted-file.ts +++ b/services/web/frontend/js/features/history/context/hooks/use-restore-deleted-file.ts @@ -38,13 +38,14 @@ export function useRestoreDeletedFile() { setState('complete') const { _id: id } = result.entity setView('editor') + + // Once Angular is gone, these can be replaced with calls to context + // methods if (restoredFileMetadata.type === 'doc') { ide.editorManager.openDocId(id) } else { ide.binaryFilesManager.openFileWithId(id) } - // Get the file tree to select the entity that has just been restored - window.dispatchEvent(new CustomEvent('editor.openDoc', { detail: id })) } } }, [ diff --git a/services/web/frontend/js/features/ide-react/components/editor-and-sidebar.tsx b/services/web/frontend/js/features/ide-react/components/editor-and-sidebar.tsx index e9e6c96d5a..9cb40fb966 100644 --- a/services/web/frontend/js/features/ide-react/components/editor-and-sidebar.tsx +++ b/services/web/frontend/js/features/ide-react/components/editor-and-sidebar.tsx @@ -11,7 +11,6 @@ import { FileTreeFileRefFindResult, FileTreeFindResult, } from '@/features/ide-react/types/file-tree' -import usePersistedState from '@/shared/hooks/use-persisted-state' import FileView from '@/features/file-view/components/file-view' import { FileRef } from '../../../../../types/file-ref' import { EditorPane } from '@/features/ide-react/components/editor/editor-pane' @@ -23,6 +22,8 @@ import { useEditorManagerContext } from '@/features/ide-react/context/editor-man import NoOpenDocPane from '@/features/ide-react/components/editor/no-open-doc-pane' import { debugConsole } from '@/utils/debugging' import { HistorySidebar } from '@/features/ide-react/components/history-sidebar' +import { BinaryFile } from '@/features/file-view/types/binary-file' +import useScopeValue from '@/shared/hooks/use-scope-value' type EditorAndSidebarProps = { shouldPersistLayout: boolean @@ -30,6 +31,18 @@ type EditorAndSidebarProps = { setLeftColumnDefaultSize: React.Dispatch> } +function convertFileRefToBinaryFile(fileRef: FileRef): BinaryFile { + return { + _id: fileRef._id, + name: fileRef.name, + id: fileRef._id, + type: 'file', + selected: true, + linkedFileData: fileRef.linkedFileData, + created: fileRef.created ? new Date(fileRef.created) : new Date(), + } +} + // `FileViewHeader`, which is TypeScript, expects a BinaryFile, which has a // `created` property of type `Date`, while `TPRFileViewInfo`, written in JS, // into which `FileViewHeader` passes its BinaryFile, expects a file object with @@ -39,12 +52,7 @@ type EditorAndSidebarProps = { // does too. function fileViewFile(fileRef: FileRef) { return { - _id: fileRef._id, - name: fileRef.name, - id: fileRef._id, - type: 'file', - selected: true, - linkedFileData: fileRef.linkedFileData, + ...convertFileRefToBinaryFile(fileRef), created: fileRef.created, } } @@ -55,18 +63,18 @@ export function EditorAndSidebar({ setLeftColumnDefaultSize, }: EditorAndSidebarProps) { const [leftColumnIsOpen, setLeftColumnIsOpen] = useState(true) - const { rootDocId, _id: projectId } = useProjectContext() + const { rootDocId } = useProjectContext() const { eventEmitter } = useIdeReactContext() - const { openDocId: openDocWithId, currentDocumentId } = - useEditorManagerContext() + const { + openDocId: openDocWithId, + currentDocumentId: openDocId, + openInitialDoc, + } = useEditorManagerContext() const { view } = useLayoutContext() + const [, setOpenFile] = useScopeValue('openFile') + const historyIsOpen = view === 'history' - // Persist the open document ID in local storage - const [openDocId, setOpenDocId] = usePersistedState( - `doc.open_id.${projectId}`, - rootDocId - ) const [openEntity, setOpenEntity] = useState< FileTreeDocumentFindResult | FileTreeFileRefFindResult | null >(null) @@ -92,11 +100,21 @@ export function EditorAndSidebar({ } setOpenEntity(selected) - if (selected.type === 'doc') { - setOpenDocId(selected.entity._id) + if (selected.type === 'doc' && fileTreeReady) { + openDocWithId(selected.entity._id) + } + + // Keep openFile scope value in sync with the file tree + const openFile = + selected.type === 'fileRef' + ? convertFileRefToBinaryFile(selected.entity) + : null + setOpenFile(openFile) + if (openFile) { + window.dispatchEvent(new CustomEvent('file-view:file-opened')) } }, - [setOpenDocId] + [fileTreeReady, setOpenFile, openDocWithId] ) const handleFileTreeDelete: FileTreeDeleteHandler = useCallback( @@ -115,46 +133,38 @@ export function EditorAndSidebar({ // trigger the onSelect handler in this component, which will update the local // state. useEffect(() => { - debugConsole.log( - `currentDocumentId changed to ${currentDocumentId}. Updating file tree` - ) - if (currentDocumentId === null) { + debugConsole.log(`openDocId changed to ${openDocId}`) + if (openDocId === null) { return } window.dispatchEvent( - new CustomEvent('editor.openDoc', { detail: currentDocumentId }) + new CustomEvent('editor.openDoc', { detail: openDocId }) ) - }, [currentDocumentId]) - - // Store openDocWithId, which is unstable, in a ref and keep the ref - // synchronized with the source - const openDocWithIdRef = useRef(openDocWithId) + }, [openDocId]) + // Open a document once the file tree is ready + const initialOpenDoneRef = useRef(false) useEffect(() => { - openDocWithIdRef.current = openDocWithId - }, [openDocWithId]) - - // Open a document in the editor when the local document ID changes - useEffect(() => { - if (!fileTreeReady || !openDocId) { - return + if (fileTreeReady && !initialOpenDoneRef.current) { + initialOpenDoneRef.current = true + openInitialDoc(rootDocId) } - debugConsole.log( - `Observed change in local state. Opening document with ID ${openDocId}` - ) - openDocWithIdRef.current(openDocId) - }, [fileTreeReady, openDocId]) + }, [fileTreeReady, openInitialDoc, rootDocId]) - const leftColumnContent = historyIsOpen ? ( - - ) : ( - + // Keep the editor file tree around so that it is available and up to date + // when restoring a file + const leftColumnContent = ( + <> + + {historyIsOpen ? : null} + ) let rightColumnContent diff --git a/services/web/frontend/js/features/ide-react/components/editor-sidebar.tsx b/services/web/frontend/js/features/ide-react/components/editor-sidebar.tsx index d23378e9c4..56051c972b 100644 --- a/services/web/frontend/js/features/ide-react/components/editor-sidebar.tsx +++ b/services/web/frontend/js/features/ide-react/components/editor-sidebar.tsx @@ -6,8 +6,10 @@ import { FileTreeDeleteHandler, FileTreeSelectHandler, } from '@/features/ide-react/types/file-tree' +import classNames from 'classnames' type EditorSidebarProps = { + shouldShow: boolean shouldPersistLayout: boolean onFileTreeInit: () => void onFileTreeSelect: FileTreeSelectHandler @@ -15,6 +17,7 @@ type EditorSidebarProps = { } export default function EditorSidebar({ + shouldShow, shouldPersistLayout, onFileTreeInit, onFileTreeSelect, @@ -22,7 +25,11 @@ export default function EditorSidebar({ }: EditorSidebarProps) { return ( <> -