mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-11-14 06:03:45 +01:00
Move project context out of scope value store (#26615)
* Refactor project context to not use scope store * Fix Cypress tests for project context changes * Fix frontend React Testing Library tests for project context changes * Remove redundant code * Fix some project types in tests * Remove unused import and fix a type * Throw an error if updating the project in the project context before joining the project * Fix some review panel tests * Remove unused imports GitOrigin-RevId: 2f0c928b651f387aa980c29aef7d1ba0649790a7
This commit is contained in:
@@ -39,7 +39,9 @@ const FileTreeOpenContext = createContext<
|
||||
export const FileTreeOpenProvider: FC<React.PropsWithChildren> = ({
|
||||
children,
|
||||
}) => {
|
||||
const { rootDocId, owner } = useProjectContext()
|
||||
const { project } = useProjectContext()
|
||||
const rootDocId = project?.rootDocId
|
||||
const projectOwner = project?.owner?._id
|
||||
const { eventEmitter, projectJoined } = useIdeReactContext()
|
||||
const { openDocWithId, openInitialDoc } = useEditorManagerContext()
|
||||
const { currentDocumentId } = useEditorOpenDocContext()
|
||||
@@ -81,7 +83,7 @@ export const FileTreeOpenProvider: FC<React.PropsWithChildren> = ({
|
||||
openDocWithId(selected.entity._id, { keepCurrentView: true })
|
||||
if (selected.entity.name.endsWith('.bib')) {
|
||||
sendMB('open-bib-file', {
|
||||
projectOwner: owner._id,
|
||||
projectOwner,
|
||||
isSampleFile: selected.entity.name === 'sample.bib',
|
||||
linkedFileProvider: null,
|
||||
})
|
||||
@@ -96,7 +98,7 @@ export const FileTreeOpenProvider: FC<React.PropsWithChildren> = ({
|
||||
if (openFile) {
|
||||
if (selected?.entity?.name?.endsWith('.bib')) {
|
||||
sendMB('open-bib-file', {
|
||||
projectOwner: owner._id,
|
||||
projectOwner,
|
||||
isSampleFile: false,
|
||||
linkedFileProvider: (selected.entity as FileRef).linkedFileData
|
||||
?.provider,
|
||||
@@ -105,7 +107,7 @@ export const FileTreeOpenProvider: FC<React.PropsWithChildren> = ({
|
||||
window.dispatchEvent(new CustomEvent('file-view:file-opened'))
|
||||
}
|
||||
},
|
||||
[fileTreeReady, setOpenFile, openDocWithId, owner]
|
||||
[fileTreeReady, setOpenFile, openDocWithId, projectOwner]
|
||||
)
|
||||
|
||||
const handleFileTreeDelete = useCallback(
|
||||
|
||||
@@ -19,6 +19,8 @@ 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'
|
||||
import { ProjectMetadata } from '@/shared/context/types/project-metadata'
|
||||
|
||||
const LOADED_AT = new Date()
|
||||
|
||||
@@ -44,10 +46,6 @@ function populateIdeReactScope(store: ReactScopeValueStore) {
|
||||
store.set('settings', {})
|
||||
}
|
||||
|
||||
function populateProjectScope(store: ReactScopeValueStore) {
|
||||
store.allowNonExistentPath('project', true)
|
||||
}
|
||||
|
||||
function populatePdfScope(store: ReactScopeValueStore) {
|
||||
store.allowNonExistentPath('pdf', true)
|
||||
}
|
||||
@@ -61,7 +59,6 @@ export function createReactScopeValueStore() {
|
||||
// initialization code together with the context and would only populate
|
||||
// necessary values in the store, but this is simpler for now
|
||||
populateIdeReactScope(scopeStore)
|
||||
populateProjectScope(scopeStore)
|
||||
populatePdfScope(scopeStore)
|
||||
|
||||
scopeStore.allowNonExistentPath('hasLintingError')
|
||||
@@ -94,6 +91,8 @@ export const IdeReactProvider: FC<React.PropsWithChildren> = ({ children }) => {
|
||||
const [projectJoined, setProjectJoined] = useState(false)
|
||||
|
||||
const { socket, getSocketDebuggingInfo } = useConnectionContext()
|
||||
const { joinProject, project } = useProjectContext()
|
||||
const spellCheckLanguage = project?.spellCheckLanguage
|
||||
|
||||
const reportError = useCallback(
|
||||
(error: any, meta?: Record<string, any>) => {
|
||||
@@ -105,7 +104,7 @@ export const IdeReactProvider: FC<React.PropsWithChildren> = ({ children }) => {
|
||||
performance_now: performance.now(),
|
||||
release,
|
||||
client_load: LOADED_AT,
|
||||
spellCheckLanguage: scopeStore.get('project.spellCheckLanguage'),
|
||||
spellCheckLanguage,
|
||||
...getSocketDebuggingInfo(),
|
||||
}
|
||||
|
||||
@@ -124,17 +123,28 @@ export const IdeReactProvider: FC<React.PropsWithChildren> = ({ children }) => {
|
||||
},
|
||||
})
|
||||
},
|
||||
[release, projectId, getSocketDebuggingInfo, scopeStore]
|
||||
[release, projectId, getSocketDebuggingInfo, spellCheckLanguage]
|
||||
)
|
||||
|
||||
// Populate scope values when joining project, then fire project:joined event
|
||||
useEffect(() => {
|
||||
function handleJoinProjectResponse({
|
||||
project: { rootDoc_id: rootDocId, ..._project },
|
||||
project: {
|
||||
rootDoc_id: rootDocId,
|
||||
publicAccesLevel: publicAccessLevel,
|
||||
..._project
|
||||
},
|
||||
permissionsLevel,
|
||||
}: JoinProjectPayload) {
|
||||
const project = { ..._project, rootDocId }
|
||||
scopeStore.set('project', project)
|
||||
const project = { ..._project, rootDocId, publicAccessLevel }
|
||||
|
||||
// Cast the project from the payload as ProjectMetadata to ensure it has
|
||||
// the correct type for the context. It must be close enough because the
|
||||
// data structure hasn't changed and it worked previously. This type
|
||||
// coercion was previously sidestepped by adding the project to the scope
|
||||
// store, which does not enforce types.
|
||||
joinProject(project as unknown as ProjectMetadata)
|
||||
|
||||
setPermissionsLevel(permissionsLevel)
|
||||
// Make watchers update immediately
|
||||
scopeStore.flushUpdates()
|
||||
@@ -142,21 +152,12 @@ export const IdeReactProvider: FC<React.PropsWithChildren> = ({ children }) => {
|
||||
setProjectJoined(true)
|
||||
}
|
||||
|
||||
function handleMainBibliographyDocUpdated(payload: string) {
|
||||
scopeStore.set('project.mainBibliographyDoc_id', payload)
|
||||
}
|
||||
|
||||
socket.on('joinProjectResponse', handleJoinProjectResponse)
|
||||
socket.on('mainBibliographyDocUpdated', handleMainBibliographyDocUpdated)
|
||||
|
||||
return () => {
|
||||
socket.removeListener('joinProjectResponse', handleJoinProjectResponse)
|
||||
socket.removeListener(
|
||||
'mainBibliographyDocUpdated',
|
||||
handleMainBibliographyDocUpdated
|
||||
)
|
||||
}
|
||||
}, [socket, eventEmitter, scopeStore])
|
||||
}, [socket, eventEmitter, scopeStore, joinProject])
|
||||
|
||||
const ide = useMemo(() => {
|
||||
return {
|
||||
|
||||
@@ -124,7 +124,7 @@ export const OutlineProvider: FC<React.PropsWithChildren> = ({ children }) => {
|
||||
[openDocName]
|
||||
)
|
||||
|
||||
const { _id: projectId } = useProjectContext()
|
||||
const { projectId } = useProjectContext()
|
||||
const storageKey = `file_outline.expanded.${projectId}`
|
||||
|
||||
const [outlineExpanded, setOutlineExpanded] = useState(
|
||||
|
||||
@@ -98,7 +98,7 @@ export const PermissionsProvider: React.FC<React.PropsWithChildren> = ({
|
||||
const { permissionsLevel } = useIdeReactContext()
|
||||
const hasViewerPermissions = useViewerPermissions()
|
||||
const anonymous = getMeta('ol-anonymous')
|
||||
const project = useProjectContext()
|
||||
const { features } = useProjectContext()
|
||||
|
||||
useEffect(() => {
|
||||
let activePermissionsMap
|
||||
@@ -106,7 +106,7 @@ export const PermissionsProvider: React.FC<React.PropsWithChildren> = ({
|
||||
activePermissionsMap = linkSharingWarningPermissionsMap
|
||||
} else if (anonymous) {
|
||||
activePermissionsMap = anonymousPermissionsMap
|
||||
} else if (!project.features.trackChanges) {
|
||||
} else if (!features.trackChanges) {
|
||||
activePermissionsMap = noTrackChangesPermissionsMap
|
||||
} else {
|
||||
activePermissionsMap = permissionsMap
|
||||
@@ -117,7 +117,7 @@ export const PermissionsProvider: React.FC<React.PropsWithChildren> = ({
|
||||
permissionsLevel,
|
||||
setPermissions,
|
||||
hasViewerPermissions,
|
||||
project.features.trackChanges,
|
||||
features.trackChanges,
|
||||
])
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -75,10 +75,10 @@ export const ReactContextRoot: FC<
|
||||
<Providers.SplitTestProvider>
|
||||
<Providers.ModalsContextProvider>
|
||||
<Providers.ConnectionProvider>
|
||||
<Providers.IdeReactProvider>
|
||||
<Providers.UserProvider>
|
||||
<Providers.UserSettingsProvider>
|
||||
<Providers.ProjectProvider>
|
||||
<Providers.ProjectProvider>
|
||||
<Providers.IdeReactProvider>
|
||||
<Providers.UserProvider>
|
||||
<Providers.UserSettingsProvider>
|
||||
<Providers.SnapshotProvider>
|
||||
<Providers.DetachProvider>
|
||||
<Providers.EditorPropertiesProvider>
|
||||
@@ -128,10 +128,10 @@ export const ReactContextRoot: FC<
|
||||
</Providers.EditorPropertiesProvider>
|
||||
</Providers.DetachProvider>
|
||||
</Providers.SnapshotProvider>
|
||||
</Providers.ProjectProvider>
|
||||
</Providers.UserSettingsProvider>
|
||||
</Providers.UserProvider>
|
||||
</Providers.IdeReactProvider>
|
||||
</Providers.UserSettingsProvider>
|
||||
</Providers.UserProvider>
|
||||
</Providers.IdeReactProvider>
|
||||
</Providers.ProjectProvider>
|
||||
</Providers.ConnectionProvider>
|
||||
</Providers.ModalsContextProvider>
|
||||
</Providers.SplitTestProvider>
|
||||
|
||||
@@ -55,7 +55,7 @@ export const SnapshotContext = createContext<
|
||||
>(undefined)
|
||||
|
||||
export const SnapshotProvider: FC<React.PropsWithChildren> = ({ children }) => {
|
||||
const { _id: projectId } = useProjectContext()
|
||||
const { projectId } = useProjectContext()
|
||||
const [snapshotLoadingState, setSnapshotLoadingState] =
|
||||
useState<SnapshotLoadingState>('')
|
||||
const [snapshotUpdater] = useState(() => new SnapshotUpdater(projectId))
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
listProjectInvites,
|
||||
listProjectMembers,
|
||||
} from '@/features/share-project-modal/utils/api'
|
||||
import useScopeValue from '@/shared/hooks/use-scope-value'
|
||||
import { useProjectContext } from '@/shared/context/project-context'
|
||||
import { useConnectionContext } from '@/features/ide-react/context/connection-context'
|
||||
import { useIdeReactContext } from '@/features/ide-react/context/ide-react-context'
|
||||
import { useModalsContext } from '@/features/ide-react/context/modals-context'
|
||||
@@ -16,11 +16,9 @@ import { useLocation } from '@/shared/hooks/use-location'
|
||||
function useSocketListeners() {
|
||||
const { t } = useTranslation()
|
||||
const { socket } = useConnectionContext()
|
||||
const { permissionsLevel, projectId } = useIdeReactContext()
|
||||
const { showGenericMessageModal } = useModalsContext()
|
||||
const [, setPublicAccessLevel] = useScopeValue('project.publicAccesLevel')
|
||||
const [, setProjectMembers] = useScopeValue('project.members')
|
||||
const [, setProjectInvites] = useScopeValue('project.invites')
|
||||
const { permissionsLevel } = useIdeReactContext()
|
||||
const { projectId, updateProject } = useProjectContext()
|
||||
const location = useLocation()
|
||||
|
||||
useSocketListener(
|
||||
@@ -51,10 +49,10 @@ function useSocketListeners() {
|
||||
useCallback(
|
||||
(data: { newAccessLevel?: PublicAccessLevel }) => {
|
||||
if (data.newAccessLevel) {
|
||||
setPublicAccessLevel(data.newAccessLevel)
|
||||
updateProject({ publicAccessLevel: data.newAccessLevel })
|
||||
}
|
||||
},
|
||||
[setPublicAccessLevel]
|
||||
[updateProject]
|
||||
)
|
||||
)
|
||||
|
||||
@@ -65,13 +63,13 @@ function useSocketListeners() {
|
||||
listProjectMembers(projectId)
|
||||
.then(({ members }) => {
|
||||
if (members) {
|
||||
setProjectMembers(members)
|
||||
updateProject({ members })
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
debugConsole.error('Error fetching members for project', err)
|
||||
})
|
||||
}, [projectId, setProjectMembers])
|
||||
}, [projectId, updateProject])
|
||||
)
|
||||
|
||||
useSocketListener(
|
||||
@@ -83,7 +81,7 @@ function useSocketListeners() {
|
||||
listProjectMembers(projectId)
|
||||
.then(({ members }) => {
|
||||
if (members) {
|
||||
setProjectMembers(members)
|
||||
updateProject({ members })
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
@@ -95,7 +93,7 @@ function useSocketListeners() {
|
||||
listProjectInvites(projectId)
|
||||
.then(({ invites }) => {
|
||||
if (invites) {
|
||||
setProjectInvites(invites)
|
||||
updateProject({ invites })
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
@@ -103,7 +101,29 @@ function useSocketListeners() {
|
||||
})
|
||||
}
|
||||
},
|
||||
[projectId, setProjectInvites, setProjectMembers, permissionsLevel]
|
||||
[projectId, updateProject, permissionsLevel]
|
||||
)
|
||||
)
|
||||
|
||||
useSocketListener(
|
||||
socket,
|
||||
'mainBibliographyDocUpdated',
|
||||
useCallback(
|
||||
(payload: string) => {
|
||||
updateProject({ mainBibliographyDocId: payload })
|
||||
},
|
||||
[updateProject]
|
||||
)
|
||||
)
|
||||
|
||||
useSocketListener(
|
||||
socket,
|
||||
'projectNameUpdated',
|
||||
useCallback(
|
||||
(payload: string) => {
|
||||
updateProject({ name: payload })
|
||||
},
|
||||
[updateProject]
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user