[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
This commit is contained in:
Antoine Clausse
2025-07-02 10:58:38 +02:00
committed by Copybot
parent 89bdcf59ab
commit f56675da34
4 changed files with 42 additions and 29 deletions

View File

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

View File

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

View File

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

View File

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