mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-11-12 13:13:45 +01:00
Merge pull request #35182 from overleaf/ii-use-event-listener
[web] Replace useScopeEventEmitter and useScopeEventListener with useEventListener GitOrigin-RevId: 8348c8c980b01e172fee3371980176d2b58ceec6
This commit is contained in:
@@ -24,7 +24,6 @@ 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'
|
||||
import customLocalStorage from '@/infrastructure/local-storage'
|
||||
@@ -111,8 +110,6 @@ export const EditorManagerProvider: FC<React.PropsWithChildren> = ({
|
||||
wantTrackChangesRef.current = wantTrackChanges
|
||||
}, [wantTrackChanges])
|
||||
|
||||
const goToLineEmitter = useScopeEventEmitter('editor:gotoLine')
|
||||
|
||||
const { fileTreeData } = useFileTreeData()
|
||||
|
||||
const [ignoringExternalUpdates, setIgnoringExternalUpdates] = useState(false)
|
||||
@@ -197,12 +194,11 @@ export const EditorManagerProvider: FC<React.PropsWithChildren> = ({
|
||||
[currentDocumentId]
|
||||
)
|
||||
|
||||
const jumpToLine = useCallback(
|
||||
(options: GotoLineOptions) => {
|
||||
goToLineEmitter(options)
|
||||
},
|
||||
[goToLineEmitter]
|
||||
)
|
||||
const jumpToLine = useCallback((options: GotoLineOptions) => {
|
||||
window.dispatchEvent(
|
||||
new CustomEvent('editor:gotoLine', { detail: options })
|
||||
)
|
||||
}, [])
|
||||
|
||||
const attachErrorHandlerToDocument = useCallback(
|
||||
(doc: Doc, document: DocumentContainer) => {
|
||||
@@ -417,7 +413,9 @@ export const EditorManagerProvider: FC<React.PropsWithChildren> = ({
|
||||
}
|
||||
} else if (hasGotoOffset(options)) {
|
||||
const jump = () => {
|
||||
eventEmitter.emit('editor:gotoOffset', options)
|
||||
window.dispatchEvent(
|
||||
new CustomEvent('editor:gotoOffset', { detail: options })
|
||||
)
|
||||
}
|
||||
|
||||
if (isNewDoc) {
|
||||
|
||||
@@ -16,7 +16,6 @@ import {
|
||||
import { JoinProjectPayload } from '@/features/ide-react/connection/join-project-payload'
|
||||
import { useConnectionContext } from '@/features/ide-react/context/connection-context'
|
||||
import { postJSON } from '@/infrastructure/fetch-json'
|
||||
import { ReactScopeEventEmitter } from '@/features/ide-react/scope-event-emitter/react-scope-event-emitter'
|
||||
import getMeta from '@/utils/meta'
|
||||
import { type PermissionsLevel } from '@/features/ide-react/types/permissions'
|
||||
import { useProjectContext } from '@/shared/context/project-context'
|
||||
@@ -48,9 +47,6 @@ export const IdeReactProvider: FC<React.PropsWithChildren> = ({ children }) => {
|
||||
const [permissionsLevel, setPermissionsLevel] =
|
||||
useState<PermissionsLevel>('readOnly')
|
||||
const [outOfSync, setOutOfSync] = useState(false)
|
||||
const [scopeEventEmitter] = useState(
|
||||
() => new ReactScopeEventEmitter(eventEmitter)
|
||||
)
|
||||
const [unstableStore] = useState(() => {
|
||||
const store = new ReactScopeValueStore()
|
||||
// Add dummy editor.ready key for Writefull, that relies on this calling
|
||||
@@ -165,11 +161,7 @@ export const IdeReactProvider: FC<React.PropsWithChildren> = ({ children }) => {
|
||||
|
||||
return (
|
||||
<IdeReactContext.Provider value={value}>
|
||||
<IdeProvider
|
||||
ide={ide}
|
||||
scopeEventEmitter={scopeEventEmitter}
|
||||
unstableStore={unstableStore}
|
||||
>
|
||||
<IdeProvider ide={ide} unstableStore={unstableStore}>
|
||||
{children}
|
||||
</IdeProvider>
|
||||
</IdeReactContext.Provider>
|
||||
|
||||
@@ -15,8 +15,8 @@ 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 useSocketListener from '@/features/ide-react/hooks/use-socket-listener'
|
||||
import useEventListener from '@/shared/hooks/use-event-listener'
|
||||
import { debugConsole } from '@/utils/debugging'
|
||||
import { IdeEvents } from '@/features/ide-react/create-ide-event-emitter'
|
||||
import { getHueForUserId } from '@/shared/utils/colors'
|
||||
import { useEditorOpenDocContext } from '@/features/ide-react/context/editor-open-doc-context'
|
||||
|
||||
@@ -199,21 +199,15 @@ export const OnlineUsersProvider: FC<React.PropsWithChildren> = ({
|
||||
}, [eventEmitter, setAllValues, setOnlineUsers, socket])
|
||||
|
||||
// Track the position of the main cursor
|
||||
useEffect(() => {
|
||||
const handleCursorUpdate = ({
|
||||
detail: [position],
|
||||
}: CustomEvent<IdeEvents['cursor:editor:update']>) => {
|
||||
useEventListener(
|
||||
'cursor:editor:update',
|
||||
useCallback((event: CustomEvent<CursorPosition>) => {
|
||||
const position = event.detail
|
||||
if (position) {
|
||||
setCurrentPosition(position)
|
||||
}
|
||||
}
|
||||
|
||||
eventEmitter.on('cursor:editor:update', handleCursorUpdate)
|
||||
|
||||
return () => {
|
||||
eventEmitter.off('cursor:editor:update', handleCursorUpdate)
|
||||
}
|
||||
}, [cursorUpdateInterval, eventEmitter])
|
||||
}, [])
|
||||
)
|
||||
|
||||
// Send the latest position to other clients when currentPosition changes
|
||||
useEffect(() => {
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react'
|
||||
import useScopeEventEmitter from '@/shared/hooks/use-scope-event-emitter'
|
||||
import useEventListener from '@/shared/hooks/use-event-listener'
|
||||
import { isValidTeXFile } from '@/main/is-valid-tex-file'
|
||||
import localStorage from '@/infrastructure/local-storage'
|
||||
@@ -55,8 +54,6 @@ export const OutlineProvider: FC<React.PropsWithChildren> = ({ children }) => {
|
||||
const [ignoreNextScroll, setIgnoreNextScroll] = useState<boolean>(false)
|
||||
const { sendEvent } = useEditorAnalytics()
|
||||
|
||||
const goToLineEmitter = useScopeEventEmitter('editor:gotoLine', true)
|
||||
|
||||
useEventListener(
|
||||
'file-view:file-opened',
|
||||
useCallback(() => {
|
||||
@@ -106,14 +103,18 @@ export const OutlineProvider: FC<React.PropsWithChildren> = ({ children }) => {
|
||||
const jumpToLine = useCallback(
|
||||
(lineNumber: number, syncToPdf: boolean) => {
|
||||
setIgnoreNextScroll(true)
|
||||
goToLineEmitter({
|
||||
gotoLine: lineNumber,
|
||||
gotoColumn: 0,
|
||||
syncToPdf,
|
||||
})
|
||||
window.dispatchEvent(
|
||||
new CustomEvent('editor:gotoLine', {
|
||||
detail: {
|
||||
gotoLine: lineNumber,
|
||||
gotoColumn: 0,
|
||||
syncToPdf,
|
||||
},
|
||||
})
|
||||
)
|
||||
sendEvent('outline-jump-to-line')
|
||||
},
|
||||
[goToLineEmitter, sendEvent]
|
||||
[sendEvent]
|
||||
)
|
||||
|
||||
const highlightedLine = useMemo(
|
||||
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
import {
|
||||
ScopeEventEmitter,
|
||||
ScopeEventName,
|
||||
} from '../../../../../types/ide/scope-event-emitter'
|
||||
import { IdeEvents } from '@/features/ide-react/create-ide-event-emitter'
|
||||
|
||||
export class ReactScopeEventEmitter implements ScopeEventEmitter {
|
||||
// eslint-disable-next-line no-useless-constructor
|
||||
constructor(private readonly eventEmitter: EventTarget) {}
|
||||
|
||||
emit<T extends ScopeEventName>(
|
||||
eventName: T,
|
||||
broadcast: boolean,
|
||||
...detail: IdeEvents[T]
|
||||
) {
|
||||
this.eventEmitter.dispatchEvent(new CustomEvent(eventName, { detail }))
|
||||
}
|
||||
|
||||
on<T extends ScopeEventName>(
|
||||
eventName: T,
|
||||
listener: (event: Event, ...args: IdeEvents[T]) => void
|
||||
) {
|
||||
const wrappedListener = (event: CustomEvent<IdeEvents[T]>) => {
|
||||
listener(event, ...event.detail)
|
||||
}
|
||||
this.eventEmitter.addEventListener(
|
||||
eventName,
|
||||
wrappedListener as EventListener
|
||||
)
|
||||
return () => {
|
||||
this.eventEmitter.removeEventListener(
|
||||
eventName,
|
||||
wrappedListener as EventListener
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
import { FC, memo } from 'react'
|
||||
import { FC, memo, useCallback } from 'react'
|
||||
import OutlinePane from '@/features/outline/components/outline-pane'
|
||||
import { useOutlineContext } from '@/features/ide-react/context/outline-context'
|
||||
import useScopeEventEmitter from '@/shared/hooks/use-scope-event-emitter'
|
||||
import useNestedOutline from '../hooks/use-nested-outline'
|
||||
|
||||
export const OutlineContainer: FC = memo(() => {
|
||||
@@ -13,7 +12,9 @@ export const OutlineContainer: FC = memo(() => {
|
||||
toggleOutlineExpanded,
|
||||
} = useOutlineContext()
|
||||
|
||||
const outlineToggledEmitter = useScopeEventEmitter('outline-toggled')
|
||||
const handleToggle = useCallback((isOpen: boolean) => {
|
||||
window.dispatchEvent(new CustomEvent('outline-toggled', { detail: isOpen }))
|
||||
}, [])
|
||||
|
||||
const outline = useNestedOutline()
|
||||
|
||||
@@ -21,7 +22,7 @@ export const OutlineContainer: FC = memo(() => {
|
||||
<div className="outline-container">
|
||||
<OutlinePane
|
||||
outline={outline.items}
|
||||
onToggle={outlineToggledEmitter}
|
||||
onToggle={handleToggle}
|
||||
isTexFile={canShowOutline}
|
||||
jumpToLine={jumpToLine}
|
||||
highlightedLine={highlightedLine}
|
||||
|
||||
@@ -8,7 +8,6 @@ import useDetachState from '../../../shared/hooks/use-detach-state'
|
||||
import useDetachAction from '../../../shared/hooks/use-detach-action'
|
||||
import localStorage from '../../../infrastructure/local-storage'
|
||||
import { useFileTreeData } from '../../../shared/context/file-tree-data-context'
|
||||
import useScopeEventListener from '../../../shared/hooks/use-scope-event-listener'
|
||||
import { debugConsole } from '@/utils/debugging'
|
||||
import { useFileTreePathContext } from '@/features/file-tree/contexts/file-tree-path'
|
||||
import { useEditorManagerContext } from '@/features/ide-react/context/editor-manager-context'
|
||||
@@ -165,7 +164,7 @@ export default function useSynctex(): {
|
||||
}
|
||||
}, [getCurrentFilePath, goToPdfLocation])
|
||||
|
||||
useScopeEventListener(
|
||||
useEventListener(
|
||||
'cursor:editor:syncToPdf',
|
||||
useCallback(() => {
|
||||
syncToPdf()
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { useCallback, useEffect, useRef } from 'react'
|
||||
import { EditorState } from '@codemirror/state'
|
||||
import useScopeEventEmitter from '../../../shared/hooks/use-scope-event-emitter'
|
||||
import useEventListener from '../../../shared/hooks/use-event-listener'
|
||||
import useScopeEventListener from '../../../shared/hooks/use-scope-event-listener'
|
||||
import { createExtensions } from '../extensions'
|
||||
import { setEditorTheme, setOptionsTheme } from '../extensions/theme'
|
||||
import {
|
||||
@@ -504,13 +502,16 @@ function useCodeMirrorScope(view: EditorView) {
|
||||
settingsRef.current.referencesSearchMode = referencesSearchMode
|
||||
}, [referencesSearchMode])
|
||||
|
||||
const emitSyncToPdf = useScopeEventEmitter('cursor:editor:syncToPdf')
|
||||
const emitSyncToPdf = useCallback(() => {
|
||||
window.dispatchEvent(new CustomEvent('cursor:editor:syncToPdf'))
|
||||
}, [])
|
||||
|
||||
// select and scroll to position on editor:gotoLine event (from synctex)
|
||||
useScopeEventListener(
|
||||
useEventListener(
|
||||
'editor:gotoLine',
|
||||
useCallback(
|
||||
(_event: any, options: GotoLineOptions) => {
|
||||
(event: CustomEvent<GotoLineOptions>) => {
|
||||
const options = event.detail
|
||||
setCursorLineAndScroll(
|
||||
view,
|
||||
options.gotoLine,
|
||||
@@ -526,42 +527,16 @@ function useCodeMirrorScope(view: EditorView) {
|
||||
)
|
||||
|
||||
// select and scroll to position on editor:gotoOffset event (from review panel)
|
||||
useScopeEventListener(
|
||||
useEventListener(
|
||||
'editor:gotoOffset',
|
||||
useCallback(
|
||||
(_event: any, options: GotoOffsetOptions) => {
|
||||
setCursorPositionAndScroll(view, options.gotoOffset)
|
||||
(event: CustomEvent<GotoOffsetOptions>) => {
|
||||
setCursorPositionAndScroll(view, event.detail.gotoOffset)
|
||||
},
|
||||
[view]
|
||||
)
|
||||
)
|
||||
|
||||
// dispatch 'cursor:editor:update' to Angular scope (for synctex and realtime)
|
||||
const dispatchCursorUpdate = useScopeEventEmitter('cursor:editor:update')
|
||||
|
||||
const handleCursorUpdate = useCallback(
|
||||
(event: CustomEvent) => {
|
||||
dispatchCursorUpdate(event.detail)
|
||||
},
|
||||
[dispatchCursorUpdate]
|
||||
)
|
||||
|
||||
// listen for 'cursor:editor:update' events from CodeMirror, and dispatch them to Angular
|
||||
useEventListener('cursor:editor:update', handleCursorUpdate)
|
||||
|
||||
// dispatch 'cursor:editor:update' to Angular scope (for outline)
|
||||
const dispatchScrollUpdate = useScopeEventEmitter('scroll:editor:update')
|
||||
|
||||
const handleScrollUpdate = useCallback(
|
||||
(event: CustomEvent) => {
|
||||
dispatchScrollUpdate(event.detail)
|
||||
},
|
||||
[dispatchScrollUpdate]
|
||||
)
|
||||
|
||||
// listen for 'cursor:editor:update' events from CodeMirror, and dispatch them to Angular
|
||||
useEventListener('scroll:editor:update', handleScrollUpdate)
|
||||
|
||||
// enable the compile log linter a) when "Code Check" is off, b) when the project hasn't changed and isn't compiling.
|
||||
// the project "changed at" date is reset at the start of the compile, i.e. "the project hasn't changed",
|
||||
// but we don't want to display the compile log diagnostics from the previous compile.
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
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'
|
||||
import { useUserSettingsContext } from './user-settings-context'
|
||||
import { userStyles } from '../utils/styles'
|
||||
@@ -11,7 +10,6 @@ export type Ide = {
|
||||
}
|
||||
|
||||
type IdeContextValue = Ide & {
|
||||
scopeEventEmitter: ScopeEventEmitter
|
||||
unstableStore: ScopeValueStore
|
||||
}
|
||||
|
||||
@@ -20,10 +18,9 @@ export const IdeContext = createContext<IdeContextValue | undefined>(undefined)
|
||||
export const IdeProvider: FC<
|
||||
React.PropsWithChildren<{
|
||||
ide: Ide
|
||||
scopeEventEmitter: ScopeEventEmitter
|
||||
unstableStore: ScopeValueStore
|
||||
}>
|
||||
> = ({ ide, scopeEventEmitter, unstableStore, children }) => {
|
||||
> = ({ ide, unstableStore, children }) => {
|
||||
/**
|
||||
* Expose unstableStore via `window.overleaf.unstable.store`, so it can be accessed by external extensions.
|
||||
*
|
||||
@@ -65,10 +62,9 @@ export const IdeProvider: FC<
|
||||
const value = useMemo<IdeContextValue>(() => {
|
||||
return {
|
||||
...ide,
|
||||
scopeEventEmitter,
|
||||
unstableStore,
|
||||
}
|
||||
}, [ide, scopeEventEmitter, unstableStore])
|
||||
}, [ide, unstableStore])
|
||||
|
||||
return <IdeContext.Provider value={value}>{children}</IdeContext.Provider>
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@ import getMeta from '../../utils/meta'
|
||||
import { DetachRole } from './detach-context'
|
||||
import { debugConsole } from '@/utils/debugging'
|
||||
import { BinaryFile } from '@/features/file-view/types/binary-file'
|
||||
import useScopeEventEmitter from '@/shared/hooks/use-scope-event-emitter'
|
||||
import useEventListener from '@/shared/hooks/use-event-listener'
|
||||
import { isMac } from '@/shared/utils/os'
|
||||
import { sendSearchEvent } from '@/features/event-tracking/search-events'
|
||||
@@ -90,7 +89,6 @@ export const LayoutProvider: FC<React.PropsWithChildren> = ({ children }) => {
|
||||
// what to show in the "flat" view (editor or pdf)
|
||||
const [view, _setView] = useState<IdeView | null>('editor')
|
||||
const [openFile, setOpenFile] = useState<BinaryFile | null>(null)
|
||||
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
|
||||
@@ -102,7 +100,7 @@ export const LayoutProvider: FC<React.PropsWithChildren> = ({ children }) => {
|
||||
_setView(oldValue => {
|
||||
// ensure that the "history:toggle" event is broadcast when switching in or out of history view
|
||||
if (value === 'history' || oldValue === 'history') {
|
||||
historyToggleEmitter()
|
||||
window.dispatchEvent(new CustomEvent('history:toggle'))
|
||||
}
|
||||
|
||||
if (value === 'history') {
|
||||
@@ -121,14 +119,7 @@ export const LayoutProvider: FC<React.PropsWithChildren> = ({ children }) => {
|
||||
return value
|
||||
})
|
||||
},
|
||||
[
|
||||
_setView,
|
||||
setRailIsOpen,
|
||||
historyToggleEmitter,
|
||||
prevRailIsOpen,
|
||||
setPrevRailIsOpen,
|
||||
railIsOpen,
|
||||
]
|
||||
[_setView, setRailIsOpen, prevRailIsOpen, setPrevRailIsOpen, railIsOpen]
|
||||
)
|
||||
|
||||
const restoreView = useCallback(() => {
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
import { useCallback } from 'react'
|
||||
import { useIdeContext } from '../context/ide-context'
|
||||
import { ScopeEventName } from '../../../../types/ide/scope-event-emitter'
|
||||
import { IdeEvents } from '@/features/ide-react/create-ide-event-emitter'
|
||||
|
||||
export default function useScopeEventEmitter<T extends ScopeEventName>(
|
||||
eventName: T,
|
||||
broadcast = true
|
||||
) {
|
||||
const { scopeEventEmitter } = useIdeContext()
|
||||
|
||||
return useCallback(
|
||||
(...detail: IdeEvents[T]) => {
|
||||
scopeEventEmitter.emit(eventName, broadcast, ...detail)
|
||||
},
|
||||
[scopeEventEmitter, eventName, broadcast]
|
||||
)
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import { useEffect } from 'react'
|
||||
import { useIdeContext } from '../context/ide-context'
|
||||
import { ScopeEventName } from '../../../../types/ide/scope-event-emitter'
|
||||
import { IdeEvents } from '@/features/ide-react/create-ide-event-emitter'
|
||||
|
||||
export default function useScopeEventListener<T extends ScopeEventName>(
|
||||
eventName: T,
|
||||
listener: (event: Event, ...args: IdeEvents[T]) => void
|
||||
) {
|
||||
const { scopeEventEmitter } = useIdeContext()
|
||||
|
||||
useEffect(() => {
|
||||
return scopeEventEmitter.on(eventName, listener)
|
||||
}, [scopeEventEmitter, eventName, listener])
|
||||
}
|
||||
@@ -14,7 +14,6 @@ import React, {
|
||||
import { IdeReactContext } from '@/features/ide-react/context/ide-react-context'
|
||||
import { IdeEventEmitter } from '@/features/ide-react/create-ide-event-emitter'
|
||||
import { ReactScopeValueStore } from '@/features/ide-react/scope-value-store/react-scope-value-store'
|
||||
import { ReactScopeEventEmitter } from '@/features/ide-react/scope-event-emitter/react-scope-event-emitter'
|
||||
import { ConnectionContext } from '@/features/ide-react/context/connection-context'
|
||||
import {
|
||||
EditorOpenDocContext,
|
||||
@@ -398,14 +397,10 @@ const makeIdeReactProvider = (
|
||||
}))
|
||||
|
||||
const [ideContextValue] = useState(() => {
|
||||
const scopeEventEmitter = new ReactScopeEventEmitter(
|
||||
new IdeEventEmitter()
|
||||
)
|
||||
const unstableStore = new ReactScopeValueStore()
|
||||
|
||||
return {
|
||||
socket,
|
||||
scopeEventEmitter,
|
||||
unstableStore,
|
||||
}
|
||||
})
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
import { IdeEvents } from '@/features/ide-react/create-ide-event-emitter'
|
||||
|
||||
export type ScopeEventName = keyof IdeEvents
|
||||
|
||||
export interface ScopeEventEmitter {
|
||||
emit: <T extends ScopeEventName>(
|
||||
eventName: T,
|
||||
broadcast: boolean,
|
||||
...detail: IdeEvents[T]
|
||||
) => void
|
||||
on: <T extends ScopeEventName>(
|
||||
eventName: T,
|
||||
listener: (event: Event, ...args: IdeEvents[T]) => void
|
||||
) => () => void
|
||||
}
|
||||
Reference in New Issue
Block a user