From 779e346cb181f1968d034a0ae0836f5278c46a6b Mon Sep 17 00:00:00 2001 From: Antoine Clausse Date: Fri, 4 Jul 2025 12:31:58 +0200 Subject: [PATCH] [web] Move scopes `permissionsLevel`, `permissions` and `hasLintingError` to React context (#26607) * Move `hasLintingError` from scope to react state * Move `permissionsLevel` to IdeReactContext states * Get `permissionsLevel` from `useIdeReactContext()` * Set `permissionsLevel` in mocked `IdeReactProvider` * Replace `permissions` scope by React state * Fixup `permission` changes * Remove redundant type GitOrigin-RevId: 6203c61f9ac429789624196bf67e508310f4577f --- .../settings-spell-check-language.tsx | 4 +-- .../hooks/use-root-doc-id.tsx | 4 +-- .../editor-navigation-toolbar-root.tsx | 11 ++++--- .../components/unsaved-docs/unsaved-docs.tsx | 4 +-- .../context/editor-manager-context.tsx | 5 ++-- .../ide-react/context/ide-react-context.tsx | 29 +++++++++++++------ .../ide-react/context/metadata-context.tsx | 4 +-- .../ide-react/context/permissions-context.tsx | 22 +++++++++----- .../ide-react/hooks/use-socket-listeners.ts | 4 +-- .../components/toolbar/project-title.tsx | 4 ++- .../components/toolbar/toolbar.tsx | 4 ++- .../components/review-mode-switcher.tsx | 5 ++-- .../components/editor-over-limit-modal.tsx | 4 ++- .../components/view-only-access-modal.tsx | 5 ++-- .../hooks/use-codemirror-scope.ts | 3 +- .../js/shared/context/editor-context.tsx | 15 +--------- .../shared/context/file-tree-data-context.tsx | 5 +++- .../shared/context/local-compile-context.tsx | 5 ++-- .../js/shared/hooks/use-viewer-permissions.ts | 4 +-- .../web/frontend/stories/decorators/scope.tsx | 4 ++- .../components/editor-left-menu/scope.tsx | 2 -- .../pdf-preview/pdf-preview.spec.tsx | 19 ++++++++---- .../frontend/components/pdf-preview/scope.tsx | 1 - .../frontend/helpers/editor-providers.jsx | 1 + 24 files changed, 93 insertions(+), 75 deletions(-) diff --git a/services/web/frontend/js/features/editor-left-menu/components/settings/settings-spell-check-language.tsx b/services/web/frontend/js/features/editor-left-menu/components/settings/settings-spell-check-language.tsx index ecd0ced624..5529ecd871 100644 --- a/services/web/frontend/js/features/editor-left-menu/components/settings/settings-spell-check-language.tsx +++ b/services/web/frontend/js/features/editor-left-menu/components/settings/settings-spell-check-language.tsx @@ -4,7 +4,7 @@ import getMeta from '../../../../utils/meta' import { useProjectSettingsContext } from '../../context/project-settings-context' import SettingsMenuSelect from './settings-menu-select' import type { Optgroup } from './settings-menu-select' -import { useEditorContext } from '@/shared/context/editor-context' +import { useIdeReactContext } from '@/features/ide-react/context/ide-react-context' import { supportsWebAssembly } from '@/utils/wasm' export default function SettingsSpellCheckLanguage() { @@ -12,7 +12,7 @@ export default function SettingsSpellCheckLanguage() { const { spellCheckLanguage, setSpellCheckLanguage } = useProjectSettingsContext() - const { permissionsLevel } = useEditorContext() + const { permissionsLevel } = useIdeReactContext() const optgroup: Optgroup = useMemo(() => { const options = (getMeta('ol-languages') ?? []) diff --git a/services/web/frontend/js/features/editor-left-menu/hooks/use-root-doc-id.tsx b/services/web/frontend/js/features/editor-left-menu/hooks/use-root-doc-id.tsx index 8a704f87ac..2d62431b77 100644 --- a/services/web/frontend/js/features/editor-left-menu/hooks/use-root-doc-id.tsx +++ b/services/web/frontend/js/features/editor-left-menu/hooks/use-root-doc-id.tsx @@ -1,5 +1,5 @@ import { useCallback } from 'react' -import { useEditorContext } from '../../../shared/context/editor-context' +import { useIdeReactContext } from '@/features/ide-react/context/ide-react-context' import useScopeValue from '../../../shared/hooks/use-scope-value' import type { ProjectSettings } from '../utils/api' import useSaveProjectSettings from './use-save-project-settings' @@ -7,7 +7,7 @@ import useSaveProjectSettings from './use-save-project-settings' export default function useRootDocId() { const [rootDocId] = useScopeValue('project.rootDocId') - const { permissionsLevel } = useEditorContext() + const { permissionsLevel } = useIdeReactContext() const saveProjectSettings = useSaveProjectSettings() const setRootDocIdFunc = useCallback( diff --git a/services/web/frontend/js/features/editor-navigation-toolbar/components/editor-navigation-toolbar-root.tsx b/services/web/frontend/js/features/editor-navigation-toolbar/components/editor-navigation-toolbar-root.tsx index 820f21c3a4..cb4ac6f194 100644 --- a/services/web/frontend/js/features/editor-navigation-toolbar/components/editor-navigation-toolbar-root.tsx +++ b/services/web/frontend/js/features/editor-navigation-toolbar/components/editor-navigation-toolbar-root.tsx @@ -1,6 +1,7 @@ import React, { useState, useCallback } from 'react' import ToolbarHeader from './toolbar-header' import { useEditorContext } from '../../../shared/context/editor-context' +import { useIdeReactContext } from '@/features/ide-react/context/ide-react-context' import { useChatContext } from '../../chat/context/chat-context' import { useLayoutContext } from '../../../shared/context/layout-context' import { useProjectContext } from '../../../shared/context/project-context' @@ -27,12 +28,10 @@ const EditorNavigationToolbarRoot = React.memo( features: { trackChangesVisible }, } = useProjectContext() - const { - cobranding, - isRestrictedTokenMember, - renameProject, - permissionsLevel, - } = useEditorContext() + const { cobranding, isRestrictedTokenMember, renameProject } = + useEditorContext() + + const { permissionsLevel } = useIdeReactContext() const { chatIsOpen, diff --git a/services/web/frontend/js/features/ide-react/components/unsaved-docs/unsaved-docs.tsx b/services/web/frontend/js/features/ide-react/components/unsaved-docs/unsaved-docs.tsx index 8224bb6e62..7f19737c90 100644 --- a/services/web/frontend/js/features/ide-react/components/unsaved-docs/unsaved-docs.tsx +++ b/services/web/frontend/js/features/ide-react/components/unsaved-docs/unsaved-docs.tsx @@ -1,5 +1,5 @@ import { useEditorManagerContext } from '@/features/ide-react/context/editor-manager-context' -import { useEditorContext } from '@/shared/context/editor-context' +import { useIdeReactContext } from '@/features/ide-react/context/ide-react-context' import { FC, useCallback, useEffect, useRef, useState } from 'react' import { PermissionsLevel } from '@/features/ide-react/types/permissions' import { UnsavedDocsLockedAlert } from '@/features/ide-react/components/unsaved-docs/unsaved-docs-locked-alert' @@ -12,7 +12,7 @@ const MAX_UNSAVED_SECONDS = 30 // lock the editor after this time if unsaved export const UnsavedDocs: FC = () => { const { openDocs, debugTimers } = useEditorManagerContext() - const { permissionsLevel, setPermissionsLevel } = useEditorContext() + const { permissionsLevel, setPermissionsLevel } = useIdeReactContext() const [isLocked, setIsLocked] = useState(false) const [unsavedDocs, setUnsavedDocs] = useState(new Map()) const globalAlertsContainer = useGlobalAlertsContainer() diff --git a/services/web/frontend/js/features/ide-react/context/editor-manager-context.tsx b/services/web/frontend/js/features/ide-react/context/editor-manager-context.tsx index dd06a6af86..71e827168e 100644 --- a/services/web/frontend/js/features/ide-react/context/editor-manager-context.tsx +++ b/services/web/frontend/js/features/ide-react/context/editor-manager-context.tsx @@ -35,7 +35,6 @@ import { EditorType } from '@/features/ide-react/editor/types/editor-type' 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 { convertFileRefToBinaryFile } from '@/features/ide-react/util/file-view' import { useEditorOpenDocContext } from '@/features/ide-react/context/editor-open-doc-context' @@ -89,8 +88,8 @@ export const EditorManagerProvider: FC = ({ }) => { const { t } = useTranslation() const { scopeStore } = useIdeContext() - const { reportError, eventEmitter, projectId } = useIdeReactContext() - const { setOutOfSync } = useEditorContext() + const { reportError, eventEmitter, projectId, setOutOfSync } = + useIdeReactContext() const { socket, closeConnection, connectionState } = useConnectionContext() const { view, setView, setOpenFile } = useLayoutContext() const { showGenericMessageModal, genericModalVisible, showOutOfSyncModal } = diff --git a/services/web/frontend/js/features/ide-react/context/ide-react-context.tsx b/services/web/frontend/js/features/ide-react/context/ide-react-context.tsx index 33413a52e7..27095adf00 100644 --- a/services/web/frontend/js/features/ide-react/context/ide-react-context.tsx +++ b/services/web/frontend/js/features/ide-react/context/ide-react-context.tsx @@ -19,6 +19,7 @@ import { populateEditorScope } from '@/features/ide-react/scope-adapters/editor- 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' const LOADED_AT = new Date() @@ -31,6 +32,9 @@ type IdeReactContextValue = { > reportError: (error: any, meta?: Record) => void projectJoined: boolean + permissionsLevel: PermissionsLevel + setPermissionsLevel: (permissionsLevel: PermissionsLevel) => void + setOutOfSync: (value: boolean) => void } export const IdeReactContext = createContext( @@ -43,13 +47,6 @@ function populateIdeReactScope(store: ReactScopeValueStore) { function populateProjectScope(store: ReactScopeValueStore) { store.allowNonExistentPath('project', true) - store.set('permissionsLevel', 'readOnly') - store.set('permissions', { - read: true, - write: false, - admin: false, - comment: true, - }) } function populatePdfScope(store: ReactScopeValueStore) { @@ -78,6 +75,9 @@ export const IdeReactProvider: FC = ({ children }) => { const projectId = getMeta('ol-project_id') const [scopeStore] = useState(() => createReactScopeValueStore(projectId)) const [eventEmitter] = useState(createIdeEventEmitter) + const [permissionsLevel, setPermissionsLevel] = + useState('readOnly') + const [outOfSync, setOutOfSync] = useState(false) const [scopeEventEmitter] = useState( () => new ReactScopeEventEmitter(eventEmitter) ) @@ -137,7 +137,7 @@ export const IdeReactProvider: FC = ({ children }) => { }: JoinProjectPayload) { const project = { ..._project, rootDocId } scopeStore.set('project', project) - scopeStore.set('permissionsLevel', permissionsLevel) + setPermissionsLevel(permissionsLevel) // Make watchers update immediately scopeStore.flushUpdates() eventEmitter.emit('project:joined', { project, permissionsLevel }) @@ -173,11 +173,22 @@ export const IdeReactProvider: FC = ({ children }) => { eventEmitter, startedFreeTrial, setStartedFreeTrial, + permissionsLevel: outOfSync ? 'readOnly' : permissionsLevel, + setPermissionsLevel, + setOutOfSync, projectId, reportError, projectJoined, }), - [eventEmitter, projectId, projectJoined, reportError, startedFreeTrial] + [ + eventEmitter, + outOfSync, + permissionsLevel, + projectId, + projectJoined, + reportError, + startedFreeTrial, + ] ) return ( diff --git a/services/web/frontend/js/features/ide-react/context/metadata-context.tsx b/services/web/frontend/js/features/ide-react/context/metadata-context.tsx index 7fa6332a16..f8aeec6ca9 100644 --- a/services/web/frontend/js/features/ide-react/context/metadata-context.tsx +++ b/services/web/frontend/js/features/ide-react/context/metadata-context.tsx @@ -13,7 +13,6 @@ import { useConnectionContext } from '@/features/ide-react/context/connection-co import { useEditorOpenDocContext } from '@/features/ide-react/context/editor-open-doc-context' import { getJSON, postJSON } from '@/infrastructure/fetch-json' import { useOnlineUsersContext } from '@/features/ide-react/context/online-users-context' -import { useEditorContext } from '@/shared/context/editor-context' import useSocketListener from '@/features/ide-react/hooks/use-socket-listener' import useEventListener from '@/shared/hooks/use-event-listener' import { useModalsContext } from '@/features/ide-react/context/modals-context' @@ -49,10 +48,9 @@ export const MetadataContext = createContext< export const MetadataProvider: FC = ({ children }) => { const { t } = useTranslation() - const { eventEmitter, projectId } = useIdeReactContext() + const { eventEmitter, permissionsLevel, projectId } = useIdeReactContext() const { socket } = useConnectionContext() const { onlineUsersCount } = useOnlineUsersContext() - const { permissionsLevel } = useEditorContext() const permissions = usePermissionsContext() const { currentDocument } = useEditorOpenDocContext() const { showGenericMessageModal } = useModalsContext() diff --git a/services/web/frontend/js/features/ide-react/context/permissions-context.tsx b/services/web/frontend/js/features/ide-react/context/permissions-context.tsx index 1e10a2cd11..bdb101d3f5 100644 --- a/services/web/frontend/js/features/ide-react/context/permissions-context.tsx +++ b/services/web/frontend/js/features/ide-react/context/permissions-context.tsx @@ -1,12 +1,11 @@ -import { createContext, useContext, useEffect } from 'react' +import { createContext, useContext, useEffect, useState } from 'react' import { useConnectionContext } from '@/features/ide-react/context/connection-context' -import { useEditorContext } from '@/shared/context/editor-context' +import { useIdeReactContext } from '@/features/ide-react/context/ide-react-context' import getMeta from '@/utils/meta' import { Permissions, PermissionsLevel, } from '@/features/ide-react/types/permissions' -import useScopeValue from '@/shared/hooks/use-scope-value' import { DeepReadonly } from '../../../../../types/utils' import useViewerPermissions from '@/shared/hooks/use-viewer-permissions' import { useProjectContext } from '@/shared/context/project-context' @@ -79,15 +78,24 @@ const noTrackChangesPermissionsMap: typeof permissionsMap = { owner: permissionsMap.owner, } +const defaultPermissions: Permissions = { + read: true, + write: true, + admin: false, + comment: true, + resolveOwnComments: false, + resolveAllComments: false, + trackedWrite: true, + labelVersion: false, +} + export const PermissionsProvider: React.FC = ({ children, }) => { const [permissions, setPermissions] = - useScopeValue>('permissions') + useState(defaultPermissions) const { connectionState } = useConnectionContext() - const { permissionsLevel } = useEditorContext() as { - permissionsLevel: PermissionsLevel - } + const { permissionsLevel } = useIdeReactContext() const hasViewerPermissions = useViewerPermissions() const anonymous = getMeta('ol-anonymous') const project = useProjectContext() diff --git a/services/web/frontend/js/features/ide-react/hooks/use-socket-listeners.ts b/services/web/frontend/js/features/ide-react/hooks/use-socket-listeners.ts index 6b2f305593..388fa0299b 100644 --- a/services/web/frontend/js/features/ide-react/hooks/use-socket-listeners.ts +++ b/services/web/frontend/js/features/ide-react/hooks/use-socket-listeners.ts @@ -12,14 +12,12 @@ import { debugConsole } from '@/utils/debugging' import { useCallback } from 'react' import { PublicAccessLevel } from '../../../../../types/public-access-level' import { useLocation } from '@/shared/hooks/use-location' -import { useEditorContext } from '@/shared/context/editor-context' function useSocketListeners() { const { t } = useTranslation() const { socket } = useConnectionContext() - const { projectId } = useIdeReactContext() + const { permissionsLevel, projectId } = useIdeReactContext() const { showGenericMessageModal } = useModalsContext() - const { permissionsLevel } = useEditorContext() const [, setPublicAccessLevel] = useScopeValue('project.publicAccesLevel') const [, setProjectMembers] = useScopeValue('project.members') const [, setProjectInvites] = useScopeValue('project.invites') diff --git a/services/web/frontend/js/features/ide-redesign/components/toolbar/project-title.tsx b/services/web/frontend/js/features/ide-redesign/components/toolbar/project-title.tsx index bf3320d8b9..b975909f6e 100644 --- a/services/web/frontend/js/features/ide-redesign/components/toolbar/project-title.tsx +++ b/services/web/frontend/js/features/ide-redesign/components/toolbar/project-title.tsx @@ -9,6 +9,7 @@ import { useProjectContext } from '@/shared/context/project-context' import { useTranslation } from 'react-i18next' import importOverleafModules from '../../../../../macros/import-overleaf-module.macro' import { useEditorContext } from '@/shared/context/editor-context' +import { useIdeReactContext } from '@/features/ide-react/context/ide-react-context' import { DownloadProjectPDF, DownloadProjectZip } from './download-project' import { useCallback, useState } from 'react' import OLDropdownMenuItem from '@/features/ui/components/ol/ol-dropdown-menu-item' @@ -21,7 +22,8 @@ const SubmitProjectButton = publishModalModules?.import.NewPublishDropdownButton export const ToolbarProjectTitle = () => { const { cobranding } = useEditorContext() const { t } = useTranslation() - const { permissionsLevel, renameProject } = useEditorContext() + const { renameProject } = useEditorContext() + const { permissionsLevel } = useIdeReactContext() const { name } = useProjectContext() const shouldDisplaySubmitButton = (permissionsLevel === 'owner' || permissionsLevel === 'readAndWrite') && diff --git a/services/web/frontend/js/features/ide-redesign/components/toolbar/toolbar.tsx b/services/web/frontend/js/features/ide-redesign/components/toolbar/toolbar.tsx index 2af70eb9ce..6da6e2cef9 100644 --- a/services/web/frontend/js/features/ide-redesign/components/toolbar/toolbar.tsx +++ b/services/web/frontend/js/features/ide-redesign/components/toolbar/toolbar.tsx @@ -14,13 +14,15 @@ import { useEditorContext } from '@/shared/context/editor-context' import importOverleafModules from '../../../../../macros/import-overleaf-module.macro' import UpgradeButton from './upgrade-button' import getMeta from '@/utils/meta' +import { useIdeReactContext } from '@/features/ide-react/context/ide-react-context' const [publishModalModules] = importOverleafModules('publishModal') const SubmitProjectButton = publishModalModules?.import.NewPublishToolbarButton export const Toolbar = () => { const { view, setView } = useLayoutContext() - const { cobranding, permissionsLevel } = useEditorContext() + const { cobranding } = useEditorContext() + const { permissionsLevel } = useIdeReactContext() const shouldDisplaySubmitButton = (permissionsLevel === 'owner' || permissionsLevel === 'readAndWrite') && SubmitProjectButton diff --git a/services/web/frontend/js/features/review-panel-new/components/review-mode-switcher.tsx b/services/web/frontend/js/features/review-panel-new/components/review-mode-switcher.tsx index 2405c2ed60..74c865632c 100644 --- a/services/web/frontend/js/features/review-panel-new/components/review-mode-switcher.tsx +++ b/services/web/frontend/js/features/review-panel-new/components/review-mode-switcher.tsx @@ -17,6 +17,7 @@ import { usePermissionsContext } from '@/features/ide-react/context/permissions- import usePersistedState from '@/shared/hooks/use-persisted-state' import { sendMB } from '@/infrastructure/event-tracking' import { useEditorContext } from '@/shared/context/editor-context' +import { useIdeReactContext } from '@/features/ide-react/context/ide-react-context' import { useProjectContext } from '@/shared/context/project-context' import UpgradeTrackChangesModal from './upgrade-track-changes-modal' import { ReviewModePromo } from '@/features/review-panel-new/components/review-mode-promo' @@ -33,7 +34,7 @@ const useCurrentMode = (): Mode => { trackChanges?.onForEveryone || (user?.id && trackChanges?.onForMembers[user.id]) || (!user?.id && trackChanges?.onForGuests) - const { permissionsLevel } = useEditorContext() + const { permissionsLevel } = useIdeReactContext() if (permissionsLevel === 'readOnly') { return 'view' @@ -52,7 +53,7 @@ function ReviewModeSwitcher() { const { saveTrackChangesForCurrentUser, saveTrackChanges } = useTrackChangesStateActionsContext() const mode = useCurrentMode() - const { permissionsLevel } = useEditorContext() + const { permissionsLevel } = useIdeReactContext() const { write, trackedWrite } = usePermissionsContext() const project = useProjectContext() const [showUpgradeModal, setShowUpgradeModal] = useState(false) diff --git a/services/web/frontend/js/features/share-project-modal/components/editor-over-limit-modal.tsx b/services/web/frontend/js/features/share-project-modal/components/editor-over-limit-modal.tsx index 2764a57d8e..8914d8ba3b 100644 --- a/services/web/frontend/js/features/share-project-modal/components/editor-over-limit-modal.tsx +++ b/services/web/frontend/js/features/share-project-modal/components/editor-over-limit-modal.tsx @@ -3,13 +3,15 @@ import EditorOverLimitModalContent from './editor-over-limit-modal-content' import customLocalStorage from '@/infrastructure/local-storage' import { useProjectContext } from '@/shared/context/project-context' import { useEditorContext } from '@/shared/context/editor-context' +import { useIdeReactContext } from '@/features/ide-react/context/ide-react-context' import { sendMB } from '@/infrastructure/event-tracking' import OLModal from '@/features/ui/components/ol/ol-modal' const EditorOverLimitModal = () => { const [show, setShow] = useState(false) - const { isProjectOwner, permissionsLevel } = useEditorContext() + const { isProjectOwner } = useEditorContext() + const { permissionsLevel } = useIdeReactContext() const { members, features, _id: projectId } = useProjectContext() const handleHide = () => { diff --git a/services/web/frontend/js/features/share-project-modal/components/view-only-access-modal.tsx b/services/web/frontend/js/features/share-project-modal/components/view-only-access-modal.tsx index ab2ea023bb..d474e681c1 100644 --- a/services/web/frontend/js/features/share-project-modal/components/view-only-access-modal.tsx +++ b/services/web/frontend/js/features/share-project-modal/components/view-only-access-modal.tsx @@ -3,14 +3,15 @@ import ViewOnlyAccessModalContent from './view-only-access-modal-content' import customLocalStorage from '@/infrastructure/local-storage' import { useProjectContext } from '@/shared/context/project-context' import { useEditorContext } from '@/shared/context/editor-context' +import { useIdeReactContext } from '@/features/ide-react/context/ide-react-context' import { sendMB } from '@/infrastructure/event-tracking' import OLModal from '@/features/ui/components/ol/ol-modal' const ViewOnlyAccessModal = () => { const [show, setShow] = useState(false) - const { isProjectOwner, isPendingEditor, permissionsLevel } = - useEditorContext() + const { isProjectOwner, isPendingEditor } = useEditorContext() + const { permissionsLevel } = useIdeReactContext() const { members, features, _id: projectId } = useProjectContext() const handleHide = () => { 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 6c20a44702..c0d27c354a 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 @@ -58,11 +58,12 @@ import { import { GotoLineOptions } from '@/features/ide-react/types/goto-line-options' import { useOnlineUsersContext } from '@/features/ide-react/context/online-users-context' import { useEditorOpenDocContext } from '@/features/ide-react/context/editor-open-doc-context' +import { usePermissionsContext } from '@/features/ide-react/context/permissions-context' function useCodeMirrorScope(view: EditorView) { const { fileTreeData } = useFileTreeData() - const [permissions] = useScopeValue('permissions') + const permissions: Permissions = usePermissionsContext() // set up scope listeners diff --git a/services/web/frontend/js/shared/context/editor-context.tsx b/services/web/frontend/js/shared/context/editor-context.tsx index 12763aab58..5685951ad2 100644 --- a/services/web/frontend/js/shared/context/editor-context.tsx +++ b/services/web/frontend/js/shared/context/editor-context.tsx @@ -17,7 +17,6 @@ import { useDetachContext } from './detach-context' import getMeta from '../../utils/meta' import { useUserContext } from './user-context' import { saveProjectSettings } from '@/features/editor-left-menu/utils/api' -import { PermissionsLevel } from '@/features/ide-react/types/permissions' import { useModalsContext } from '@/features/ide-react/context/modals-context' import { WritefullAPI } from './types/writefull-instance' import { Cobranding } from '../../../../types/cobranding' @@ -28,19 +27,16 @@ export const EditorContext = createContext< cobranding?: Cobranding hasPremiumCompile?: boolean renameProject: (newName: string) => void - setPermissionsLevel: (permissionsLevel: PermissionsLevel) => void showSymbolPalette?: boolean toggleSymbolPalette?: () => void insertSymbol?: (symbol: SymbolWithCharacter) => void isProjectOwner: boolean isRestrictedTokenMember?: boolean isPendingEditor: boolean - permissionsLevel: PermissionsLevel deactivateTutorial: (tutorial: string) => void inactiveTutorials: string[] currentPopup: string | null setCurrentPopup: Dispatch> - setOutOfSync: (value: boolean) => void hasPremiumSuggestion: boolean setHasPremiumSuggestion: (value: boolean) => void setPremiumSuggestionResetDate: (date: Date) => void @@ -78,9 +74,6 @@ export const EditorProvider: FC = ({ children }) => { }, []) const [projectName, setProjectName] = useScopeValue('project.name') - const [permissionsLevel, setPermissionsLevel] = - useScopeValue('permissionsLevel') - const [outOfSync, setOutOfSync] = useState(false) const [showSymbolPalette] = useScopeValue('editor.showSymbolPalette') const [toggleSymbolPalette] = useScopeValue('editor.toggleSymbolPalette') @@ -187,8 +180,6 @@ export const EditorProvider: FC = ({ children }) => { cobranding, hasPremiumCompile: features?.compileGroup === 'priority', renameProject, - permissionsLevel: outOfSync ? 'readOnly' : permissionsLevel, - setPermissionsLevel, isProjectOwner: owner?._id === userId, isRestrictedTokenMember: getMeta('ol-isRestrictedTokenMember'), isPendingEditor, @@ -199,7 +190,6 @@ export const EditorProvider: FC = ({ children }) => { deactivateTutorial, currentPopup, setCurrentPopup, - setOutOfSync, hasPremiumSuggestion, setHasPremiumSuggestion, premiumSuggestionResetDate, @@ -213,8 +203,6 @@ export const EditorProvider: FC = ({ children }) => { owner, userId, renameProject, - permissionsLevel, - setPermissionsLevel, isPendingEditor, showSymbolPalette, toggleSymbolPalette, @@ -223,8 +211,6 @@ export const EditorProvider: FC = ({ children }) => { deactivateTutorial, currentPopup, setCurrentPopup, - outOfSync, - setOutOfSync, hasPremiumSuggestion, setHasPremiumSuggestion, premiumSuggestionResetDate, @@ -238,6 +224,7 @@ export const EditorProvider: FC = ({ children }) => { {children} ) } + export function useEditorContext() { const context = useContext(EditorContext) diff --git a/services/web/frontend/js/shared/context/file-tree-data-context.tsx b/services/web/frontend/js/shared/context/file-tree-data-context.tsx index 7e426b9e54..889c865e91 100644 --- a/services/web/frontend/js/shared/context/file-tree-data-context.tsx +++ b/services/web/frontend/js/shared/context/file-tree-data-context.tsx @@ -28,6 +28,8 @@ import { useSnapshotContext, } from '@/features/ide-react/context/snapshot-context' import importOverleafModules from '../../../macros/import-overleaf-module.macro' +import { useIdeReactContext } from '@/features/ide-react/context/ide-react-context' + const { buildFileTree, createFolder } = (importOverleafModules('snapshotUtils')[0] ?.import as typeof StubSnapshotUtils) || StubSnapshotUtils @@ -61,6 +63,7 @@ enum ACTION_TYPES { MOVE = 'MOVE', CREATE = 'CREATE', } + /* eslint-enable no-unused-vars */ type Action = @@ -181,7 +184,7 @@ export const FileTreeDataProvider: FC = ({ }) => { const [project] = useScopeValue('project') const { currentDocumentId, setOpenDocName } = useEditorOpenDocContext() - const [permissionsLevel] = useScopeValue('permissionsLevel') + const { permissionsLevel } = useIdeReactContext() const { fileTreeFromHistory, snapshot, snapshotVersion } = useSnapshotContext() const fileTreeReadOnly = diff --git a/services/web/frontend/js/shared/context/local-compile-context.tsx b/services/web/frontend/js/shared/context/local-compile-context.tsx index af1430ac05..ccc2c1453c 100644 --- a/services/web/frontend/js/shared/context/local-compile-context.tsx +++ b/services/web/frontend/js/shared/context/local-compile-context.tsx @@ -10,7 +10,6 @@ import { Dispatch, SetStateAction, } from 'react' -import useScopeValue from '../hooks/use-scope-value' import usePersistedState from '../hooks/use-persisted-state' import useAbortController from '../hooks/use-abort-controller' import DocumentCompiler from '../../features/pdf-preview/util/compiler' @@ -85,7 +84,7 @@ export type CompileContext = { setAutoCompile: (value: boolean) => void setDraft: (value: any) => void setError: (value: any) => void - setHasLintingError: (value: any) => void // only for storybook + setHasLintingError: (value: boolean) => void // only for storybook setHighlights: (value: any) => void setPosition: Dispatch> setShowCompileTimeWarning: (value: any) => void @@ -273,7 +272,7 @@ export const LocalCompileProvider: FC = ({ ) // whether the editor linter found errors - const [hasLintingError, setHasLintingError] = useScopeValue('hasLintingError') + const [hasLintingError, setHasLintingError] = useState(false) // the timestamp that a doc was last changed const [changedAt, setChangedAt] = useState(0) diff --git a/services/web/frontend/js/shared/hooks/use-viewer-permissions.ts b/services/web/frontend/js/shared/hooks/use-viewer-permissions.ts index 5f6e7f1f72..269d094b27 100644 --- a/services/web/frontend/js/shared/hooks/use-viewer-permissions.ts +++ b/services/web/frontend/js/shared/hooks/use-viewer-permissions.ts @@ -1,7 +1,7 @@ -import { useEditorContext } from '../context/editor-context' +import { useIdeReactContext } from '@/features/ide-react/context/ide-react-context' function useViewerPermissions() { - const { permissionsLevel } = useEditorContext() + const { permissionsLevel } = useIdeReactContext() return permissionsLevel === 'readOnly' } diff --git a/services/web/frontend/stories/decorators/scope.tsx b/services/web/frontend/stories/decorators/scope.tsx index c81b9b6103..cc89a31685 100644 --- a/services/web/frontend/stories/decorators/scope.tsx +++ b/services/web/frontend/stories/decorators/scope.tsx @@ -75,7 +75,6 @@ const initialScope = { }, open_doc_name: 'testfile.tex', }, - hasLintingError: false, permissionsLevel: 'owner', } @@ -190,6 +189,9 @@ const IdeReactProvider: FC = ({ children }) => { setStartedFreeTrial, reportError: () => {}, projectJoined: true, + permissionsLevel: 'owner' as const, + setPermissionsLevel: () => {}, + setOutOfSync: () => {}, })) const [ideContextValue] = useState(() => { diff --git a/services/web/test/frontend/components/editor-left-menu/scope.tsx b/services/web/test/frontend/components/editor-left-menu/scope.tsx index 4c58c01f53..8281fe1f72 100644 --- a/services/web/test/frontend/components/editor-left-menu/scope.tsx +++ b/services/web/test/frontend/components/editor-left-menu/scope.tsx @@ -12,7 +12,6 @@ type Scope = { getSnapshot?: () => string } } - hasLintingError?: boolean ui?: { view?: 'editor' | 'history' | 'file' | 'pdf' pdfLayout?: 'flat' | 'sideBySide' | 'split' @@ -46,6 +45,5 @@ export const mockScope = (scope?: Scope) => ({ getSnapshot: () => 'some doc content', }, }, - hasLintingError: false, ...scope, }) diff --git a/services/web/test/frontend/components/pdf-preview/pdf-preview.spec.tsx b/services/web/test/frontend/components/pdf-preview/pdf-preview.spec.tsx index ed0ab2ffc1..47f7e1024d 100644 --- a/services/web/test/frontend/components/pdf-preview/pdf-preview.spec.tsx +++ b/services/web/test/frontend/components/pdf-preview/pdf-preview.spec.tsx @@ -7,7 +7,8 @@ import { IdeView, useLayoutContext, } from '../../../../frontend/js/shared/context/layout-context' -import { FC, useEffect } from 'react' +import { FC, PropsWithChildren, useEffect } from 'react' +import { useLocalCompileContext } from '@/shared/context/local-compile-context' const storeAndFireEvent = (win: typeof window, key: string, value: unknown) => { localStorage.setItem(key, value) @@ -462,14 +463,20 @@ describe('', function () { const scope = mockScope() // enable linting in the editor const userSettings = { syntaxValidation: true } - // mock a linting error - scope.hasLintingError = true + + const WithLintingErrors: FC = ({ children }) => { + const { setHasLintingError } = useLocalCompileContext() + useEffect(() => setHasLintingError(true), [setHasLintingError]) + return children + } cy.mount( -
- -
+ +
+ +
+
) diff --git a/services/web/test/frontend/components/pdf-preview/scope.tsx b/services/web/test/frontend/components/pdf-preview/scope.tsx index 397813faa2..08dbebbd3c 100644 --- a/services/web/test/frontend/components/pdf-preview/scope.tsx +++ b/services/web/test/frontend/components/pdf-preview/scope.tsx @@ -15,5 +15,4 @@ export const mockScope = () => ({ doc: '\\documentclass{article}', }), }, - hasLintingError: false, }) diff --git a/services/web/test/frontend/helpers/editor-providers.jsx b/services/web/test/frontend/helpers/editor-providers.jsx index f2ed404f66..5a0088120a 100644 --- a/services/web/test/frontend/helpers/editor-providers.jsx +++ b/services/web/test/frontend/helpers/editor-providers.jsx @@ -204,6 +204,7 @@ const makeIdeReactProvider = (scope, socket) => { setStartedFreeTrial, reportError: () => {}, projectJoined: true, + permissionsLevel: scope.permissionsLevel, })) const [ideContextValue] = useState(() => {