'use client'; import { createContext, useCallback, useContext, useMemo, useState } from 'react'; import { type DataCallback, deleteUnsubscribe, deleteVersion, getTRSFile, patchConstituenta, patchDeleteConstituenta, patchEditorsSet as patchSetEditors, patchInlineSynthesis, patchLibraryItem, patchMoveConstituenta, patchProduceStructure, patchRenameConstituenta, patchResetAliases, patchRestoreOrder, patchRestoreVersion, patchSetAccessPolicy, patchSetLocation, patchSetOwner, patchSubstituteConstituents, patchUploadTRS, patchVersion, postCreateVersion, postNewConstituenta, postSubscribe } from '@/app/backendAPI'; import { type ErrorData } from '@/components/info/InfoError'; import useRSFormDetails from '@/hooks/useRSFormDetails'; import { AccessPolicy, ILibraryItem, IVersionData, VersionID } from '@/models/library'; import { ILibraryUpdateData } from '@/models/library'; import { ConstituentaID, IConstituentaList, IConstituentaMeta, ICstCreateData, ICstMovetoData, ICstRenameData, ICstSubstituteData, ICstUpdateData, IInlineSynthesisData, IRSForm, IRSFormData, IRSFormUploadData, ITargetCst } from '@/models/rsform'; import { UserID } from '@/models/user'; import { useAuth } from './AuthContext'; import { useLibrary } from './LibraryContext'; interface IRSFormContext { schema?: IRSForm; schemaID: string; versionID?: string; loading: boolean; errorLoading: ErrorData; processing: boolean; processingError: ErrorData; isArchive: boolean; isOwned: boolean; isSubscribed: boolean; update: (data: ILibraryUpdateData, callback?: DataCallback) => void; download: (callback: DataCallback) => void; upload: (data: IRSFormUploadData, callback: () => void) => void; subscribe: (callback?: () => void) => void; unsubscribe: (callback?: () => void) => void; setOwner: (newOwner: UserID, callback?: () => void) => void; setAccessPolicy: (newPolicy: AccessPolicy, callback?: () => void) => void; setLocation: (newLocation: string, callback?: () => void) => void; setEditors: (newEditors: UserID[], callback?: () => void) => void; resetAliases: (callback: () => void) => void; restoreOrder: (callback: () => void) => void; produceStructure: (data: ITargetCst, callback?: DataCallback) => void; inlineSynthesis: (data: IInlineSynthesisData, callback?: DataCallback) => void; cstCreate: (data: ICstCreateData, callback?: DataCallback) => void; cstRename: (data: ICstRenameData, callback?: DataCallback) => void; cstSubstitute: (data: ICstSubstituteData, callback?: () => void) => void; cstUpdate: (data: ICstUpdateData, callback?: DataCallback) => void; cstDelete: (data: IConstituentaList, callback?: () => void) => void; cstMoveTo: (data: ICstMovetoData, callback?: () => void) => void; versionCreate: (data: IVersionData, callback?: (version: VersionID) => void) => void; versionUpdate: (target: VersionID, data: IVersionData, callback?: () => void) => void; versionDelete: (target: VersionID, callback?: () => void) => void; versionRestore: (target: string, callback?: () => void) => void; } const RSFormContext = createContext(null); export const useRSForm = () => { const context = useContext(RSFormContext); if (context === null) { throw new Error('useRSForm has to be used within '); } return context; }; interface RSFormStateProps { schemaID: string; versionID?: string; children: React.ReactNode; } export const RSFormState = ({ schemaID, versionID, children }: RSFormStateProps) => { const library = useLibrary(); const { user } = useAuth(); const { schema, // prettier: split lines reload, error: errorLoading, setSchema, loading } = useRSFormDetails({ target: schemaID, version: versionID }); const [processing, setProcessing] = useState(false); const [processingError, setProcessingError] = useState(undefined); const [toggleTracking, setToggleTracking] = useState(false); const isOwned = useMemo(() => { return user?.id === schema?.owner || false; }, [user, schema?.owner]); const isArchive = useMemo(() => !!versionID, [versionID]); const isSubscribed = useMemo(() => { if (!user || !schema || !user.id) { return false; } return schema.subscribers.includes(user.id); // eslint-disable-next-line react-hooks/exhaustive-deps }, [user, schema, toggleTracking]); const update = useCallback( (data: ILibraryUpdateData, callback?: DataCallback) => { if (!schema) { return; } setProcessingError(undefined); patchLibraryItem(schemaID, { data: data, showError: true, setLoading: setProcessing, onError: setProcessingError, onSuccess: newData => { setSchema(Object.assign(schema, newData)); library.localUpdateItem(newData); if (callback) callback(newData); } }); }, [schemaID, setSchema, schema, library] ); const upload = useCallback( (data: IRSFormUploadData, callback?: () => void) => { if (!schema) { return; } setProcessingError(undefined); patchUploadTRS(schemaID, { data: data, showError: true, setLoading: setProcessing, onError: setProcessingError, onSuccess: newData => { setSchema(newData); library.localUpdateItem(newData); if (callback) callback(); } }); }, [schemaID, setSchema, schema, library] ); const subscribe = useCallback( (callback?: () => void) => { if (!schema || !user) { return; } setProcessingError(undefined); postSubscribe(schemaID, { showError: true, setLoading: setProcessing, onError: setProcessingError, onSuccess: () => { if (user.id && !schema.subscribers.includes(user.id)) { schema.subscribers.push(user.id); } if (!user.subscriptions.includes(schema.id)) { user.subscriptions.push(schema.id); } setToggleTracking(prev => !prev); if (callback) callback(); } }); }, [schemaID, schema, user] ); const unsubscribe = useCallback( (callback?: () => void) => { if (!schema || !user) { return; } setProcessingError(undefined); deleteUnsubscribe(schemaID, { showError: true, setLoading: setProcessing, onError: setProcessingError, onSuccess: () => { if (user.id && schema.subscribers.includes(user.id)) { schema.subscribers.splice(schema.subscribers.indexOf(user.id), 1); } if (user.subscriptions.includes(schema.id)) { user.subscriptions.splice(user.subscriptions.indexOf(schema.id), 1); } setToggleTracking(prev => !prev); if (callback) callback(); } }); }, [schemaID, schema, user] ); const setOwner = useCallback( (newOwner: UserID, callback?: () => void) => { if (!schema) { return; } setProcessingError(undefined); patchSetOwner(schemaID, { data: { user: newOwner }, showError: true, setLoading: setProcessing, onError: setProcessingError, onSuccess: () => { schema.owner = newOwner; if (callback) callback(); } }); }, [schemaID, schema] ); const setAccessPolicy = useCallback( (newPolicy: AccessPolicy, callback?: () => void) => { if (!schema) { return; } setProcessingError(undefined); patchSetAccessPolicy(schemaID, { data: { access_policy: newPolicy }, showError: true, setLoading: setProcessing, onError: setProcessingError, onSuccess: () => { schema.access_policy = newPolicy; if (callback) callback(); } }); }, [schemaID, schema] ); const setLocation = useCallback( (newLocation: string, callback?: () => void) => { if (!schema) { return; } setProcessingError(undefined); patchSetLocation(schemaID, { data: { location: newLocation }, showError: true, setLoading: setProcessing, onError: setProcessingError, onSuccess: () => { schema.location = newLocation; if (callback) callback(); } }); }, [schemaID, schema] ); const setEditors = useCallback( (newEditors: UserID[], callback?: () => void) => { if (!schema) { return; } setProcessingError(undefined); patchSetEditors(schemaID, { data: { users: newEditors }, showError: true, setLoading: setProcessing, onError: setProcessingError, onSuccess: () => { schema.editors = newEditors; if (callback) callback(); } }); }, [schemaID, schema] ); const resetAliases = useCallback( (callback?: () => void) => { if (!schema || !user) { return; } setProcessingError(undefined); patchResetAliases(schemaID, { showError: true, setLoading: setProcessing, onError: setProcessingError, onSuccess: newData => { setSchema(Object.assign(schema, newData)); library.localUpdateTimestamp(newData.id); if (callback) callback(); } }); }, [schemaID, schema, library, user, setSchema] ); const restoreOrder = useCallback( (callback?: () => void) => { if (!schema || !user) { return; } setProcessingError(undefined); patchRestoreOrder(schemaID, { showError: true, setLoading: setProcessing, onError: setProcessingError, onSuccess: newData => { setSchema(Object.assign(schema, newData)); library.localUpdateTimestamp(newData.id); if (callback) callback(); } }); }, [schemaID, schema, library, user, setSchema] ); const produceStructure = useCallback( (data: ITargetCst, callback?: DataCallback) => { setProcessingError(undefined); patchProduceStructure(schemaID, { data: data, showError: true, setLoading: setProcessing, onError: setProcessingError, onSuccess: newData => { setSchema(newData.schema); library.localUpdateTimestamp(newData.schema.id); if (callback) callback(newData.cst_list); } }); }, [setSchema, library, schemaID] ); const download = useCallback( (callback: DataCallback) => { setProcessingError(undefined); getTRSFile(schemaID, String(schema?.version ?? ''), { showError: true, setLoading: setProcessing, onError: setProcessingError, onSuccess: callback }); }, [schemaID, schema] ); const cstCreate = useCallback( (data: ICstCreateData, callback?: DataCallback) => { setProcessingError(undefined); postNewConstituenta(schemaID, { data: data, showError: true, setLoading: setProcessing, onError: setProcessingError, onSuccess: newData => { setSchema(newData.schema); library.localUpdateTimestamp(newData.schema.id); if (callback) callback(newData.new_cst); } }); }, [schemaID, library, setSchema] ); const cstDelete = useCallback( (data: IConstituentaList, callback?: () => void) => { setProcessingError(undefined); patchDeleteConstituenta(schemaID, { data: data, showError: true, setLoading: setProcessing, onError: setProcessingError, onSuccess: newData => { setSchema(newData); library.localUpdateTimestamp(newData.id); if (callback) callback(); } }); }, [schemaID, library, setSchema] ); const cstUpdate = useCallback( (data: ICstUpdateData, callback?: DataCallback) => { setProcessingError(undefined); patchConstituenta(String(data.id), { data: data, showError: true, setLoading: setProcessing, onError: setProcessingError, onSuccess: newData => reload(setProcessing, () => { library.localUpdateTimestamp(Number(schemaID)); if (callback) callback(newData); }) }); }, [schemaID, library, reload] ); const cstRename = useCallback( (data: ICstRenameData, callback?: DataCallback) => { setProcessingError(undefined); patchRenameConstituenta(schemaID, { data: data, showError: true, setLoading: setProcessing, onError: setProcessingError, onSuccess: newData => { setSchema(newData.schema); library.localUpdateTimestamp(newData.schema.id); if (callback) callback(newData.new_cst); } }); }, [setSchema, library, schemaID] ); const cstSubstitute = useCallback( (data: ICstSubstituteData, callback?: () => void) => { setProcessingError(undefined); patchSubstituteConstituents(schemaID, { data: data, showError: true, setLoading: setProcessing, onError: setProcessingError, onSuccess: newData => { setSchema(newData); library.localUpdateTimestamp(newData.id); if (callback) callback(); } }); }, [setSchema, library, schemaID] ); const cstMoveTo = useCallback( (data: ICstMovetoData, callback?: () => void) => { setProcessingError(undefined); patchMoveConstituenta(schemaID, { data: data, showError: true, setLoading: setProcessing, onError: setProcessingError, onSuccess: newData => { setSchema(newData); library.localUpdateTimestamp(Number(schemaID)); if (callback) callback(); } }); }, [schemaID, library, setSchema] ); const versionCreate = useCallback( (data: IVersionData, callback?: (version: number) => void) => { setProcessingError(undefined); postCreateVersion(schemaID, { data: data, showError: true, setLoading: setProcessing, onError: setProcessingError, onSuccess: newData => { setSchema(newData.schema); library.localUpdateTimestamp(Number(schemaID)); if (callback) callback(newData.version); } }); }, [schemaID, library, setSchema] ); const versionUpdate = useCallback( (target: number, data: IVersionData, callback?: () => void) => { setProcessingError(undefined); patchVersion(String(target), { data: data, showError: true, setLoading: setProcessing, onError: setProcessingError, onSuccess: () => { schema!.versions = schema!.versions.map(prev => { if (prev.id === target) { prev.description = data.description; prev.version = data.version; return prev; } else { return prev; } }); setSchema(schema); if (callback) callback(); } }); }, [schema, setSchema] ); const versionDelete = useCallback( (target: number, callback?: () => void) => { setProcessingError(undefined); deleteVersion(String(target), { showError: true, setLoading: setProcessing, onError: setProcessingError, onSuccess: () => { schema!.versions = schema!.versions.filter(prev => prev.id !== target); setSchema(schema); if (callback) callback(); } }); }, [schema, setSchema] ); const versionRestore = useCallback( (target: string, callback?: () => void) => { setProcessingError(undefined); patchRestoreVersion(target, { showError: true, setLoading: setProcessing, onError: setProcessingError, onSuccess: () => { setSchema(schema); if (callback) callback(); } }); }, [schema, setSchema] ); const inlineSynthesis = useCallback( (data: IInlineSynthesisData, callback?: DataCallback) => { setProcessingError(undefined); patchInlineSynthesis({ data: data, showError: true, setLoading: setProcessing, onError: setProcessingError, onSuccess: newData => { setSchema(newData); library.localUpdateTimestamp(Number(schemaID)); if (callback) callback(newData); } }); }, [library, schemaID, setSchema] ); return ( {children} ); };