mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-05-28 19:41:33 +02:00
Disable updating project-wide settings while socket is currently listening to update project-wide settings in a project.
This may happen if the project is being used by multiple people, and we want to avoid race condition on the update since it's possible for multiple people to update setting value at the same time. GitOrigin-RevId: cdad6a6456e2d9e4ef1812ebfd6f6ef59f23747f
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { createContext, useContext, useMemo } from 'react'
|
||||
import { createContext, useContext, useMemo, useState } from 'react'
|
||||
import type { PropsWithChildren } from 'react'
|
||||
import type {
|
||||
FontFamily,
|
||||
@@ -52,6 +52,8 @@ export const ProjectSettingsContext = createContext<
|
||||
export function ProjectSettingsProvider({
|
||||
children,
|
||||
}: PropsWithChildren<Record<string, never>>) {
|
||||
const [ignoreUpdates, setIgnoreUpdates] = useState(false)
|
||||
|
||||
const {
|
||||
compiler,
|
||||
setCompiler,
|
||||
@@ -61,7 +63,7 @@ export function ProjectSettingsProvider({
|
||||
setRootDocId,
|
||||
spellCheckLanguage,
|
||||
setSpellCheckLanguage,
|
||||
} = useSetProjectWideSettings()
|
||||
} = useSetProjectWideSettings({ ignoreUpdates })
|
||||
|
||||
const {
|
||||
autoComplete,
|
||||
@@ -86,7 +88,9 @@ export function ProjectSettingsProvider({
|
||||
setPdfViewer,
|
||||
} = useUserWideSettings()
|
||||
|
||||
useProjectWideSettingsSocketListener()
|
||||
useProjectWideSettingsSocketListener({
|
||||
onListen: () => setIgnoreUpdates(true),
|
||||
})
|
||||
|
||||
const value: ProjectSettingsContextValue = useMemo(
|
||||
() => ({
|
||||
|
||||
@@ -1,21 +1,52 @@
|
||||
import { useEffect } from 'react'
|
||||
import { useCallback, useEffect } from 'react'
|
||||
import { ProjectCompiler } from '../../../../../types/project-settings'
|
||||
import { useIdeContext } from '../../../shared/context/ide-context'
|
||||
import useScopeValue from '../../../shared/hooks/use-scope-value'
|
||||
|
||||
export default function useProjectWideSettingsSocketListener() {
|
||||
type UseProjectWideSettingsSocketListener = {
|
||||
onListen: () => void
|
||||
}
|
||||
|
||||
export default function useProjectWideSettingsSocketListener({
|
||||
onListen,
|
||||
}: UseProjectWideSettingsSocketListener) {
|
||||
const ide = useIdeContext()
|
||||
|
||||
const [compiler, setCompiler] =
|
||||
const [compilerScope, setCompilerScope] =
|
||||
useScopeValue<ProjectCompiler>('project.compiler')
|
||||
const [imageName, setImageName] = useScopeValue<string>('project.imageName')
|
||||
const [spellCheckLanguage, setSpellCheckLanguage] = useScopeValue<string>(
|
||||
'project.spellCheckLanguage'
|
||||
const [imageNameScope, setImageNameScope] =
|
||||
useScopeValue<string>('project.imageName')
|
||||
const [spellCheckLanguageScope, setSpellCheckLanguageScope] =
|
||||
useScopeValue<string>('project.spellCheckLanguage')
|
||||
|
||||
const setCompiler = useCallback(
|
||||
(compiler: ProjectCompiler) => {
|
||||
onListen()
|
||||
setCompilerScope(compiler)
|
||||
},
|
||||
[setCompilerScope, onListen]
|
||||
)
|
||||
|
||||
const setImageName = useCallback(
|
||||
(imageName: string) => {
|
||||
onListen()
|
||||
setImageNameScope(imageName)
|
||||
},
|
||||
[setImageNameScope, onListen]
|
||||
)
|
||||
|
||||
const setSpellCheckLanguage = useCallback(
|
||||
(spellCheckLanguage: string) => {
|
||||
onListen()
|
||||
setSpellCheckLanguageScope(spellCheckLanguage)
|
||||
},
|
||||
[setSpellCheckLanguageScope, onListen]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
// data is not available on initial mounting
|
||||
const dataAvailable = compiler && imageName && spellCheckLanguage
|
||||
const dataAvailable =
|
||||
compilerScope && imageNameScope && spellCheckLanguageScope
|
||||
|
||||
if (dataAvailable && ide?.socket) {
|
||||
ide.socket.on('compilerUpdated', setCompiler)
|
||||
@@ -32,11 +63,11 @@ export default function useProjectWideSettingsSocketListener() {
|
||||
}
|
||||
}, [
|
||||
ide?.socket,
|
||||
compiler,
|
||||
compilerScope,
|
||||
setCompiler,
|
||||
imageName,
|
||||
imageNameScope,
|
||||
setImageName,
|
||||
spellCheckLanguage,
|
||||
spellCheckLanguageScope,
|
||||
setSpellCheckLanguage,
|
||||
])
|
||||
}
|
||||
|
||||
@@ -6,8 +6,13 @@ import { ProjectSettingsScope, saveProjectSettings } from '../utils/api'
|
||||
import useSetRootDocId from './use-set-root-doc-id'
|
||||
import useSetSpellCheckLanguage from './use-set-spell-check-language'
|
||||
|
||||
// TODO: handle ignoreUpdates
|
||||
export default function useSetProjectWideSettings() {
|
||||
type UseSetProjectWideSettings = {
|
||||
ignoreUpdates: boolean
|
||||
}
|
||||
|
||||
export default function useSetProjectWideSettings({
|
||||
ignoreUpdates,
|
||||
}: UseSetProjectWideSettings) {
|
||||
// The value will be undefined on mount
|
||||
const [project, setProject] = useScopeValue<ProjectSettingsScope | undefined>(
|
||||
'project',
|
||||
@@ -17,26 +22,30 @@ export default function useSetProjectWideSettings() {
|
||||
|
||||
const setCompiler = useCallback(
|
||||
(compiler: ProjectCompiler) => {
|
||||
if (project?.compiler) {
|
||||
const allowUpdate = !ignoreUpdates && project?.compiler
|
||||
|
||||
if (allowUpdate) {
|
||||
setProject({ ...project, compiler })
|
||||
saveProjectSettings({ projectId, compiler })
|
||||
}
|
||||
},
|
||||
[projectId, project, setProject]
|
||||
[projectId, project, setProject, ignoreUpdates]
|
||||
)
|
||||
|
||||
const setImageName = useCallback(
|
||||
(imageName: string) => {
|
||||
if (project?.imageName) {
|
||||
const allowUpdate = !ignoreUpdates && project?.imageName
|
||||
|
||||
if (allowUpdate) {
|
||||
setProject({ ...project, imageName })
|
||||
saveProjectSettings({ projectId, imageName })
|
||||
}
|
||||
},
|
||||
[projectId, project, setProject]
|
||||
[projectId, project, setProject, ignoreUpdates]
|
||||
)
|
||||
|
||||
const setRootDocId = useSetRootDocId()
|
||||
const setSpellCheckLanguage = useSetSpellCheckLanguage()
|
||||
const setRootDocId = useSetRootDocId({ ignoreUpdates })
|
||||
const setSpellCheckLanguage = useSetSpellCheckLanguage({ ignoreUpdates })
|
||||
|
||||
return {
|
||||
compiler: project?.compiler,
|
||||
|
||||
@@ -4,7 +4,11 @@ import { useProjectContext } from '../../../shared/context/project-context'
|
||||
import useScopeValue from '../../../shared/hooks/use-scope-value'
|
||||
import { saveProjectSettings } from '../utils/api'
|
||||
|
||||
export default function useSetRootDocId() {
|
||||
type UseSetRootDocId = {
|
||||
ignoreUpdates: boolean
|
||||
}
|
||||
|
||||
export default function useSetRootDocId({ ignoreUpdates }: UseSetRootDocId) {
|
||||
const [rootDocIdScope, setRootDocIdScope] =
|
||||
useScopeValue<string>('project.rootDoc_id')
|
||||
const { permissionsLevel } = useEditorContext()
|
||||
@@ -12,12 +16,13 @@ export default function useSetRootDocId() {
|
||||
|
||||
const setRootDocId = useCallback(
|
||||
async (rootDocId: string) => {
|
||||
const disallowChange =
|
||||
typeof rootDocIdScope === 'undefined' ||
|
||||
permissionsLevel === 'readOnly' ||
|
||||
rootDocIdScope === rootDocId
|
||||
const allowUpdate =
|
||||
!ignoreUpdates &&
|
||||
typeof rootDocIdScope !== 'undefined' &&
|
||||
permissionsLevel !== 'readOnly' &&
|
||||
rootDocIdScope !== rootDocId
|
||||
|
||||
if (!disallowChange) {
|
||||
if (allowUpdate) {
|
||||
try {
|
||||
await saveProjectSettings({ projectId, rootDoc_id: rootDocId })
|
||||
setRootDocIdScope(rootDocId)
|
||||
@@ -26,7 +31,13 @@ export default function useSetRootDocId() {
|
||||
}
|
||||
}
|
||||
},
|
||||
[permissionsLevel, projectId, rootDocIdScope, setRootDocIdScope]
|
||||
[
|
||||
permissionsLevel,
|
||||
projectId,
|
||||
rootDocIdScope,
|
||||
setRootDocIdScope,
|
||||
ignoreUpdates,
|
||||
]
|
||||
)
|
||||
return setRootDocId
|
||||
}
|
||||
|
||||
@@ -4,17 +4,25 @@ import { useProjectContext } from '../../../shared/context/project-context'
|
||||
import useScopeValue from '../../../shared/hooks/use-scope-value'
|
||||
import { saveProjectSettings, saveUserSettings } from '../utils/api'
|
||||
|
||||
export default function useSetSpellCheckLanguage() {
|
||||
type UseSetSpellCheckLanguage = {
|
||||
ignoreUpdates: boolean
|
||||
}
|
||||
|
||||
export default function useSetSpellCheckLanguage({
|
||||
ignoreUpdates,
|
||||
}: UseSetSpellCheckLanguage) {
|
||||
const [spellCheckLanguageScope, setSpellCheckLanguageScope] =
|
||||
useScopeValue<string>('project.spellCheckLanguage')
|
||||
const { _id: projectId } = useProjectContext()
|
||||
|
||||
const setSpellCheckLanguage = useCallback(
|
||||
(spellCheckLanguage: string) => {
|
||||
if (
|
||||
spellCheckLanguageScope &&
|
||||
const allowUpdate =
|
||||
!ignoreUpdates &&
|
||||
spellCheckLanguage &&
|
||||
spellCheckLanguage !== spellCheckLanguageScope
|
||||
) {
|
||||
|
||||
if (allowUpdate) {
|
||||
sendMB('setting-changed', {
|
||||
changedSetting: 'spellCheckLanguage',
|
||||
changedSettingVal: spellCheckLanguage,
|
||||
@@ -27,7 +35,12 @@ export default function useSetSpellCheckLanguage() {
|
||||
saveUserSettings({ spellCheckLanguage })
|
||||
}
|
||||
},
|
||||
[projectId, setSpellCheckLanguageScope, spellCheckLanguageScope]
|
||||
[
|
||||
projectId,
|
||||
setSpellCheckLanguageScope,
|
||||
spellCheckLanguageScope,
|
||||
ignoreUpdates,
|
||||
]
|
||||
)
|
||||
|
||||
return setSpellCheckLanguage
|
||||
|
||||
Reference in New Issue
Block a user