|
|
@@ -1,43 +1,152 @@
|
|
|
import { UserId } from '../../../../../types/user'
|
|
|
-import { createContext, FC, useContext, useEffect, useState } from 'react'
|
|
|
+import {
|
|
|
+ createContext,
|
|
|
+ FC,
|
|
|
+ useCallback,
|
|
|
+ useContext,
|
|
|
+ useEffect,
|
|
|
+ useMemo,
|
|
|
+ useState,
|
|
|
+} from 'react'
|
|
|
import useSocketListener from '@/features/ide-react/hooks/use-socket-listener'
|
|
|
import { useConnectionContext } from '@/features/ide-react/context/connection-context'
|
|
|
import { useProjectContext } from '@/shared/context/project-context'
|
|
|
import { useEditorManagerContext } from '@/features/ide-react/context/editor-manager-context'
|
|
|
import { useUserContext } from '@/shared/context/user-context'
|
|
|
+import { postJSON } from '@/infrastructure/fetch-json'
|
|
|
+import useEventListener from '@/shared/hooks/use-event-listener'
|
|
|
+import { ProjectContextValue } from '@/shared/context/types/project-context'
|
|
|
+import { usePermissionsContext } from '@/features/ide-react/context/permissions-context'
|
|
|
|
|
|
-export type TrackChangesState = boolean | Record<UserId | '__guests__', boolean>
|
|
|
+export type TrackChangesState = {
|
|
|
+ onForEveryone: boolean
|
|
|
+ onForGuests: boolean
|
|
|
+ onForMembers: Record<UserId, boolean | undefined>
|
|
|
+}
|
|
|
|
|
|
export const TrackChangesStateContext = createContext<
|
|
|
TrackChangesState | undefined
|
|
|
>(undefined)
|
|
|
|
|
|
+type SaveTrackChangesRequestBody = {
|
|
|
+ on?: boolean
|
|
|
+ on_for?: Record<UserId, boolean | undefined>
|
|
|
+ on_for_guests?: boolean
|
|
|
+}
|
|
|
+
|
|
|
+type TrackChangesStateActions = {
|
|
|
+ saveTrackChanges: (trackChangesBody: SaveTrackChangesRequestBody) => void
|
|
|
+}
|
|
|
+
|
|
|
+const TrackChangesStateActionsContext = createContext<
|
|
|
+ TrackChangesStateActions | undefined
|
|
|
+>(undefined)
|
|
|
+
|
|
|
export const TrackChangesStateProvider: FC = ({ children }) => {
|
|
|
+ const permissions = usePermissionsContext()
|
|
|
const { socket } = useConnectionContext()
|
|
|
const project = useProjectContext()
|
|
|
const user = useUserContext()
|
|
|
const { setWantTrackChanges } = useEditorManagerContext()
|
|
|
|
|
|
// TODO: update project.trackChangesState instead?
|
|
|
- const [value, setValue] = useState<TrackChangesState>(
|
|
|
- project.trackChangesState ?? false
|
|
|
- )
|
|
|
+ const [trackChangesValue, setTrackChangesValue] = useState<
|
|
|
+ ProjectContextValue['trackChangesState']
|
|
|
+ >(project.trackChangesState ?? false)
|
|
|
|
|
|
- useSocketListener(socket, 'toggle-track-changes', setValue)
|
|
|
+ useSocketListener(socket, 'toggle-track-changes', setTrackChangesValue)
|
|
|
|
|
|
useEffect(() => {
|
|
|
setWantTrackChanges(
|
|
|
- value === true || (value !== false && value[user.id ?? '__guests__'])
|
|
|
+ trackChangesValue === true ||
|
|
|
+ (trackChangesValue !== false &&
|
|
|
+ trackChangesValue[user.id ?? '__guests__'])
|
|
|
)
|
|
|
- }, [setWantTrackChanges, value, user.id])
|
|
|
+ }, [setWantTrackChanges, trackChangesValue, user.id])
|
|
|
+
|
|
|
+ const actions = useMemo(
|
|
|
+ () => ({
|
|
|
+ async saveTrackChanges(trackChangesBody: SaveTrackChangesRequestBody) {
|
|
|
+ postJSON(`/project/${project._id}/track_changes`, {
|
|
|
+ body: trackChangesBody,
|
|
|
+ })
|
|
|
+ },
|
|
|
+ }),
|
|
|
+ [project._id]
|
|
|
+ )
|
|
|
+
|
|
|
+ const trackChangesIsObject =
|
|
|
+ trackChangesValue !== true && trackChangesValue !== false
|
|
|
+ const onForEveryone = trackChangesValue === true
|
|
|
+ const onForGuests =
|
|
|
+ onForEveryone ||
|
|
|
+ (trackChangesIsObject && 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]
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return onForMembers
|
|
|
+ }, [trackChangesIsObject, trackChangesValue])
|
|
|
+
|
|
|
+ useEventListener(
|
|
|
+ 'toggle-track-changes',
|
|
|
+ useCallback(() => {
|
|
|
+ if (
|
|
|
+ user.id &&
|
|
|
+ project.features.trackChanges &&
|
|
|
+ permissions.write &&
|
|
|
+ !onForEveryone
|
|
|
+ ) {
|
|
|
+ const value = onForMembers[user.id]
|
|
|
+ actions.saveTrackChanges({
|
|
|
+ on_for: {
|
|
|
+ ...onForMembers,
|
|
|
+ [user.id]: !value,
|
|
|
+ },
|
|
|
+ on_for_guests: onForGuests,
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }, [
|
|
|
+ actions,
|
|
|
+ onForMembers,
|
|
|
+ onForGuests,
|
|
|
+ onForEveryone,
|
|
|
+ permissions.write,
|
|
|
+ project.features.trackChanges,
|
|
|
+ user.id,
|
|
|
+ ])
|
|
|
+ )
|
|
|
+
|
|
|
+ const value = useMemo(
|
|
|
+ () => ({ onForEveryone, onForGuests, onForMembers }),
|
|
|
+ [onForEveryone, onForGuests, onForMembers]
|
|
|
+ )
|
|
|
|
|
|
return (
|
|
|
- <TrackChangesStateContext.Provider value={value}>
|
|
|
- {children}
|
|
|
- </TrackChangesStateContext.Provider>
|
|
|
+ <TrackChangesStateActionsContext.Provider value={actions}>
|
|
|
+ <TrackChangesStateContext.Provider value={value}>
|
|
|
+ {children}
|
|
|
+ </TrackChangesStateContext.Provider>
|
|
|
+ </TrackChangesStateActionsContext.Provider>
|
|
|
)
|
|
|
}
|
|
|
|
|
|
export const useTrackChangesStateContext = () => {
|
|
|
return useContext(TrackChangesStateContext)
|
|
|
}
|
|
|
+
|
|
|
+export const useTrackChangesStateActionsContext = () => {
|
|
|
+ const context = useContext(TrackChangesStateActionsContext)
|
|
|
+ if (!context) {
|
|
|
+ throw new Error(
|
|
|
+ 'useTrackChangesStateActionsContext is only available inside TrackChangesStateProvider'
|
|
|
+ )
|
|
|
+ }
|
|
|
+ return context
|
|
|
+}
|