[web] Clean up boolean handling of track changes state in the frontend

GitOrigin-RevId: 640380219274e156d5bbac704d82e7ea75e1fee2
This commit is contained in:
Domagoj Kriskovic
2026-09-17 11:17:51 +00:00
committed by Copybot
parent fa6eccf939
commit a2712ce523
5 changed files with 35 additions and 40 deletions
@@ -14,11 +14,13 @@ import { useEditorPropertiesContext } from '@/features/ide-react/context/editor-
import { useUserContext } from '@/shared/context/user-context'
import { postJSON } from '@/infrastructure/fetch-json'
import useEventListener from '@/shared/hooks/use-event-listener'
import { ProjectMetadata } from '@/shared/context/types/project-metadata'
import {
ProjectMetadata,
TrackChangesStateData,
} from '@/shared/context/types/project-metadata'
import { usePermissionsContext } from '@/features/ide-react/context/permissions-context'
export type TrackChangesState = {
onForEveryone: boolean
onForGuests: boolean
onForMembers: Record<UserId, boolean | undefined>
}
@@ -28,7 +30,6 @@ export const TrackChangesStateContext = createContext<
>(undefined)
type SaveTrackChangesRequestBody = {
on?: boolean
on_for?: Record<UserId, boolean | undefined>
on_for_guests?: boolean
}
@@ -51,8 +52,13 @@ export const TrackChangesStateProvider: FC<React.PropsWithChildren> = ({
const user = useUserContext()
const { setWantTrackChanges } = useEditorPropertiesContext()
const trackChangesValue: ProjectMetadata['trackChangesState'] =
project?.trackChangesState ?? false
const trackChangesValue = useMemo<TrackChangesStateData>(() => {
if (typeof project?.trackChangesState === 'object') {
return project.trackChangesState
} else {
return {}
}
}, [project?.trackChangesState])
useSocketListener(
socket,
@@ -66,31 +72,19 @@ export const TrackChangesStateProvider: FC<React.PropsWithChildren> = ({
)
useEffect(() => {
setWantTrackChanges(
trackChangesValue === true ||
(trackChangesValue !== false &&
trackChangesValue[user.id ?? '__guests__'])
)
setWantTrackChanges(Boolean(trackChangesValue[user.id ?? '__guests__']))
}, [setWantTrackChanges, trackChangesValue, user.id])
const trackChangesIsObject =
trackChangesValue !== true && trackChangesValue !== false
const onForEveryone = trackChangesValue === true
const onForGuests =
onForEveryone ||
(trackChangesIsObject && trackChangesValue.__guests__ === true)
const onForGuests = trackChangesValue.__guests__ === true
const onForMembers = useMemo(() => {
const onForMembers: Record<UserId, boolean | undefined> = {}
if (trackChangesIsObject) {
for (const key of Object.keys(trackChangesValue)) {
if (key !== '__guests__') {
onForMembers[key as UserId] = trackChangesValue[key as UserId]
}
for (const key of Object.keys(trackChangesValue)) {
if (key !== '__guests__') {
onForMembers[key as UserId] = trackChangesValue[key as UserId]
}
}
return onForMembers
}, [trackChangesIsObject, trackChangesValue])
}, [trackChangesValue])
const saveTrackChanges = useCallback(
async (trackChangesBody: SaveTrackChangesRequestBody) => {
@@ -126,12 +120,7 @@ export const TrackChangesStateProvider: FC<React.PropsWithChildren> = ({
useEventListener(
'toggle-track-changes',
useCallback(() => {
if (
user.id &&
features.trackChanges &&
permissions.write &&
!onForEveryone
) {
if (user.id && features.trackChanges && permissions.write) {
const value = onForMembers[user.id]
actions.saveTrackChanges({
on_for: {
@@ -143,7 +132,6 @@ export const TrackChangesStateProvider: FC<React.PropsWithChildren> = ({
}, [
actions,
onForMembers,
onForEveryone,
permissions.write,
features.trackChanges,
user.id,
@@ -151,8 +139,8 @@ export const TrackChangesStateProvider: FC<React.PropsWithChildren> = ({
)
const value = useMemo(
() => ({ onForEveryone, onForGuests, onForMembers }),
[onForEveryone, onForGuests, onForMembers]
() => ({ onForGuests, onForMembers }),
[onForGuests, onForMembers]
)
return (
@@ -41,7 +41,14 @@ export interface ProjectMetadata extends ProjectSettings {
signUpDate: string
}
rootFolder?: Folder[]
trackChangesState: boolean | Record<UserId | '__guests__', boolean>
trackChangesState?: false | TrackChangesStateData
}
// The explicit per-user track changes format, keyed by user id. The
// `__guests__` key toggles track changes for link-sharing guests (it is still
// used by some Server Pro organisations)
export type TrackChangesStateData = Partial<
Record<UserId | '__guests__', boolean>
>
export type ProjectUpdate = Partial<ProjectMetadata>
@@ -15,10 +15,9 @@ export const useTrackingChangesMode = (): Mode => {
return 'review'
}
const trackChangesForCurrentUser =
trackChanges?.onForEveryone ||
(user?.id && trackChanges?.onForMembers[user.id]) ||
(!user?.id && trackChanges?.onForGuests)
const trackChangesForCurrentUser = user?.id
? trackChanges?.onForMembers[user.id]
: trackChanges?.onForGuests
if (trackChangesForCurrentUser) {
return 'review'
@@ -566,7 +566,7 @@ describe('editor context menu', { scrollBehavior: false }, function () {
ProjectProvider: makeProjectProvider(
mockProject({
// Re-assigns `withTrackChanges` value in the `track-changes-state-context` useEffect hook
trackChangesState: true,
trackChangesState: { [USER_ID]: true },
projectFeatures: {
trackChanges: true,
trackChangesVisible: true,
@@ -44,6 +44,7 @@ import type { DocumentContainer } from '@/features/ide-react/editor/document-con
import {
ProjectMetadata,
ProjectUpdate,
TrackChangesStateData,
} from '@/shared/context/types/project-metadata'
import { User, UserId } from '../../../types/user'
import { ProjectCompiler } from '../../../types/project-settings'
@@ -132,7 +133,7 @@ export const projectDefaults: ProjectMetadata = {
compiler: 'pdflatex' as ProjectCompiler,
members: [],
invites: [],
trackChangesState: {} as Record<UserId | '__guests__', boolean>,
trackChangesState: {} as TrackChangesStateData,
spellCheckLanguage: 'en',
}
@@ -233,7 +234,7 @@ export function EditorProviders({
compiler,
members: [],
invites: [],
trackChangesState: false,
trackChangesState: {} as TrackChangesStateData,
spellCheckLanguage: 'en',
}