Просмотр исходного кода

Minimise use of ide context provider (#23308)

GitOrigin-RevId: 468b8d4b33925bdb97bd46d2c4e7acb969f3d82b
Alf Eaton 1 год назад
Родитель
Сommit
9854d37916
19 измененных файлов с 107 добавлено и 198 удалено
  1. 9 9
      services/web/frontend/js/features/editor-left-menu/hooks/use-project-wide-settings-socket-listener.tsx
  2. 17 12
      services/web/frontend/js/features/file-tree/contexts/file-tree-selectable.tsx
  3. 6 8
      services/web/frontend/js/features/history/context/hooks/use-restore-deleted-file.ts
  4. 6 8
      services/web/frontend/js/features/history/context/hooks/use-restore-selected-file.ts
  5. 0 2
      services/web/frontend/js/features/ide-react/components/layout/ide-page.tsx
  6. 38 10
      services/web/frontend/js/features/ide-react/context/editor-manager-context.tsx
  7. 2 19
      services/web/frontend/js/features/ide-react/context/file-tree-open-context.tsx
  8. 0 24
      services/web/frontend/js/features/ide-react/hooks/use-open-file.ts
  9. 0 25
      services/web/frontend/js/features/ide-react/hooks/use-select-file-tree-entity.ts
  10. 9 0
      services/web/frontend/js/features/ide-react/util/find-doc-entity-by-id.ts
  11. 8 9
      services/web/frontend/js/features/pdf-preview/components/pdf-synctex-controls.jsx
  12. 3 3
      services/web/frontend/js/features/word-count-modal/components/word-count-modal-content.jsx
  13. 5 6
      services/web/frontend/js/shared/context/editor-context.tsx
  14. 2 1
      services/web/frontend/js/shared/context/ide-context.tsx
  15. 0 4
      services/web/frontend/js/shared/context/local-compile-context.tsx
  16. 0 4
      services/web/frontend/js/shared/context/mock/mock-ide.js
  17. 0 6
      services/web/frontend/stories/decorators/scope.tsx
  18. 0 44
      services/web/test/frontend/features/file-tree/components/file-tree-root.spec.tsx
  19. 2 4
      services/web/types/window.ts

+ 9 - 9
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<ProjectSettings | undefined>(
     '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])
 }

+ 17 - 12
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
-
-      dispatch({ type: ACTION_TYPES.SELECT, id: found.entity._id })
-    }
-
-    window.addEventListener('editor.openDoc', handleOpenDoc)
-    return () => window.removeEventListener('editor.openDoc', handleOpenDoc)
-  }, [fileTreeData])
+  // 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<string>) => {
+        const found = findInTree(fileTreeData, event.detail)
+        if (!found) return
+
+        dispatch({ type: ACTION_TYPES.SELECT, id: found.entity._id })
+      },
+      [fileTreeData]
+    )
+  )
 
   const select = useCallback(id => {
     dispatch({ type: ACTION_TYPES.SELECT, id })

+ 6 - 8
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<RestorationState>('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,
   ])
 

+ 6 - 8
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<RestoreState>('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,
   ])
 

+ 0 - 2
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')
 

+ 38 - 10
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<EditorManager | undefined>(
 
 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<BinaryFile | null>('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 (
-    <EditorManagerContext.Provider value={editorManager}>
+    <EditorManagerContext.Provider value={value}>
       {children}
     </EditorManagerContext.Provider>
   )

+ 2 - 19
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<BinaryFile | null>('openFile')
+  const [, setOpenFile] = useScopeValueSetterOnly<BinaryFile | null>('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(() => {

+ 0 - 24
services/web/frontend/js/features/ide-react/hooks/use-open-file.ts

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

+ 0 - 25
services/web/frontend/js/features/ide-react/hooks/use-select-file-tree-entity.ts

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

+ 9 - 0
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
+}

+ 8 - 9
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(

+ 3 - 3
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)
 

+ 5 - 6
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) => {

+ 2 - 1
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<string, any>
+  socket: Socket
 }
 
 type IdeContextValue = Ide & {

+ 0 - 4
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<CompileContext | undefined>(
 )
 
 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,

+ 0 - 4
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: () => {},
-    },
   }
 }

+ 0 - 6
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

+ 0 - 44
services/web/test/frontend/features/file-tree/components/file-tree-root.spec.tsx

@@ -175,50 +175,6 @@ describe('<FileTreeRoot/>', 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(
-      <EditorProviders
-        rootFolder={rootFolder as any}
-        projectId="123abc"
-        rootDocId="456def"
-        features={{} as any}
-        permissionsLevel="readOnly"
-      >
-        <FileTreeRoot
-          refProviders={{}}
-          setRefProviderEnabled={cy.stub()}
-          setStartedFreeTrial={cy.stub()}
-          onSelect={cy.stub()}
-          onInit={cy.stub()}
-          isConnected
-        />
-      </EditorProviders>
-    )
-
-    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 = [
       {

+ 2 - 4
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<string, unknown>
         }
       }
-      socket: {
-        on: (event: string, listener: any) => void
-        removeListener: (event: string, listener: any) => void
-      }
+      socket: Socket
     }
     MathJax: Record<string, any>
     crypto: {