From 96955fe2a0a4eaf66a44815860e21a8f513deafa Mon Sep 17 00:00:00 2001 From: Mathias Jakobsen Date: Thu, 10 Jul 2025 09:42:23 +0100 Subject: [PATCH] Merge pull request #26951 from overleaf/mj-layout-broken [web] Add restoreView to LayoutContext GitOrigin-RevId: 3a50c1e215c99236f503285fee1c924df57e07e4 --- .../context/hooks/use-restore-deleted-file.ts | 6 ++-- .../context/hooks/use-restore-project.ts | 6 ++-- .../hooks/use-restore-selected-file.ts | 6 ++-- .../toolbar/show-history-button.tsx | 11 +++++--- .../components/toolbar/toolbar.tsx | 6 ++-- .../components/switch-to-editor-button.tsx | 4 +-- .../js/shared/context/layout-context.tsx | 28 +++++++++++++------ .../components/full-project-search.spec.tsx | 1 + .../frontend/helpers/editor-providers.tsx | 7 +++++ 9 files changed, 48 insertions(+), 27 deletions(-) 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 2521454ebc..621bd9b148 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 @@ -21,7 +21,7 @@ type RestorationState = export function useRestoreDeletedFile() { const { projectId } = useHistoryContext() - const { setView } = useLayoutContext() + const { restoreView } = useLayoutContext() const { openDocWithId, openFileWithId } = useEditorManagerContext() const { showBoundary } = useErrorBoundary() const { fileTreeData } = useFileTreeData() @@ -37,7 +37,7 @@ export function useRestoreDeletedFile() { if (result) { setState('complete') const { _id: id } = result.entity - setView('editor') + restoreView() if (restoredFileMetadata.type === 'doc') { openDocWithId(id) @@ -52,7 +52,7 @@ export function useRestoreDeletedFile() { restoredFileMetadata, openDocWithId, openFileWithId, - setView, + restoreView, ]) useEffect(() => { diff --git a/services/web/frontend/js/features/history/context/hooks/use-restore-project.ts b/services/web/frontend/js/features/history/context/hooks/use-restore-project.ts index 71b1b6af12..7813ae0c5c 100644 --- a/services/web/frontend/js/features/history/context/hooks/use-restore-project.ts +++ b/services/web/frontend/js/features/history/context/hooks/use-restore-project.ts @@ -7,7 +7,7 @@ type RestorationState = 'initial' | 'restoring' | 'restored' | 'error' export const useRestoreProject = () => { const { showBoundary } = useErrorBoundary() - const { setView } = useLayoutContext() + const { restoreView } = useLayoutContext() const [restorationState, setRestorationState] = useState('initial') @@ -18,14 +18,14 @@ export const useRestoreProject = () => { restoreProjectToVersion(projectId, version) .then(() => { setRestorationState('restored') - setView('editor') + restoreView() }) .catch(err => { setRestorationState('error') showBoundary(err) }) }, - [showBoundary, setView] + [showBoundary, restoreView] ) return { diff --git a/services/web/frontend/js/features/history/context/hooks/use-restore-selected-file.ts b/services/web/frontend/js/features/history/context/hooks/use-restore-selected-file.ts index 168495bc92..d514129e9a 100644 --- a/services/web/frontend/js/features/history/context/hooks/use-restore-selected-file.ts +++ b/services/web/frontend/js/features/history/context/hooks/use-restore-selected-file.ts @@ -22,7 +22,7 @@ type RestoreState = export function useRestoreSelectedFile() { const { projectId } = useHistoryContext() - const { setView } = useLayoutContext() + const { restoreView } = useLayoutContext() const { openDocWithId, openFileWithId } = useEditorManagerContext() const { showBoundary } = useErrorBoundary() const { fileTreeData } = useFileTreeData() @@ -38,7 +38,7 @@ export function useRestoreSelectedFile() { if (result) { setState('complete') const { _id: id } = result.entity - setView('editor') + restoreView() if (restoredFileMetadata.type === 'doc') { openDocWithId(id) @@ -53,7 +53,7 @@ export function useRestoreSelectedFile() { restoredFileMetadata, openDocWithId, openFileWithId, - setView, + restoreView, ]) useEffect(() => { diff --git a/services/web/frontend/js/features/ide-redesign/components/toolbar/show-history-button.tsx b/services/web/frontend/js/features/ide-redesign/components/toolbar/show-history-button.tsx index 5bf25c8852..5540dc5d3b 100644 --- a/services/web/frontend/js/features/ide-redesign/components/toolbar/show-history-button.tsx +++ b/services/web/frontend/js/features/ide-redesign/components/toolbar/show-history-button.tsx @@ -7,15 +7,18 @@ import { useEditorAnalytics } from '@/shared/hooks/use-editor-analytics' export default function ShowHistoryButton() { const { t } = useTranslation() - const { view, setView } = useLayoutContext() + const { view, setView, restoreView } = useLayoutContext() const { sendEvent } = useEditorAnalytics() const toggleHistoryOpen = useCallback(() => { const action = view === 'history' ? 'close' : 'open' sendEvent('navigation-clicked-history', { action }) - - setView(view === 'history' ? 'editor' : 'history') - }, [view, setView, sendEvent]) + if (view === 'history') { + restoreView() + } else { + setView('history') + } + }, [view, setView, sendEvent, restoreView]) return (
diff --git a/services/web/frontend/js/features/ide-redesign/components/toolbar/toolbar.tsx b/services/web/frontend/js/features/ide-redesign/components/toolbar/toolbar.tsx index 6da6e2cef9..abe2012231 100644 --- a/services/web/frontend/js/features/ide-redesign/components/toolbar/toolbar.tsx +++ b/services/web/frontend/js/features/ide-redesign/components/toolbar/toolbar.tsx @@ -20,7 +20,7 @@ const [publishModalModules] = importOverleafModules('publishModal') const SubmitProjectButton = publishModalModules?.import.NewPublishToolbarButton export const Toolbar = () => { - const { view, setView } = useLayoutContext() + const { view, restoreView } = useLayoutContext() const { cobranding } = useEditorContext() const { permissionsLevel } = useIdeReactContext() const shouldDisplaySubmitButton = @@ -29,8 +29,8 @@ export const Toolbar = () => { const handleBackToEditorClick = useCallback(() => { eventTracking.sendMB('navigation-clicked-history', { action: 'close' }) - setView('editor') - }, [setView]) + restoreView() + }, [restoreView]) if (view === 'history') { return ( diff --git a/services/web/frontend/js/features/pdf-preview/components/switch-to-editor-button.tsx b/services/web/frontend/js/features/pdf-preview/components/switch-to-editor-button.tsx index 4b94825e04..b7410de7b1 100644 --- a/services/web/frontend/js/features/pdf-preview/components/switch-to-editor-button.tsx +++ b/services/web/frontend/js/features/pdf-preview/components/switch-to-editor-button.tsx @@ -4,7 +4,7 @@ import OLButton from '@/features/ui/components/ol/ol-button' import { useLayoutContext } from '../../../shared/context/layout-context' function SwitchToEditorButton() { - const { pdfLayout, setView, detachRole } = useLayoutContext() + const { pdfLayout, restoreView, detachRole } = useLayoutContext() const { t } = useTranslation() @@ -17,7 +17,7 @@ function SwitchToEditorButton() { } function handleClick() { - setView('editor') + restoreView() window.setTimeout(() => { window.dispatchEvent(new Event('editor:focus')) }) diff --git a/services/web/frontend/js/shared/context/layout-context.tsx b/services/web/frontend/js/shared/context/layout-context.tsx index 6bc8c5b222..c3647d3926 100644 --- a/services/web/frontend/js/shared/context/layout-context.tsx +++ b/services/web/frontend/js/shared/context/layout-context.tsx @@ -8,6 +8,7 @@ import { SetStateAction, FC, useState, + useRef, } from 'react' import useDetachLayout from '../hooks/use-detach-layout' import localStorage from '../../infrastructure/local-storage' @@ -61,6 +62,7 @@ export type LayoutContextValue = LayoutContextOwnStates & { pdfPreviewOpen: boolean setProjectSearchIsOpen: Dispatch> setOpenFile: Dispatch> + restoreView: () => void } const debugPdfDetach = getMeta('ol-debugPdfDetach') @@ -85,6 +87,8 @@ export const LayoutProvider: FC = ({ children }) => { const historyToggleEmitter = useScopeEventEmitter('history:toggle', true) const { isOpen: railIsOpen, setIsOpen: setRailIsOpen } = useRailContext() const [prevRailIsOpen, setPrevRailIsOpen] = useState(railIsOpen) + // Whether we came from a file or a document when we left the ide + const lastIdeView = useRef('editor') const setView = useCallback( (value: IdeView | null) => { @@ -103,12 +107,8 @@ export const LayoutProvider: FC = ({ children }) => { setRailIsOpen(prevRailIsOpen) } - if (value === 'editor' && openFile) { - // if a file is currently opened, ensure the view is 'file' instead of - // 'editor' when the 'editor' view is requested. This is to ensure - // that the entity selected in the file tree is the one visible and - // that docs don't take precedence over files. - return 'file' + if (value === 'editor' || value === 'file') { + lastIdeView.current = value } return value @@ -117,7 +117,6 @@ export const LayoutProvider: FC = ({ children }) => { [ _setView, setRailIsOpen, - openFile, historyToggleEmitter, prevRailIsOpen, setPrevRailIsOpen, @@ -125,6 +124,10 @@ export const LayoutProvider: FC = ({ children }) => { ] ) + const restoreView = useCallback(() => { + setView(lastIdeView.current ?? 'editor') + }, [setView]) + // whether the chat pane is open const [chatIsOpen, setChatIsOpen] = usePersistedState( 'ui.chatOpen', @@ -192,11 +195,16 @@ export const LayoutProvider: FC = ({ children }) => { const changeLayout = useCallback( (newLayout: IdeLayout, newView: IdeView = 'editor') => { + const targetView = newLayout === 'sideBySide' ? 'editor' : newView setPdfLayout(newLayout) - setView(newLayout === 'sideBySide' ? 'editor' : newView) + if (targetView === 'editor') { + restoreView() + } else { + setView(targetView) + } setLayoutInLocalStorage(newLayout) }, - [setPdfLayout, setView] + [setPdfLayout, setView, restoreView] ) // Force codemirror to reposition all tooltips to prevent an issue @@ -276,6 +284,7 @@ export const LayoutProvider: FC = ({ children }) => { setLoadingStyleSheet, setView, view, + restoreView, }), [ reattach, @@ -302,6 +311,7 @@ export const LayoutProvider: FC = ({ children }) => { setLoadingStyleSheet, setView, view, + restoreView, ] ) diff --git a/services/web/test/frontend/features/full-project-search/components/full-project-search.spec.tsx b/services/web/test/frontend/features/full-project-search/components/full-project-search.spec.tsx index e87467ee30..6e04a9cf3f 100644 --- a/services/web/test/frontend/features/full-project-search/components/full-project-search.spec.tsx +++ b/services/web/test/frontend/features/full-project-search/components/full-project-search.spec.tsx @@ -95,6 +95,7 @@ const createInitialValue = () => setProjectSearchIsOpen: cy.stub(), openFile: null, setOpenFile: cy.stub(), + restoreView: cy.stub(), }) satisfies LayoutContextValue const LayoutProvider: FC = ({ children }) => { diff --git a/services/web/test/frontend/helpers/editor-providers.tsx b/services/web/test/frontend/helpers/editor-providers.tsx index accf5ad73d..16df207b88 100644 --- a/services/web/test/frontend/helpers/editor-providers.tsx +++ b/services/web/test/frontend/helpers/editor-providers.tsx @@ -409,6 +409,11 @@ const makeLayoutProvider = ( }, [setPdfLayout, setView] ) + + const restoreView = useCallback(() => { + setView('editor') + }, []) + const { reattach, detach, @@ -443,6 +448,7 @@ const makeLayoutProvider = ( setLoadingStyleSheet, setView, view, + restoreView, }), [ reattach, @@ -469,6 +475,7 @@ const makeLayoutProvider = ( setLoadingStyleSheet, setView, view, + restoreView, ] )