mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-07 20:31:06 -05: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:
parent
af01ace5a0
commit
677ec173ed
5 changed files with 101 additions and 33 deletions
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue