diff --git a/services/web/frontend/js/features/file-tree/components/file-tree-context.tsx b/services/web/frontend/js/features/file-tree/components/file-tree-context.tsx index 95224aeb64..2913c6f9e0 100644 --- a/services/web/frontend/js/features/file-tree/components/file-tree-context.tsx +++ b/services/web/frontend/js/features/file-tree/components/file-tree-context.tsx @@ -10,7 +10,6 @@ import { FC } from 'react' // FileTreeMutable: provides entities mutation operations // FileTreeSelectable: handles selection and multi-selection const FileTreeContext: FC<{ - reindexReferences: () => void refProviders: Record setRefProviderEnabled: (provider: string, value: boolean) => void setStartedFreeTrial: (value: boolean) => void @@ -18,7 +17,6 @@ const FileTreeContext: FC<{ fileTreeContainer?: HTMLDivElement }> = ({ refProviders, - reindexReferences, setRefProviderEnabled, setStartedFreeTrial, onSelect, @@ -30,10 +28,9 @@ const FileTreeContext: FC<{ refProviders={refProviders} setRefProviderEnabled={setRefProviderEnabled} setStartedFreeTrial={setStartedFreeTrial} - reindexReferences={reindexReferences} > - + {children} diff --git a/services/web/frontend/js/features/file-tree/components/file-tree-root.tsx b/services/web/frontend/js/features/file-tree/components/file-tree-root.tsx index a5a4844bf0..66f547aa5c 100644 --- a/services/web/frontend/js/features/file-tree/components/file-tree-root.tsx +++ b/services/web/frontend/js/features/file-tree/components/file-tree-root.tsx @@ -26,11 +26,9 @@ const FileTreeRoot = React.memo<{ isConnected: boolean setRefProviderEnabled: () => void setStartedFreeTrial: () => void - reindexReferences: () => void refProviders: Record }>(function FileTreeRoot({ refProviders, - reindexReferences, setRefProviderEnabled, setStartedFreeTrial, onSelect, @@ -95,7 +93,6 @@ const FileTreeRoot = React.memo<{ refProviders={refProviders} setRefProviderEnabled={setRefProviderEnabled} setStartedFreeTrial={setStartedFreeTrial} - reindexReferences={reindexReferences} onSelect={onSelect} fileTreeContainer={fileTreeContainer} > diff --git a/services/web/frontend/js/features/file-tree/contexts/file-tree-actionable.tsx b/services/web/frontend/js/features/file-tree/contexts/file-tree-actionable.tsx index c85926b61b..1b0257e5b9 100644 --- a/services/web/frontend/js/features/file-tree/contexts/file-tree-actionable.tsx +++ b/services/web/frontend/js/features/file-tree/contexts/file-tree-actionable.tsx @@ -33,6 +33,7 @@ import { DuplicateFilenameMoveError, } from '../errors' import { Folder } from '../../../../../types/folder' +import { useReferencesContext } from '@/features/ide-react/context/references-context' type DroppedFile = File & { relativePath?: string @@ -216,11 +217,10 @@ function fileTreeActionableReducer(state: State, action: Action) { } } -export const FileTreeActionableProvider: FC<{ - reindexReferences: () => void -}> = ({ reindexReferences, children }) => { +export const FileTreeActionableProvider: FC = ({ children }) => { const { _id: projectId } = useProjectContext() const { permissionsLevel } = useEditorContext() + const { indexAllReferences } = useReferencesContext() const [state, dispatch] = useReducer( permissionsLevel === 'readOnly' @@ -305,7 +305,7 @@ export const FileTreeActionableProvider: FC<{ // @ts-ignore (TODO: improve mapSeries types) .then(() => { if (shouldReindexReferences) { - reindexReferences() + indexAllReferences(true) } dispatch({ type: ACTION_TYPES.CLEAR }) }) @@ -314,7 +314,7 @@ export const FileTreeActionableProvider: FC<{ dispatch({ type: ACTION_TYPES.ERROR, error }) }) ) - }, [fileTreeData, projectId, selectedEntityIds, reindexReferences]) + }, [fileTreeData, projectId, selectedEntityIds, indexAllReferences]) // moves entities. Tree is updated immediately and data are sync'd after. const finishMoving = useCallback( diff --git a/services/web/frontend/js/features/file-tree/contexts/file-tree-main.tsx b/services/web/frontend/js/features/file-tree/contexts/file-tree-main.tsx index 33a051a633..85872fe129 100644 --- a/services/web/frontend/js/features/file-tree/contexts/file-tree-main.tsx +++ b/services/web/frontend/js/features/file-tree/contexts/file-tree-main.tsx @@ -5,7 +5,6 @@ type ContextMenuCoords = { top: number; left: number } const FileTreeMainContext = createContext< | { refProviders: object - reindexReferences: () => void setRefProviderEnabled: (provider: string, value: boolean) => void setStartedFreeTrial: (value: boolean) => void contextMenuCoords: ContextMenuCoords | null @@ -27,13 +26,11 @@ export function useFileTreeMainContext() { } export const FileTreeMainProvider: FC<{ - reindexReferences: () => void refProviders: object setRefProviderEnabled: (provider: string, value: boolean) => void setStartedFreeTrial: (value: boolean) => void }> = ({ refProviders, - reindexReferences, setRefProviderEnabled, setStartedFreeTrial, children, @@ -45,7 +42,6 @@ export const FileTreeMainProvider: FC<{ user.refProviders || {} ) - function reindexReferences() { - indexAllReferences(true) - } - const setRefProviderEnabled = useCallback( (provider: keyof RefProviders, value = true) => { setRefProviders(refProviders => ({ ...refProviders, [provider]: value })) @@ -33,7 +27,6 @@ export const FileTree = memo(function FileTree() { return ( void - indexAllReferences: (shouldBroadcast: boolean) => void -} - -type IndexReferencesResponse = References - -const ReferencesContext = createContext( - undefined -) - -export function populateReferenceScope(store: ReactScopeValueStore) { - store.set('$root._references', { keys: [] }) -} +export const ReferencesContext = createContext< + | { + referenceKeys: Set + indexAllReferences: (shouldBroadcast: boolean) => void + } + | undefined +>(undefined) export const ReferencesProvider: FC = ({ children }) => { const { fileTreeData } = useFileTreeData() const { eventEmitter, projectId } = useIdeReactContext() const { socket } = useConnectionContext() - const [references, setReferences] = - useScopeValue('$root._references') + const [referenceKeys, setReferenceKeys] = useState(new Set()) const [existingIndexHash, setExistingIndexHash] = useState< Record >({}) - const storeReferencesKeys = useCallback( - (newKeys: string[], replaceExistingKeys: boolean) => { - const oldKeys = references.keys - const keys = replaceExistingKeys ? newKeys : _.union(oldKeys, newKeys) - window.dispatchEvent( - new CustomEvent('project:references', { - detail: keys, - }) - ) - setReferences({ keys }) - }, - [references.keys, setReferences] - ) - const indexAllReferences = useCallback( (shouldBroadcast: boolean) => { postJSON(`/project/${projectId}/references/indexAll`, { @@ -75,15 +43,15 @@ export const ReferencesProvider: FC = ({ children }) => { shouldBroadcast, }, }) - .then((response: IndexReferencesResponse) => { - storeReferencesKeys(response.keys, true) + .then((response: { keys: string[] }) => { + setReferenceKeys(new Set(response.keys)) }) .catch(error => { // allow the request to fail debugConsole.error(error) }) }, - [projectId, storeReferencesKeys] + [projectId] ) const indexReferencesIfDocModified = useCallback( @@ -132,25 +100,15 @@ export const ReferencesProvider: FC = ({ children }) => { } }, [eventEmitter, fileTreeData, indexReferencesIfDocModified]) - useEffect(() => { - const handleShouldReindex = () => { - indexAllReferences(true) - } - - eventEmitter.on('references:should-reindex', handleShouldReindex) - - return () => { - eventEmitter.off('references:should-reindex', handleShouldReindex) - } - }, [eventEmitter, indexAllReferences]) - useEffect(() => { const handleProjectJoined = () => { // We only need to grab the references when the editor first loads, // not on every reconnect - socket.on('references:keys:updated', (keys, allDocs) => - storeReferencesKeys(keys, allDocs) - ) + socket.on('references:keys:updated', (keys, allDocs) => { + setReferenceKeys(oldKeys => + allDocs ? new Set(keys) : new Set([...oldKeys, ...keys]) + ) + }) indexAllReferences(false) } @@ -159,14 +117,14 @@ export const ReferencesProvider: FC = ({ children }) => { return () => { eventEmitter.off('project:joined', handleProjectJoined) } - }, [eventEmitter, indexAllReferences, socket, storeReferencesKeys]) + }, [eventEmitter, indexAllReferences, socket]) - const value = useMemo( + const value = useMemo( () => ({ - indexReferencesIfDocModified, + referenceKeys, indexAllReferences, }), - [indexReferencesIfDocModified, indexAllReferences] + [indexAllReferences, referenceKeys] ) return ( @@ -176,7 +134,7 @@ export const ReferencesProvider: FC = ({ children }) => { ) } -export function useReferencesContext(): ReferencesContextValue { +export function useReferencesContext() { const context = useContext(ReferencesContext) if (!context) { diff --git a/services/web/frontend/js/features/ide-react/create-ide-event-emitter.ts b/services/web/frontend/js/features/ide-react/create-ide-event-emitter.ts index b130090afd..ed20e380c4 100644 --- a/services/web/frontend/js/features/ide-react/create-ide-event-emitter.ts +++ b/services/web/frontend/js/features/ide-react/create-ide-event-emitter.ts @@ -20,7 +20,6 @@ export type IdeEvents = { 'cursor:editor:syncToPdf': [] 'scroll:editor:update': [] 'comment:start_adding': [] - 'references:should-reindex': [] 'history:toggle': [] 'entity:deleted': [entity: FileTreeFindResult] } diff --git a/services/web/frontend/js/features/source-editor/extensions/language.ts b/services/web/frontend/js/features/source-editor/extensions/language.ts index da61b4c09b..b36c14135c 100644 --- a/services/web/frontend/js/features/source-editor/extensions/language.ts +++ b/services/web/frontend/js/features/source-editor/extensions/language.ts @@ -24,7 +24,7 @@ type Metadata = { labels: Set packageNames: Set commands: Command[] - references: string[] + referenceKeys: Set fileTreeData: Folder } diff --git a/services/web/frontend/js/features/source-editor/hooks/use-codemirror-scope.ts b/services/web/frontend/js/features/source-editor/hooks/use-codemirror-scope.ts index 853633916c..c83fe1c617 100644 --- a/services/web/frontend/js/features/source-editor/hooks/use-codemirror-scope.ts +++ b/services/web/frontend/js/features/source-editor/hooks/use-codemirror-scope.ts @@ -60,6 +60,7 @@ import { useLayoutContext } from '@/shared/context/layout-context' import { debugConsole } from '@/utils/debugging' import { useMetadataContext } from '@/features/ide-react/context/metadata-context' import { useUserContext } from '@/shared/context/user-context' +import { useReferencesContext } from '@/features/ide-react/context/references-context' function useCodeMirrorScope(view: EditorView) { const { fileTreeData } = useFileTreeData() @@ -107,7 +108,7 @@ function useCodeMirrorScope(view: EditorView) { const [visual] = useScopeValue('editor.showVisual') - const [references] = useScopeValue<{ keys: string[] }>('$root._references') + const { referenceKeys } = useReferencesContext() // build the translation phrases const phrases = usePhrases() @@ -214,7 +215,7 @@ function useCodeMirrorScope(view: EditorView) { // TODO: read this data from the scope? const metadataRef = useRef({ ...metadata, - references: references.keys, + referenceKeys, fileTreeData, }) @@ -226,13 +227,9 @@ function useCodeMirrorScope(view: EditorView) { // listen to project reference keys updates useEffect(() => { - const listener = (event: Event) => { - metadataRef.current.references = (event as CustomEvent).detail - view.dispatch(setMetadata(metadataRef.current)) - } - window.addEventListener('project:references', listener) - return () => window.removeEventListener('project:references', listener) - }, [view]) + metadataRef.current.referenceKeys = referenceKeys + view.dispatch(setMetadata(metadataRef.current)) + }, [view, referenceKeys]) // listen to project root folder updates useEffect(() => { diff --git a/services/web/frontend/js/features/source-editor/languages/latex/completions/references.ts b/services/web/frontend/js/features/source-editor/languages/latex/completions/references.ts index 147d3e86b8..1e994481a2 100644 --- a/services/web/frontend/js/features/source-editor/languages/latex/completions/references.ts +++ b/services/web/frontend/js/features/source-editor/languages/latex/completions/references.ts @@ -16,10 +16,10 @@ export function buildReferenceCompletions( return } - for (const reference of metadata.references) { + for (const referenceKey of metadata.referenceKeys) { completions.references.push({ type: 'reference', - label: reference, + label: referenceKey, extend: extendRequiredParameter, }) } diff --git a/services/web/frontend/stories/decorators/scope.tsx b/services/web/frontend/stories/decorators/scope.tsx index 5529a762d2..9f8838125b 100644 --- a/services/web/frontend/stories/decorators/scope.tsx +++ b/services/web/frontend/stories/decorators/scope.tsx @@ -97,11 +97,6 @@ const initialize = () => { // }, $broadcast: () => {}, - $root: { - _references: { - keys: ['bibkeyExample'], - }, - }, ui: { chatOpen: true, pdfLayout: 'flat', diff --git a/services/web/frontend/stories/file-tree.stories.jsx b/services/web/frontend/stories/file-tree.stories.jsx index 1832024e25..51621cb094 100644 --- a/services/web/frontend/stories/file-tree.stories.jsx +++ b/services/web/frontend/stories/file-tree.stories.jsx @@ -155,9 +155,6 @@ export default { console.log('started free trial') }, refProviders: {}, - reindexReferences: () => { - console.log('reindex references') - }, setRefProviderEnabled: provider => { console.log(`ref provider ${provider} enabled`) }, diff --git a/services/web/frontend/stories/modals/create-file/create-file-modal-decorator.jsx b/services/web/frontend/stories/modals/create-file/create-file-modal-decorator.jsx index bb1ea8dc7d..a70a507436 100644 --- a/services/web/frontend/stories/modals/create-file/create-file-modal-decorator.jsx +++ b/services/web/frontend/stories/modals/create-file/create-file-modal-decorator.jsx @@ -7,9 +7,6 @@ import PropTypes from 'prop-types' const defaultFileTreeContextProps = { refProviders: { mendeley: false, zotero: false }, - reindexReferences: () => { - console.log('reindex references') - }, setRefProviderEnabled: provider => { console.log(`ref provider ${provider} enabled`) }, 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 fa95db490d..6021f60083 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 @@ -30,7 +30,6 @@ describe('', function () { > ', function () { > ', function () { > ', function () { > ', function () { > ', function () { > ', function () { > @@ -373,11 +372,27 @@ describe('autocomplete', { scrollBehavior: false }, function () { ] const scope = mockScope() - scope.$root._references.keys = ['ref-1', 'ref-2', 'ref-3'] + + const ReferencesProvider: FC = ({ children }) => { + return ( + + {children} + + ) + } cy.mount( - + @@ -428,7 +443,6 @@ describe('autocomplete', { scrollBehavior: false }, function () { ] const scope = mockScope() - scope.$root._references.keys = ['foo'] scope.project.rootFolder = rootFolder cy.mount( @@ -893,7 +907,6 @@ describe('autocomplete', { scrollBehavior: false }, function () { ] const scope = mockScope() - scope.$root._references.keys = ['foo'] scope.project.rootFolder = rootFolder cy.mount( diff --git a/services/web/test/frontend/features/source-editor/helpers/mock-scope.ts b/services/web/test/frontend/features/source-editor/helpers/mock-scope.ts index 0e91ad952c..0e2b5462ea 100644 --- a/services/web/test/frontend/features/source-editor/helpers/mock-scope.ts +++ b/services/web/test/frontend/features/source-editor/helpers/mock-scope.ts @@ -80,10 +80,5 @@ export const mockScope = (content?: string) => { $on: cy.stub().log(false), $broadcast: cy.stub().log(false), $emit: cy.stub().log(false), - $root: { - _references: { - keys: ['foo'], - }, - }, } }