Przeglądaj źródła

[web] Move `pdf.*` scopes to react states (#26599)

* Move `pdf.logEntryAnnotations` to react state

* Remove unused scope `pdf.downloadUrl`

* Remove unused scope `pdf.url`

* Move `pdf.uncompiled` to react state

* Move `pdf.logEntries` to react state

* Remove `pdf` from `mockScope`

* Fix test: "renders annotations in the gutter"

GitOrigin-RevId: bf1d0ec30cc0ffcc1177871651483c296ed08baf
Antoine Clausse 1 rok temu
rodzic
commit
40136785dd

+ 4 - 0
services/web/frontend/js/features/pdf-preview/util/output-files.js

@@ -143,6 +143,10 @@ export const handleLogFiles = async (outputFiles, data, signal) => {
   return result
 }
 
+/**
+ * @typedef {import('../../../../../types/annotation').Annotation} Annotation
+ * @returns {Record<string, Annotation[]>}
+ */
 export function buildLogEntryAnnotations(entries, fileTreeData, rootDocId) {
   const rootDocDirname = dirname(fileTreeData, rootDocId)
 

+ 11 - 23
services/web/frontend/js/shared/context/local-compile-context.tsx

@@ -11,7 +11,6 @@ import {
   SetStateAction,
 } from 'react'
 import useScopeValue from '../hooks/use-scope-value'
-import useScopeValueSetterOnly from '../hooks/use-scope-value-setter-only'
 import usePersistedState from '../hooks/use-persisted-state'
 import useAbortController from '../hooks/use-abort-controller'
 import DocumentCompiler from '../../features/pdf-preview/util/compiler'
@@ -51,6 +50,7 @@ import { isSplitTestEnabled } from '@/utils/splitTestUtils'
 import { captureException } from '@/infrastructure/error-reporter'
 import OError from '@overleaf/o-error'
 import getMeta from '@/utils/meta'
+import type { Annotation } from '../../../../types/annotation'
 
 type PdfFile = Record<string, any>
 
@@ -74,7 +74,7 @@ export type CompileContext = {
     warnings: LogEntry[]
     typesetting: LogEntry[]
   }
-  logEntryAnnotations?: Record<string, any>
+  logEntryAnnotations?: Record<string, Annotation[]>
   outputFilesArchive?: string
   pdfDownloadUrl?: string
   pdfFile?: PdfFile
@@ -98,7 +98,7 @@ export type CompileContext = {
   stopOnFirstError: boolean
   stopOnValidationError: boolean
   stoppedOnFirstError: boolean
-  uncompiled?: boolean
+  uncompiled: boolean
   validationIssues?: Record<string, any>
   firstRenderDone: (metrics: {
     latencyFetch: number
@@ -156,34 +156,22 @@ export const LocalCompileProvider: FC<React.PropsWithChildren> = ({
   const [hasShortCompileTimeout, setHasShortCompileTimeout] = useState(false)
 
   // the log entries parsed from the compile output log
-  const [logEntries, setLogEntries] = useScopeValueSetterOnly('pdf.logEntries')
+  const [logEntries, setLogEntries] = useState<CompileContext['logEntries']>()
 
   // annotations for display in the editor, built from the log entries
-  const [logEntryAnnotations, setLogEntryAnnotations] = useScopeValue(
-    'pdf.logEntryAnnotations'
-  )
+  const [logEntryAnnotations, setLogEntryAnnotations] = useState<
+    undefined | Record<string, Annotation[]>
+  >()
 
   // the PDF viewer and whether syntax validation is enabled globally
   const { userSettings } = useUserSettingsContext()
   const { pdfViewer, syntaxValidation } = userSettings
 
-  // the URL for downloading the PDF
-  const [, setPdfDownloadUrl] =
-    useScopeValueSetterOnly<string>('pdf.downloadUrl')
-
-  // the URL for loading the PDF in the preview pane
-  const [, setPdfUrl] = useScopeValueSetterOnly<string>('pdf.url')
-
   // low level details for metrics
   const [pdfFile, setPdfFile] = useState<PdfFile | undefined>()
 
-  useEffect(() => {
-    setPdfDownloadUrl(pdfFile?.pdfDownloadUrl)
-    setPdfUrl(pdfFile?.pdfUrl)
-  }, [pdfFile, setPdfDownloadUrl, setPdfUrl])
-
   // the project is considered to be "uncompiled" if a doc has changed, or finished saving, since the last compile started.
-  const [uncompiled, setUncompiled] = useScopeValue('pdf.uncompiled')
+  const [uncompiled, setUncompiled] = useState(false)
 
   // whether a doc has been edited since the last compile started
   const [editedSinceCompileStarted, setEditedSinceCompileStarted] =
@@ -294,7 +282,7 @@ export const LocalCompileProvider: FC<React.PropsWithChildren> = ({
 
   const cleanupCompileResult = useCallback(() => {
     setPdfFile(undefined)
-    setLogEntries(null)
+    setLogEntries(undefined)
     setLogEntryAnnotations({})
   }, [setPdfFile, setLogEntries, setLogEntryAnnotations])
 
@@ -513,8 +501,8 @@ export const LocalCompileProvider: FC<React.PropsWithChildren> = ({
 
         // handle log files
         // asynchronous (TODO: cancel on new compile?)
-        setLogEntryAnnotations(null)
-        setLogEntries(null)
+        setLogEntryAnnotations(undefined)
+        setLogEntries(undefined)
         setRawLog(undefined)
 
         handleLogFiles(outputFiles, data, abortController.signal).then(

+ 27 - 3
services/web/test/frontend/features/source-editor/components/codemirror-editor.spec.tsx

@@ -7,7 +7,9 @@ import { activeEditorLine } from '../helpers/active-editor-line'
 import { TestContainer } from '../helpers/test-container'
 import customLocalStorage from '@/infrastructure/local-storage'
 import { OnlineUsersContext } from '@/features/ide-react/context/online-users-context'
-import { FC } from 'react'
+import { LocalCompileContext } from '@/shared/context/local-compile-context'
+import type { FC, PropsWithChildren } from 'react'
+import type { Annotation } from '../../../../../types/annotation'
 
 describe('<CodeMirrorEditor/>', { scrollBehavior: false }, function () {
   beforeEach(function () {
@@ -64,27 +66,39 @@ describe('<CodeMirrorEditor/>', { scrollBehavior: false }, function () {
   it('renders annotations in the gutter', function () {
     const scope = mockScope()
 
-    scope.pdf.logEntryAnnotations = {
+    const logEntryAnnotations: Record<string, Annotation[]> = {
       [docId]: [
         {
+          id: '1',
+          entryIndex: 1,
           row: 20,
           type: 'error',
           text: 'Another error',
+          firstOnLine: true,
         },
         {
+          id: '2',
+          entryIndex: 2,
           row: 19,
           type: 'error',
           text: 'An error',
+          firstOnLine: true,
         },
         {
+          id: '3',
+          entryIndex: 3,
           row: 20,
           type: 'warning',
           text: 'A warning on the same line',
+          firstOnLine: false,
         },
         {
+          id: '4',
+          entryIndex: 4,
           row: 25,
           type: 'warning',
           text: 'Another warning',
+          firstOnLine: true,
         },
       ],
     }
@@ -93,9 +107,19 @@ describe('<CodeMirrorEditor/>', { scrollBehavior: false }, function () {
 
     cy.clock()
 
+    const LocalCompileProvider: FC<PropsWithChildren> = ({ children }) => (
+      // @ts-expect-error: not entering all the values for LocalCompileContext
+      <LocalCompileContext.Provider value={{ logEntryAnnotations }}>
+        {children}
+      </LocalCompileContext.Provider>
+    )
     cy.mount(
       <TestContainer>
-        <EditorProviders scope={scope} userSettings={userSettings}>
+        <EditorProviders
+          scope={scope}
+          userSettings={userSettings}
+          providers={{ LocalCompileProvider }}
+        >
           <CodeMirrorEditor />
         </EditorProviders>
       </TestContainer>

+ 0 - 3
services/web/test/frontend/features/source-editor/helpers/mock-scope.ts

@@ -22,9 +22,6 @@ export const mockScope = (
       showVisual: false,
       wantTrackChanges: false,
     },
-    pdf: {
-      logEntryAnnotations: {},
-    },
     project: {
       _id: 'test-project',
       name: 'Test Project',