From 9854d37916a512fcc777393400b901866d074e8d Mon Sep 17 00:00:00 2001 From: Alf Eaton Date: Thu, 6 Feb 2025 11:22:33 +0000 Subject: [PATCH] Minimise use of ide context provider (#23308) GitOrigin-RevId: 468b8d4b33925bdb97bd46d2c4e7acb969f3d82b --- ...-project-wide-settings-socket-listener.tsx | 18 +++---- .../contexts/file-tree-selectable.tsx | 27 ++++++----- .../context/hooks/use-restore-deleted-file.ts | 14 +++--- .../hooks/use-restore-selected-file.ts | 14 +++--- .../ide-react/components/layout/ide-page.tsx | 2 - .../context/editor-manager-context.tsx | 48 +++++++++++++++---- .../context/file-tree-open-context.tsx | 21 +------- .../features/ide-react/hooks/use-open-file.ts | 24 ---------- .../hooks/use-select-file-tree-entity.ts | 25 ---------- .../ide-react/util/find-doc-entity-by-id.ts | 9 ++++ .../components/pdf-synctex-controls.jsx | 17 ++++--- .../components/word-count-modal-content.jsx | 6 +-- .../js/shared/context/editor-context.tsx | 11 ++--- .../js/shared/context/ide-context.tsx | 3 +- .../shared/context/local-compile-context.tsx | 4 -- .../js/shared/context/mock/mock-ide.js | 4 -- .../web/frontend/stories/decorators/scope.tsx | 6 --- .../components/file-tree-root.spec.tsx | 44 ----------------- services/web/types/window.ts | 6 +-- 19 files changed, 106 insertions(+), 197 deletions(-) delete mode 100644 services/web/frontend/js/features/ide-react/hooks/use-open-file.ts delete mode 100644 services/web/frontend/js/features/ide-react/hooks/use-select-file-tree-entity.ts diff --git a/services/web/frontend/js/features/editor-left-menu/hooks/use-project-wide-settings-socket-listener.tsx b/services/web/frontend/js/features/editor-left-menu/hooks/use-project-wide-settings-socket-listener.tsx index 5b517c53b3..fde26a19d9 100644 --- a/services/web/frontend/js/features/editor-left-menu/hooks/use-project-wide-settings-socket-listener.tsx +++ b/services/web/frontend/js/features/editor-left-menu/hooks/use-project-wide-settings-socket-listener.tsx @@ -4,7 +4,7 @@ import useScopeValue from '../../../shared/hooks/use-scope-value' import type { ProjectSettings } from '../utils/api' export default function useProjectWideSettingsSocketListener() { - const ide = useIdeContext() + const { socket } = useIdeContext() const [project, setProject] = useScopeValue( 'project' @@ -41,18 +41,18 @@ export default function useProjectWideSettingsSocketListener() { // data is not available on initial mounting const dataAvailable = !!project - if (dataAvailable && ide?.socket) { - ide.socket.on('compilerUpdated', setCompiler) - ide.socket.on('imageNameUpdated', setImageName) - ide.socket.on('spellCheckLanguageUpdated', setSpellCheckLanguage) + if (dataAvailable && socket) { + socket.on('compilerUpdated', setCompiler) + socket.on('imageNameUpdated', setImageName) + socket.on('spellCheckLanguageUpdated', setSpellCheckLanguage) return () => { - ide.socket.removeListener('compilerUpdated', setCompiler) - ide.socket.removeListener('imageNameUpdated', setImageName) - ide.socket.removeListener( + socket.removeListener('compilerUpdated', setCompiler) + socket.removeListener('imageNameUpdated', setImageName) + socket.removeListener( 'spellCheckLanguageUpdated', setSpellCheckLanguage ) } } - }, [ide?.socket, project, setCompiler, setImageName, setSpellCheckLanguage]) + }, [socket, project, setCompiler, setImageName, setSpellCheckLanguage]) } diff --git a/services/web/frontend/js/features/file-tree/contexts/file-tree-selectable.tsx b/services/web/frontend/js/features/file-tree/contexts/file-tree-selectable.tsx index e7ee96803c..f52fe385e5 100644 --- a/services/web/frontend/js/features/file-tree/contexts/file-tree-selectable.tsx +++ b/services/web/frontend/js/features/file-tree/contexts/file-tree-selectable.tsx @@ -22,6 +22,7 @@ import { fileCollator } from '@/features/file-tree/util/file-collator' import { Folder } from '../../../../../types/folder' import { FileTreeEntity } from '../../../../../types/file-tree-entity' import { isMac } from '@/shared/utils/os' +import useEventListener from '@/shared/hooks/use-event-listener' const FileTreeSelectableContext = createContext< | { @@ -188,18 +189,22 @@ export const FileTreeSelectableProvider: FC<{ setSelectedEntities, ]) - useEffect(() => { - // listen for `editor.openDoc` and selected that doc - function handleOpenDoc(ev: any) { - const found = findInTree(fileTreeData, ev.detail) - if (!found) return + // Synchronize the file tree when openFileWithId or openDocWithId is called on the editor + // manager context from elsewhere. If the file tree does change, it will + // trigger the onSelect handler in this component, which will update the local + // state. + useEventListener( + 'entity:opened', + useCallback( + (event: CustomEvent) => { + const found = findInTree(fileTreeData, event.detail) + if (!found) return - dispatch({ type: ACTION_TYPES.SELECT, id: found.entity._id }) - } - - window.addEventListener('editor.openDoc', handleOpenDoc) - return () => window.removeEventListener('editor.openDoc', handleOpenDoc) - }, [fileTreeData]) + dispatch({ type: ACTION_TYPES.SELECT, id: found.entity._id }) + }, + [fileTreeData] + ) + ) const select = useCallback(id => { dispatch({ type: ACTION_TYPES.SELECT, id }) 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 5dcb5f2fef..226991e3b1 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 @@ -1,5 +1,4 @@ import { sendMB } from '../../../../infrastructure/event-tracking' -import { useIdeContext } from '../../../../shared/context/ide-context' import { useLayoutContext } from '../../../../shared/context/layout-context' import { restoreFile } from '../../services/api' import { isFileRemoved } from '../../utils/file-diff' @@ -10,6 +9,7 @@ import { useFileTreeData } from '@/shared/context/file-tree-data-context' import { findInTree } from '@/features/file-tree/util/find-in-tree' import { useCallback, useEffect, useState } from 'react' import { RestoreFileResponse } from '@/features/history/services/types/restore-file' +import { useEditorManagerContext } from '@/features/ide-react/context/editor-manager-context' type RestorationState = | 'idle' @@ -21,8 +21,8 @@ type RestorationState = export function useRestoreDeletedFile() { const { projectId } = useHistoryContext() - const ide = useIdeContext() const { setView } = useLayoutContext() + const { openDocWithId, openFileWithId } = useEditorManagerContext() const handleError = useErrorHandler() const { fileTreeData } = useFileTreeData() const [state, setState] = useState('idle') @@ -39,12 +39,10 @@ export function useRestoreDeletedFile() { 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.openDocWithId(id) + openDocWithId(id) } else { - ide.binaryFilesManager.openFileWithId(id) + openFileWithId(id) } } } @@ -52,8 +50,8 @@ export function useRestoreDeletedFile() { state, fileTreeData, restoredFileMetadata, - ide.editorManager, - ide.binaryFilesManager, + openDocWithId, + openFileWithId, setView, ]) 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 f33847d0f0..12b72faafd 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 @@ -1,4 +1,3 @@ -import { useIdeContext } from '../../../../shared/context/ide-context' import { useLayoutContext } from '../../../../shared/context/layout-context' import { restoreFileToVersion } from '../../services/api' import { isFileRemoved } from '../../utils/file-diff' @@ -9,6 +8,7 @@ import { useFileTreeData } from '@/shared/context/file-tree-data-context' import { findInTree } from '@/features/file-tree/util/find-in-tree' import { useCallback, useEffect, useState } from 'react' import { RestoreFileResponse } from '../../services/types/restore-file' +import { useEditorManagerContext } from '@/features/ide-react/context/editor-manager-context' const RESTORE_FILE_TIMEOUT = 3000 @@ -22,8 +22,8 @@ type RestoreState = export function useRestoreSelectedFile() { const { projectId } = useHistoryContext() - const ide = useIdeContext() const { setView } = useLayoutContext() + const { openDocWithId, openFileWithId } = useEditorManagerContext() const handleError = useErrorHandler() const { fileTreeData } = useFileTreeData() const [state, setState] = useState('idle') @@ -40,12 +40,10 @@ export function useRestoreSelectedFile() { 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.openDocWithId(id) + openDocWithId(id) } else { - ide.binaryFilesManager.openFileWithId(id) + openFileWithId(id) } } } @@ -53,8 +51,8 @@ export function useRestoreSelectedFile() { state, fileTreeData, restoredFileMetadata, - ide.editorManager, - ide.binaryFilesManager, + openDocWithId, + openFileWithId, setView, ]) diff --git a/services/web/frontend/js/features/ide-react/components/layout/ide-page.tsx b/services/web/frontend/js/features/ide-react/components/layout/ide-page.tsx index 84f8ba87b2..22f9fc0da4 100644 --- a/services/web/frontend/js/features/ide-react/components/layout/ide-page.tsx +++ b/services/web/frontend/js/features/ide-react/components/layout/ide-page.tsx @@ -4,7 +4,6 @@ import { MainLayout } from '@/features/ide-react/components/layout/main-layout' import EditorLeftMenu from '@/features/editor-left-menu/components/editor-left-menu' import { useLayoutEventTracking } from '@/features/ide-react/hooks/use-layout-event-tracking' import useSocketListeners from '@/features/ide-react/hooks/use-socket-listeners' -import { useOpenFile } from '@/features/ide-react/hooks/use-open-file' import { useEditingSessionHeartbeat } from '@/features/ide-react/hooks/use-editing-session-heartbeat' import { useRegisterUserActivity } from '@/features/ide-react/hooks/use-register-user-activity' import { useHasLintingError } from '@/features/ide-react/hooks/use-has-linting-error' @@ -26,7 +25,6 @@ export default function IdePage() { useEditingSessionHeartbeat() // send a batched event when user is active useRegisterUserActivity() // record activity and ensure connection when user is active useHasLintingError() // pass editor:lint hasLintingError to the compiler - useOpenFile() // create ide.binaryFilesManager (TODO: move to the history file restore component) const newEditor = useFeatureFlag('editor-redesign') diff --git a/services/web/frontend/js/features/ide-react/context/editor-manager-context.tsx b/services/web/frontend/js/features/ide-react/context/editor-manager-context.tsx index 5546105621..1db4130081 100644 --- a/services/web/frontend/js/features/ide-react/context/editor-manager-context.tsx +++ b/services/web/frontend/js/features/ide-react/context/editor-manager-context.tsx @@ -21,7 +21,10 @@ import { useLayoutContext } from '@/shared/context/layout-context' import { GotoLineOptions } from '@/features/ide-react/types/goto-line-options' import { Doc } from '../../../../../types/doc' import { useFileTreeData } from '@/shared/context/file-tree-data-context' -import { findDocEntityById } from '@/features/ide-react/util/find-doc-entity-by-id' +import { + findDocEntityById, + findFileRefEntityById, +} from '@/features/ide-react/util/find-doc-entity-by-id' import useScopeEventEmitter from '@/shared/hooks/use-scope-event-emitter' import { useModalsContext } from '@/features/ide-react/context/modals-context' import { useTranslation } from 'react-i18next' @@ -32,6 +35,9 @@ import { DocId } from '../../../../../types/project-settings' import { Update } from '@/features/history/services/types/update' import { useDebugDiffTracker } from '../hooks/use-debug-diff-tracker' import { useEditorContext } from '@/shared/context/editor-context' +import useScopeValueSetterOnly from '@/shared/hooks/use-scope-value-setter-only' +import { BinaryFile } from '@/features/file-view/types/binary-file' +import { convertFileRefToBinaryFile } from '@/features/ide-react/util/file-view' export interface GotoOffsetOptions { gotoOffset: number @@ -57,6 +63,7 @@ export type EditorManager = { openDocWithId: (docId: string, options?: OpenDocOptions) => void openDoc: (document: Doc, options?: OpenDocOptions) => void openDocs: OpenDocuments + openFileWithId: (fileId: string) => void openInitialDoc: (docId: string) => void jumpToLine: (options: GotoLineOptions) => void wantTrackChanges: boolean @@ -94,7 +101,7 @@ export const EditorManagerContext = createContext( export const EditorManagerProvider: FC = ({ children }) => { const { t } = useTranslation() - const ide = useIdeContext() + const { scopeStore } = useIdeContext() const { projectId } = useIdeReactContext() const { reportError, eventEmitter } = useIdeReactContext() const { setOutOfSync } = useEditorContext() @@ -203,14 +210,14 @@ export const EditorManagerProvider: FC = ({ children }) => { // implementation in EditorManager interacts with Angular scope to update // the layout. Once Angular is gone, this can become a context method. useEffect(() => { - ide.scopeStore.set('editor.toggleSymbolPalette', () => { + scopeStore.set('editor.toggleSymbolPalette', () => { setShowSymbolPalette(show => { const newValue = !show sendMB(newValue ? 'symbol-palette-show' : 'symbol-palette-hide') return newValue }) }) - }, [ide.scopeStore, setShowSymbolPalette]) + }, [scopeStore, setShowSymbolPalette]) const getEditorType = useCallback((): EditorType | null => { if (!currentDocument) { @@ -443,6 +450,11 @@ export const EditorManagerProvider: FC = ({ children }) => { detail: { isNewDoc, docId: doc._id }, }) ) + window.dispatchEvent( + new CustomEvent('entity:opened', { + detail: doc._id, + }) + ) if (hasGotoLine(options)) { window.setTimeout(() => jumpToLine(options)) @@ -527,6 +539,24 @@ export const EditorManagerProvider: FC = ({ children }) => { [fileTreeData, openDoc] ) + const [, setOpenFile] = useScopeValueSetterOnly('openFile') + + const openFileWithId = useCallback( + (fileRefId: string) => { + const fileRef = findFileRefEntityById(fileTreeData, fileRefId) + if (!fileRef) { + return + } + setOpenFile(convertFileRefToBinaryFile(fileRef)) + window.dispatchEvent( + new CustomEvent('entity:opened', { + detail: fileRef._id, + }) + ) + }, + [fileTreeData, setOpenFile] + ) + const openInitialDoc = useCallback( (fallbackDocId: string) => { const docId = @@ -655,7 +685,7 @@ export const EditorManagerProvider: FC = ({ children }) => { } }, [currentDocument, syncTrackChangesState, wantTrackChanges]) - const editorManager = useMemo( + const value = useMemo( () => ({ getEditorType, showSymbolPalette, @@ -668,6 +698,7 @@ export const EditorManagerProvider: FC = ({ children }) => { openDocWithId, openDoc, openDocs, + openFileWithId, openInitialDoc, jumpToLine, wantTrackChanges, @@ -686,6 +717,7 @@ export const EditorManagerProvider: FC = ({ children }) => { openDocWithId, openDoc, openDocs, + openFileWithId, openInitialDoc, jumpToLine, wantTrackChanges, @@ -694,12 +726,8 @@ export const EditorManagerProvider: FC = ({ children }) => { ] ) - // Expose editorManager via ide object because some React code relies on it, - // for now - ide.editorManager = editorManager - return ( - + {children} ) diff --git a/services/web/frontend/js/features/ide-react/context/file-tree-open-context.tsx b/services/web/frontend/js/features/ide-react/context/file-tree-open-context.tsx index 82f5dfc049..b72a061b67 100644 --- a/services/web/frontend/js/features/ide-react/context/file-tree-open-context.tsx +++ b/services/web/frontend/js/features/ide-react/context/file-tree-open-context.tsx @@ -11,8 +11,7 @@ import { import { useProjectContext } from '@/shared/context/project-context' import { useIdeReactContext } from '@/features/ide-react/context/ide-react-context' import { useEditorManagerContext } from '@/features/ide-react/context/editor-manager-context' -import { useSelectFileTreeEntity } from '@/features/ide-react/hooks/use-select-file-tree-entity' -import useScopeValue from '@/shared/hooks/use-scope-value' +import useScopeValueSetterOnly from '@/shared/hooks/use-scope-value-setter-only' import { BinaryFile } from '@/features/file-view/types/binary-file' import { FileTreeDocumentFindResult, @@ -23,7 +22,6 @@ import { debugConsole } from '@/utils/debugging' import { convertFileRefToBinaryFile } from '@/features/ide-react/util/file-view' import { sendMB } from '@/infrastructure/event-tracking' import { FileRef } from '../../../../../types/file-ref' -import useEventListener from '@/shared/hooks/use-event-listener' const FileTreeOpenContext = createContext< | { @@ -41,8 +39,7 @@ export const FileTreeOpenProvider: FC = ({ children }) => { const { eventEmitter, projectJoined } = useIdeReactContext() const { openDocWithId, currentDocumentId, openInitialDoc } = useEditorManagerContext() - const { selectEntity } = useSelectFileTreeEntity() - const [, setOpenFile] = useScopeValue('openFile') + const [, setOpenFile] = useScopeValueSetterOnly('openFile') const [openEntity, setOpenEntity] = useState< FileTreeDocumentFindResult | FileTreeFileRefFindResult | null >(null) @@ -111,20 +108,6 @@ export const FileTreeOpenProvider: FC = ({ children }) => { [eventEmitter, currentDocumentId, openDocWithId, rootDocId] ) - // Synchronize the file tree when openDoc or openDocWithId is called on the editor - // manager context from elsewhere. If the file tree does change, it will - // trigger the onSelect handler in this component, which will update the local - // state. - useEventListener( - 'doc:after-opened', - useCallback( - (event: CustomEvent<{ docId: string }>) => { - selectEntity(event.detail.docId) - }, - [selectEntity] - ) - ) - // Open a document once the file tree and project are ready const initialOpenDoneRef = useRef(false) useEffect(() => { diff --git a/services/web/frontend/js/features/ide-react/hooks/use-open-file.ts b/services/web/frontend/js/features/ide-react/hooks/use-open-file.ts deleted file mode 100644 index 1329982c3d..0000000000 --- a/services/web/frontend/js/features/ide-react/hooks/use-open-file.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { useIdeContext } from '@/shared/context/ide-context' -import { useFileTreeData } from '@/shared/context/file-tree-data-context' -import { useCallback } from 'react' -import { findInTree } from '@/features/file-tree/util/find-in-tree' - -export function useOpenFile() { - const ide = useIdeContext() - - const { fileTreeData } = useFileTreeData() - - const openFileWithId = useCallback( - (id: string) => { - const result = findInTree(fileTreeData, id) - if (result?.type === 'fileRef') { - window.dispatchEvent(new CustomEvent('editor.openDoc', { detail: id })) - } - }, - [fileTreeData] - ) - - // Expose BinaryFilesManager via ide object solely for the benefit of the file - // restore feature in history. This can be removed once Angular is gone. - ide.binaryFilesManager = { openFileWithId } -} diff --git a/services/web/frontend/js/features/ide-react/hooks/use-select-file-tree-entity.ts b/services/web/frontend/js/features/ide-react/hooks/use-select-file-tree-entity.ts deleted file mode 100644 index babc788618..0000000000 --- a/services/web/frontend/js/features/ide-react/hooks/use-select-file-tree-entity.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { useFileTreeData } from '@/shared/context/file-tree-data-context' -import { useCallback } from 'react' -import { findInTree } from '@/features/file-tree/util/find-in-tree' - -export function useSelectFileTreeEntity() { - const { fileTreeData, selectedEntities } = useFileTreeData() - - const selectEntity = useCallback( - id => { - if ( - selectedEntities.length === 1 && - selectedEntities[0].entity._id === id - ) { - return - } - const entityToSelect = findInTree(fileTreeData, id) - if (entityToSelect) { - window.dispatchEvent(new CustomEvent('editor.openDoc', { detail: id })) - } - }, - [fileTreeData, selectedEntities] - ) - - return { selectEntity } -} diff --git a/services/web/frontend/js/features/ide-react/util/find-doc-entity-by-id.ts b/services/web/frontend/js/features/ide-react/util/find-doc-entity-by-id.ts index 202d29a60f..1527151659 100644 --- a/services/web/frontend/js/features/ide-react/util/find-doc-entity-by-id.ts +++ b/services/web/frontend/js/features/ide-react/util/find-doc-entity-by-id.ts @@ -1,6 +1,7 @@ import { findInTree } from '@/features/file-tree/util/find-in-tree' import { Folder } from '../../../../../types/folder' import { Doc } from '../../../../../types/doc' +import { FileRef } from '../../../../../types/file-ref' export function findDocEntityById(fileTreeData: Folder, docId: string) { const item = findInTree(fileTreeData, docId) @@ -9,3 +10,11 @@ export function findDocEntityById(fileTreeData: Folder, docId: string) { } return item.entity as Doc } + +export function findFileRefEntityById(fileTreeData: Folder, docId: string) { + const item = findInTree(fileTreeData, docId) + if (!item || item.type !== 'fileRef') { + return null + } + return item.entity as FileRef +} diff --git a/services/web/frontend/js/features/pdf-preview/components/pdf-synctex-controls.jsx b/services/web/frontend/js/features/pdf-preview/components/pdf-synctex-controls.jsx index 0b5dd1332d..bc51f307ba 100644 --- a/services/web/frontend/js/features/pdf-preview/components/pdf-synctex-controls.jsx +++ b/services/web/frontend/js/features/pdf-preview/components/pdf-synctex-controls.jsx @@ -1,7 +1,6 @@ import classNames from 'classnames' import { memo, useCallback, useEffect, useState, useRef } from 'react' import PropTypes from 'prop-types' -import { useIdeContext } from '../../../shared/context/ide-context' import { useProjectContext } from '../../../shared/context/project-context' import { getJSON } from '../../../infrastructure/fetch-json' import { useDetachCompileContext as useCompileContext } from '../../../shared/context/detach-compile-context' @@ -25,6 +24,7 @@ import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/boots import MaterialIcon from '@/shared/components/material-icon' import { Spinner } from 'react-bootstrap-5' import { bsVersion } from '@/features/utils/bootstrap-5' +import { useEditorManagerContext } from '@/features/ide-react/context/editor-manager-context' function GoToCodeButton({ position, @@ -170,8 +170,6 @@ function GoToPdfButton({ } function PdfSynctexControls() { - const ide = useIdeContext() - const { _id: projectId, rootDocId } = useProjectContext() const { detachRole } = useLayoutContext() @@ -187,10 +185,11 @@ function PdfSynctexControls() { const { selectedEntities } = useFileTreeData() const { findEntityByPath, dirname, pathInFolder } = useFileTreePathContext() + const { getCurrentDocumentId, openDocWithId } = useEditorManagerContext() const [cursorPosition, setCursorPosition] = useState(() => { const position = localStorage.getItem( - `doc.position.${ide.editorManager.getCurrentDocumentId()}` + `doc.position.${getCurrentDocumentId()}` ) return position ? position.cursorPosition : null }) @@ -203,7 +202,7 @@ function PdfSynctexControls() { const listener = event => setCursorPosition(event.detail) window.addEventListener('cursor:editor:update', listener) return () => window.removeEventListener('cursor:editor:update', listener) - }, [ide]) + }, []) const [syncToPdfInFlight, setSyncToPdfInFlight] = useState(false) const [syncToCodeInFlight, setSyncToCodeInFlight] = useDetachState( @@ -216,7 +215,7 @@ function PdfSynctexControls() { const [, setSynctexError] = useScopeValue('sync_tex_error') const getCurrentFilePath = useCallback(() => { - const docId = ide.editorManager.getCurrentDocumentId() + const docId = getCurrentDocumentId() let path = pathInFolder(docId) // If the root file is folder/main.tex, then synctex sees the path as folder/./main.tex @@ -227,7 +226,7 @@ function PdfSynctexControls() { } return path - }, [dirname, ide.editorManager, pathInFolder, rootDocId]) + }, [dirname, getCurrentDocumentId, pathInFolder, rootDocId]) const goToCodeLine = useCallback( (file, line) => { @@ -238,7 +237,7 @@ function PdfSynctexControls() { return } - ide.editorManager.openDocWithId(doc._id, { + openDocWithId(doc._id, { gotoLine: line, }) } else { @@ -251,7 +250,7 @@ function PdfSynctexControls() { }, 4000) } }, - [findEntityByPath, ide.editorManager, isMounted, setSynctexError] + [findEntityByPath, openDocWithId, isMounted, setSynctexError] ) const goToPdfLocation = useCallback( diff --git a/services/web/frontend/js/features/word-count-modal/components/word-count-modal-content.jsx b/services/web/frontend/js/features/word-count-modal/components/word-count-modal-content.jsx index 47d2659676..8050b384ac 100644 --- a/services/web/frontend/js/features/word-count-modal/components/word-count-modal-content.jsx +++ b/services/web/frontend/js/features/word-count-modal/components/word-count-modal-content.jsx @@ -1,7 +1,7 @@ import PropTypes from 'prop-types' import { useTranslation } from 'react-i18next' -import { useIdeContext } from '../../../shared/context/ide-context' -import { useProjectContext } from '../../../shared/context/project-context' +import { useProjectContext } from '@/shared/context/project-context' +import { useLocalCompileContext } from '@/shared/context/local-compile-context' import { useWordCount } from '../hooks/use-word-count' import { OLModalBody, @@ -20,7 +20,7 @@ import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/boots // NOTE: this component is only mounted when the modal is open export default function WordCountModalContent({ handleHide }) { const { _id: projectId } = useProjectContext() - const { clsiServerId } = useIdeContext() + const { clsiServerId } = useLocalCompileContext() const { t } = useTranslation() const { data, error, loading } = useWordCount(projectId, clsiServerId) diff --git a/services/web/frontend/js/shared/context/editor-context.tsx b/services/web/frontend/js/shared/context/editor-context.tsx index beeb155358..f0d0739595 100644 --- a/services/web/frontend/js/shared/context/editor-context.tsx +++ b/services/web/frontend/js/shared/context/editor-context.tsx @@ -60,7 +60,7 @@ export const EditorContext = createContext< >(undefined) export const EditorProvider: FC = ({ children }) => { - const ide = useIdeContext() + const { socket } = useIdeContext() const { id: userId, featureUsage } = useUserContext() const { role } = useDetachContext() const { showGenericMessageModal } = useModalsContext() @@ -127,12 +127,11 @@ export const EditorProvider: FC = ({ children }) => { ) useEffect(() => { - if (ide?.socket) { - ide.socket.on('projectNameUpdated', setProjectName) - return () => - ide.socket.removeListener('projectNameUpdated', setProjectName) + if (socket) { + socket.on('projectNameUpdated', setProjectName) + return () => socket.removeListener('projectNameUpdated', setProjectName) } - }, [ide?.socket, setProjectName]) + }, [socket, setProjectName]) const renameProject = useCallback( (newName: string) => { diff --git a/services/web/frontend/js/shared/context/ide-context.tsx b/services/web/frontend/js/shared/context/ide-context.tsx index 630494ffb5..7c2122674b 100644 --- a/services/web/frontend/js/shared/context/ide-context.tsx +++ b/services/web/frontend/js/shared/context/ide-context.tsx @@ -1,10 +1,11 @@ import { createContext, FC, useContext, useEffect, useMemo } from 'react' import { ScopeValueStore } from '../../../../types/ide/scope-value-store' import { ScopeEventEmitter } from '../../../../types/ide/scope-event-emitter' +import { Socket } from '@/features/ide-react/connection/types/socket' export type Ide = { - [key: string]: any // TODO: define the rest of the `ide` and `$scope` properties $scope: Record + socket: Socket } type IdeContextValue = Ide & { diff --git a/services/web/frontend/js/shared/context/local-compile-context.tsx b/services/web/frontend/js/shared/context/local-compile-context.tsx index 6cceec8ef5..c4be1c1d63 100644 --- a/services/web/frontend/js/shared/context/local-compile-context.tsx +++ b/services/web/frontend/js/shared/context/local-compile-context.tsx @@ -28,7 +28,6 @@ import { handleLogFiles, handleOutputFiles, } from '../../features/pdf-preview/util/output-files' -import { useIdeContext } from './ide-context' import { useProjectContext } from './project-context' import { useEditorContext } from './editor-context' import { buildFileList } from '../../features/pdf-preview/util/file-list' @@ -112,8 +111,6 @@ export const LocalCompileContext = createContext( ) export const LocalCompileProvider: FC = ({ children }) => { - const ide = useIdeContext() - const { hasPremiumCompile, isProjectOwner } = useEditorContext() const { openDocWithId, openDocs } = useEditorManagerContext() @@ -530,7 +527,6 @@ export const LocalCompileProvider: FC = ({ children }) => { } }, [ data, - ide, alphaProgram, labsProgram, features, diff --git a/services/web/frontend/js/shared/context/mock/mock-ide.js b/services/web/frontend/js/shared/context/mock/mock-ide.js index c47651031a..96bfbd4533 100644 --- a/services/web/frontend/js/shared/context/mock/mock-ide.js +++ b/services/web/frontend/js/shared/context/mock/mock-ide.js @@ -53,9 +53,5 @@ export const getMockIde = () => { settings: { syntaxValidation: false, pdfViewer: 'pdfjs' }, hasLintingError: false, }, - editorManager: { - openDoc: () => {}, - getCurrentDocumentId: () => {}, - }, } } diff --git a/services/web/frontend/stories/decorators/scope.tsx b/services/web/frontend/stories/decorators/scope.tsx index 9fda7c599c..473aa037de 100644 --- a/services/web/frontend/stories/decorators/scope.tsx +++ b/services/web/frontend/stories/decorators/scope.tsx @@ -101,12 +101,6 @@ const initialize = () => { socket: new SocketIOShim.SocketShimNoop( new SocketIOMock() ) as unknown as Socket, - editorManager: { - getCurrentDocumentId: () => 'foo', - openDoc: (id: string, options: unknown) => { - console.log('open doc', id, options) - }, - }, } // window.metaAttributesCache is reset in preview.tsx diff --git a/services/web/test/frontend/features/file-tree/components/file-tree-root.spec.tsx b/services/web/test/frontend/features/file-tree/components/file-tree-root.spec.tsx index 4c2680a072..b4b3bc32d8 100644 --- a/services/web/test/frontend/features/file-tree/components/file-tree-root.spec.tsx +++ b/services/web/test/frontend/features/file-tree/components/file-tree-root.spec.tsx @@ -175,50 +175,6 @@ describe('', function () { ]) }) - it('listen to editor.openDoc', function () { - const rootFolder = [ - { - _id: 'root-folder-id', - name: 'rootFolder', - docs: [ - { _id: '456def', name: 'main.tex' }, - { _id: '789ghi', name: 'other.tex' }, - ], - folders: [], - fileRefs: [], - }, - ] - - cy.mount( - - - - ) - - cy.findByRole('treeitem', { name: 'main.tex', selected: true }) - - // entities not found should be ignored - cy.document().trigger('editor.openDoc', { detail: 'not-an-id' }) - cy.findByRole('treeitem', { name: 'main.tex', selected: true }) - - cy.document().trigger('editor.openDoc', { detail: '789ghi' }) - cy.findByRole('treeitem', { name: 'main.tex', selected: false }) - cy.findByRole('treeitem', { name: 'other.tex', selected: true }) - }) - it('only shows a menu button when a single item is selected', function () { const rootFolder = [ { diff --git a/services/web/types/window.ts b/services/web/types/window.ts index 2aac5c30ed..64f2d9ed47 100644 --- a/services/web/types/window.ts +++ b/services/web/types/window.ts @@ -1,6 +1,7 @@ import 'recurly__recurly-js' import { ScopeValueStore } from './ide/scope-value-store' import { MetaAttributesCache } from '@/utils/meta' +import { Socket } from '@/features/ide-react/connection/types/socket' declare global { // eslint-disable-next-line no-unused-vars @@ -12,10 +13,7 @@ declare global { logEntryAnnotations: Record } } - socket: { - on: (event: string, listener: any) => void - removeListener: (event: string, listener: any) => void - } + socket: Socket } MathJax: Record crypto: {