Portal/rsconcept/frontend/src/context/OssContext.tsx
Ivan fbd84ece4d
Some checks failed
Backend CI / build (3.12) (push) Has been cancelled
Frontend CI / build (22.x) (push) Has been cancelled
R: Invalidate OSS on RSForm change. Add lazy loading
2024-08-16 12:44:09 +03:00

447 lines
12 KiB
TypeScript

'use client';
import { createContext, useCallback, useContext, useEffect, useMemo, useState } from 'react';
import { DataCallback } from '@/backend/apiTransport';
import {
deleteUnsubscribe,
patchLibraryItem,
patchSetAccessPolicy,
patchSetEditors,
patchSetLocation,
patchSetOwner,
postSubscribe
} from '@/backend/library';
import {
patchCreateInput,
patchDeleteOperation,
patchSetInput,
patchUpdateOperation,
patchUpdatePositions,
postCreateOperation,
postExecuteOperation
} from '@/backend/oss';
import { type ErrorData } from '@/components/info/InfoError';
import { AccessPolicy, ILibraryItem } from '@/models/library';
import { ILibraryUpdateData } from '@/models/library';
import {
IOperationCreateData,
IOperationData,
IOperationDeleteData,
IOperationSchema,
IOperationSchemaData,
IOperationSetInputData,
IOperationUpdateData,
IPositionsData,
ITargetOperation
} from '@/models/oss';
import { UserID } from '@/models/user';
import { contextOutsideScope } from '@/utils/labels';
import { useAuth } from './AuthContext';
import { useGlobalOss } from './GlobalOssContext';
import { useLibrary } from './LibraryContext';
interface IOssContext {
schema?: IOperationSchema;
itemID: string;
loading: boolean;
loadingError: ErrorData;
processing: boolean;
processingError: ErrorData;
isOwned: boolean;
isSubscribed: boolean;
update: (data: ILibraryUpdateData, callback?: DataCallback<ILibraryItem>) => 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;
savePositions: (data: IPositionsData, callback?: () => void) => void;
createOperation: (data: IOperationCreateData, callback?: DataCallback<IOperationData>) => void;
deleteOperation: (data: IOperationDeleteData, callback?: () => void) => void;
createInput: (data: ITargetOperation, callback?: DataCallback<ILibraryItem>) => void;
setInput: (data: IOperationSetInputData, callback?: () => void) => void;
updateOperation: (data: IOperationUpdateData, callback?: () => void) => void;
executeOperation: (data: ITargetOperation, callback?: () => void) => void;
}
const OssContext = createContext<IOssContext | null>(null);
export const useOSS = () => {
const context = useContext(OssContext);
if (context === null) {
throw new Error(contextOutsideScope('useOSS', 'OssState'));
}
return context;
};
interface OssStateProps {
itemID: string;
children: React.ReactNode;
}
export const OssState = ({ itemID, children }: OssStateProps) => {
const library = useLibrary();
const oss = useGlobalOss();
const model = oss.schema;
const { user } = useAuth();
const [processing, setProcessing] = useState(false);
const [processingError, setProcessingError] = useState<ErrorData>(undefined);
const [toggleTracking, setToggleTracking] = useState(false);
const isOwned = useMemo(() => {
return user?.id === model?.owner || false;
}, [user, model?.owner]);
const isSubscribed = useMemo(() => {
if (!user || !model || !user.id) {
return false;
}
return model.subscribers.includes(user.id);
}, [user, model, toggleTracking]);
useEffect(() => {
oss.setID(itemID);
}, [itemID, oss.setID]);
const update = useCallback(
(data: ILibraryUpdateData, callback?: DataCallback<ILibraryItem>) => {
if (!model) {
return;
}
setProcessingError(undefined);
patchLibraryItem(itemID, {
data: data,
showError: true,
setLoading: setProcessing,
onError: setProcessingError,
onSuccess: newData => {
const fullData: IOperationSchemaData = Object.assign(model, newData);
oss.setData(fullData);
library.localUpdateItem(newData);
if (callback) callback(newData);
}
});
},
[itemID, model, library.localUpdateItem, oss.setData]
);
const subscribe = useCallback(
(callback?: () => void) => {
if (!model || !user) {
return;
}
setProcessingError(undefined);
postSubscribe(itemID, {
showError: true,
setLoading: setProcessing,
onError: setProcessingError,
onSuccess: () => {
if (user.id && !model.subscribers.includes(user.id)) {
model.subscribers.push(user.id);
}
if (!user.subscriptions.includes(model.id)) {
user.subscriptions.push(model.id);
}
setToggleTracking(prev => !prev);
if (callback) callback();
}
});
},
[itemID, user, model]
);
const unsubscribe = useCallback(
(callback?: () => void) => {
if (!model || !user) {
return;
}
setProcessingError(undefined);
deleteUnsubscribe(itemID, {
showError: true,
setLoading: setProcessing,
onError: setProcessingError,
onSuccess: () => {
if (user.id && model.subscribers.includes(user.id)) {
model.subscribers.splice(model.subscribers.indexOf(user.id), 1);
}
if (user.subscriptions.includes(model.id)) {
user.subscriptions.splice(user.subscriptions.indexOf(model.id), 1);
}
setToggleTracking(prev => !prev);
if (callback) callback();
}
});
},
[itemID, model, user]
);
const setOwner = useCallback(
(newOwner: UserID, callback?: () => void) => {
if (!model) {
return;
}
setProcessingError(undefined);
patchSetOwner(itemID, {
data: {
user: newOwner
},
showError: true,
setLoading: setProcessing,
onError: setProcessingError,
onSuccess: () => {
model.owner = newOwner;
library.localUpdateItem(model);
if (callback) callback();
}
});
},
[itemID, model, library.localUpdateItem]
);
const setAccessPolicy = useCallback(
(newPolicy: AccessPolicy, callback?: () => void) => {
if (!model) {
return;
}
setProcessingError(undefined);
patchSetAccessPolicy(itemID, {
data: {
access_policy: newPolicy
},
showError: true,
setLoading: setProcessing,
onError: setProcessingError,
onSuccess: () => {
model.access_policy = newPolicy;
library.localUpdateItem(model);
if (callback) callback();
}
});
},
[itemID, model, library.localUpdateItem]
);
const setLocation = useCallback(
(newLocation: string, callback?: () => void) => {
if (!model) {
return;
}
setProcessingError(undefined);
patchSetLocation(itemID, {
data: {
location: newLocation
},
showError: true,
setLoading: setProcessing,
onError: setProcessingError,
onSuccess: () => {
model.location = newLocation;
library.localUpdateItem(model);
if (callback) callback();
}
});
},
[itemID, model, library.localUpdateItem]
);
const setEditors = useCallback(
(newEditors: UserID[], callback?: () => void) => {
if (!model) {
return;
}
setProcessingError(undefined);
patchSetEditors(itemID, {
data: {
users: newEditors
},
showError: true,
setLoading: setProcessing,
onError: setProcessingError,
onSuccess: () => {
model.editors = newEditors;
if (callback) callback();
}
});
},
[itemID, model]
);
const savePositions = useCallback(
(data: IPositionsData, callback?: () => void) => {
setProcessingError(undefined);
patchUpdatePositions(itemID, {
data: data,
showError: true,
setLoading: setProcessing,
onError: setProcessingError,
onSuccess: () => {
library.localUpdateTimestamp(Number(itemID));
if (callback) callback();
}
});
},
[itemID, library.localUpdateTimestamp]
);
const createOperation = useCallback(
(data: IOperationCreateData, callback?: DataCallback<IOperationData>) => {
setProcessingError(undefined);
postCreateOperation(itemID, {
data: data,
showError: true,
setLoading: setProcessing,
onError: setProcessingError,
onSuccess: newData => {
oss.setData(newData.oss);
library.localUpdateTimestamp(newData.oss.id);
if (callback) callback(newData.new_operation);
}
});
},
[itemID, library.localUpdateTimestamp, oss.setData]
);
const deleteOperation = useCallback(
(data: IOperationDeleteData, callback?: () => void) => {
setProcessingError(undefined);
patchDeleteOperation(itemID, {
data: data,
showError: true,
setLoading: setProcessing,
onError: setProcessingError,
onSuccess: newData => {
oss.setData(newData);
library.localUpdateTimestamp(newData.id);
if (callback) callback();
}
});
},
[itemID, library.localUpdateTimestamp, oss.setData]
);
const createInput = useCallback(
(data: ITargetOperation, callback?: DataCallback<ILibraryItem>) => {
setProcessingError(undefined);
patchCreateInput(itemID, {
data: data,
showError: true,
setLoading: setProcessing,
onError: setProcessingError,
onSuccess: newData => {
oss.setData(newData.oss);
library.reloadItems(() => {
if (callback) callback(newData.new_schema);
});
}
});
},
[itemID, library.reloadItems, oss.setData]
);
const setInput = useCallback(
(data: IOperationSetInputData, callback?: () => void) => {
if (!model) {
return;
}
setProcessingError(undefined);
patchSetInput(itemID, {
data: data,
showError: true,
setLoading: setProcessing,
onError: setProcessingError,
onSuccess: newData => {
oss.setData(newData);
library.localUpdateTimestamp(newData.id);
if (callback) callback();
}
});
},
[itemID, model, library.localUpdateTimestamp, oss.setData]
);
const updateOperation = useCallback(
(data: IOperationUpdateData, callback?: () => void) => {
if (!model) {
return;
}
setProcessingError(undefined);
patchUpdateOperation(itemID, {
data: data,
showError: true,
setLoading: setProcessing,
onError: setProcessingError,
onSuccess: newData => {
oss.setData(newData);
library.reloadItems(() => {
if (callback) callback();
});
}
});
},
[itemID, model, library.reloadItems, oss.setData]
);
const executeOperation = useCallback(
(data: ITargetOperation, callback?: () => void) => {
if (!model) {
return;
}
setProcessingError(undefined);
postExecuteOperation(itemID, {
data: data,
showError: true,
setLoading: setProcessing,
onError: setProcessingError,
onSuccess: newData => {
oss.setData(newData);
library.reloadItems(() => {
if (callback) callback();
});
}
});
},
[itemID, model, library.reloadItems, oss.setData]
);
return (
<OssContext.Provider
value={{
schema: model,
itemID,
loading: oss.loading,
loadingError: oss.loadingError,
processing,
processingError,
isOwned,
isSubscribed,
update,
subscribe,
unsubscribe,
setOwner,
setEditors,
setAccessPolicy,
setLocation,
savePositions,
createOperation,
deleteOperation,
createInput,
setInput,
updateOperation,
executeOperation
}}
>
{children}
</OssContext.Provider>
);
};