ConceptPortal-public/rsconcept/frontend/src/pages/OssPage/OssEditContext.tsx

305 lines
9.3 KiB
TypeScript
Raw Normal View History

2024-06-04 23:00:22 +03:00
'use client';
import { createContext, useContext, useEffect, useState } from 'react';
2024-06-04 23:00:22 +03:00
import { toast } from 'react-toastify';
import { useConceptNavigation } from '@/app/Navigation/NavigationContext';
2024-07-24 18:11:39 +03:00
import { urls } from '@/app/urls';
import { useAuth } from '@/backend/auth/useAuth';
import { useDeleteItem } from '@/backend/library/useDeleteItem';
import { useInputUpdate } from '@/backend/oss/useInputUpdate';
import { useOperationCreate } from '@/backend/oss/useOperationCreate';
import { useOperationDelete } from '@/backend/oss/useOperationDelete';
import { useOperationUpdate } from '@/backend/oss/useOperationUpdate';
import { useOssSuspense } from '@/backend/oss/useOSS';
import { useRelocateConstituents } from '@/backend/oss/useRelocateConstituents';
import { useUpdatePositions } from '@/backend/oss/useUpdatePositions';
import { ILibraryItemEditor, LibraryItemID } from '@/models/library';
import { calculateInsertPosition } from '@/models/miscellaneousAPI';
import { IOperationPosition, IOperationSchema, OperationID, OperationType } from '@/models/oss';
import { UserRole } from '@/models/user';
import { useDialogsStore } from '@/stores/dialogs';
import { usePreferencesStore } from '@/stores/preferences';
2025-01-15 23:03:35 +03:00
import { useRoleStore } from '@/stores/role';
2024-08-02 11:17:39 +03:00
import { PARAMETER } from '@/utils/constants';
import { information, prompts } from '@/utils/labels';
2024-06-04 23:00:22 +03:00
import { RSTabID } from '../RSFormPage/RSEditContext';
export enum OssTabID {
CARD = 0,
GRAPH = 1
}
2024-08-02 11:17:39 +03:00
export interface ICreateOperationPrompt {
2024-09-16 19:38:51 +03:00
defaultX: number;
defaultY: number;
2024-08-02 11:17:39 +03:00
inputs: OperationID[];
positions: IOperationPosition[];
callback: (newID: OperationID) => void;
}
export interface IOssEditContext extends ILibraryItemEditor {
schema: IOperationSchema;
2024-07-23 23:04:21 +03:00
selected: OperationID[];
2024-06-04 23:00:22 +03:00
isOwned: boolean;
2024-06-04 23:00:22 +03:00
isMutable: boolean;
isAttachedToOSS: boolean;
2024-06-04 23:00:22 +03:00
2024-07-26 00:34:08 +03:00
showTooltip: boolean;
setShowTooltip: (newValue: boolean) => void;
2024-07-26 00:34:08 +03:00
navigateTab: (tab: OssTabID) => void;
navigateOperationSchema: (target: OperationID) => void;
2024-06-04 23:00:22 +03:00
deleteSchema: () => void;
2024-07-23 23:04:21 +03:00
setSelected: React.Dispatch<React.SetStateAction<OperationID[]>>;
canDelete: (target: OperationID) => boolean;
promptCreateOperation: (props: ICreateOperationPrompt) => void;
promptDeleteOperation: (target: OperationID, positions: IOperationPosition[]) => void;
2024-07-28 21:30:10 +03:00
promptEditInput: (target: OperationID, positions: IOperationPosition[]) => void;
2024-07-29 16:56:24 +03:00
promptEditOperation: (target: OperationID, positions: IOperationPosition[]) => void;
2024-10-28 23:55:25 +03:00
promptRelocateConstituents: (target: OperationID | undefined, positions: IOperationPosition[]) => void;
2024-06-04 23:00:22 +03:00
}
const OssEditContext = createContext<IOssEditContext | null>(null);
export const useOssEdit = () => {
const context = useContext(OssEditContext);
if (context === null) {
2024-12-12 21:58:19 +03:00
throw new Error('useOssEdit has to be used within <OssEditState>');
2024-06-04 23:00:22 +03:00
}
return context;
};
interface OssEditStateProps {
itemID: LibraryItemID;
2024-06-04 23:00:22 +03:00
}
export const OssEditState = ({ itemID, children }: React.PropsWithChildren<OssEditStateProps>) => {
2024-07-24 18:11:39 +03:00
const router = useConceptNavigation();
2024-06-04 23:00:22 +03:00
const { user } = useAuth();
const adminMode = usePreferencesStore(state => state.adminMode);
2025-01-15 23:03:35 +03:00
const role = useRoleStore(state => state.role);
const adjustRole = useRoleStore(state => state.adjustRole);
const { schema } = useOssSuspense({ itemID: itemID });
2024-06-04 23:00:22 +03:00
const isOwned = user?.id === schema.owner || false;
const isMutable = role > UserRole.READER && !schema.read_only;
2024-06-04 23:00:22 +03:00
2024-07-26 00:34:08 +03:00
const [showTooltip, setShowTooltip] = useState(true);
const [selected, setSelected] = useState<OperationID[]>([]);
2024-08-02 11:17:39 +03:00
const showEditInput = useDialogsStore(state => state.showChangeInputSchema);
const showEditOperation = useDialogsStore(state => state.showEditOperation);
const showDeleteOperation = useDialogsStore(state => state.showDeleteOperation);
const showRelocateConstituents = useDialogsStore(state => state.showRelocateConstituents);
const showCreateOperation = useDialogsStore(state => state.showCreateOperation);
const { deleteItem } = useDeleteItem();
const { updatePositions } = useUpdatePositions();
const { operationCreate } = useOperationCreate();
const { operationDelete } = useOperationDelete();
const { operationUpdate } = useOperationUpdate();
const { relocateConstituents } = useRelocateConstituents();
const { inputUpdate } = useInputUpdate();
2024-07-21 15:19:57 +03:00
useEffect(
2024-06-04 23:00:22 +03:00
() =>
2025-01-15 23:03:35 +03:00
adjustRole({
isOwner: isOwned,
isEditor: (user && schema.editors.includes(user?.id)) ?? false,
2025-01-15 23:03:35 +03:00
isStaff: user?.is_staff ?? false,
adminMode: adminMode
2024-06-04 23:00:22 +03:00
}),
[schema, adjustRole, isOwned, user, adminMode]
2024-06-04 23:00:22 +03:00
);
function navigateTab(tab: OssTabID) {
if (!schema) {
return;
}
const url = urls.oss_props({
id: schema.id,
tab: tab
});
router.push(url);
}
2024-06-04 23:00:22 +03:00
function navigateOperationSchema(target: OperationID) {
const node = schema.operationByID.get(target);
if (!node?.result) {
return;
}
router.push(urls.schema_props({ id: node.result, tab: RSTabID.CST_LIST }));
}
2024-06-04 23:00:22 +03:00
function deleteSchema() {
if (!schema || !window.confirm(prompts.deleteOSS)) {
return;
}
deleteItem(schema.id, () => {
toast.success(information.itemDestroyed);
router.push(urls.library);
});
}
2024-07-24 18:11:39 +03:00
function promptCreateOperation({ defaultX, defaultY, inputs, positions, callback }: ICreateOperationPrompt) {
showCreateOperation({
oss: schema,
onCreate: data => {
const target = calculateInsertPosition(schema, data.item_data.operation_type, data.arguments ?? [], positions, {
x: defaultX,
y: defaultY
});
data.positions = positions;
data.item_data.position_x = target.x;
data.item_data.position_y = target.y;
operationCreate({ itemID: schema.id, data }, operation => {
toast.success(information.newOperation(operation.alias));
if (callback) {
setTimeout(() => callback(operation.id), PARAMETER.refreshTimeout);
2024-07-23 23:04:21 +03:00
}
});
},
initialInputs: inputs
});
}
function canDelete(target: OperationID) {
const operation = schema.operationByID.get(target);
if (!operation) {
return false;
}
if (operation.operation_type === OperationType.INPUT) {
return true;
}
return schema.graph.expandOutputs([target]).length === 0;
}
function promptEditOperation(target: OperationID, positions: IOperationPosition[]) {
const operation = schema.operationByID.get(target);
if (!operation) {
return;
}
showEditOperation({
oss: schema,
target: operation,
onSubmit: data => {
data.positions = positions;
operationUpdate({ itemID: schema.id, data }, () => toast.success(information.changesSaved));
}
});
}
function promptDeleteOperation(target: OperationID, positions: IOperationPosition[]) {
const operation = schema.operationByID.get(target);
if (!operation) {
return;
}
showDeleteOperation({
target: operation,
onSubmit: (targetID, keepConstituents, deleteSchema) => {
operationDelete(
{
itemID: schema.id,
data: {
target: targetID,
positions: positions,
keep_constituents: keepConstituents,
delete_schema: deleteSchema
}
},
() => toast.success(information.operationDestroyed)
);
}
});
}
function promptEditInput(target: OperationID, positions: IOperationPosition[]) {
const operation = schema.operationByID.get(target);
if (!operation) {
return;
}
showEditInput({
oss: schema,
target: operation,
onSubmit: (target, newInput) => {
inputUpdate(
{
itemID: schema.id,
data: {
target: target,
positions: positions,
input: newInput ?? null
}
},
() => toast.success(information.changesSaved)
);
}
});
}
function promptRelocateConstituents(target: OperationID | undefined, positions: IOperationPosition[]) {
const operation = target ? schema.operationByID.get(target) : undefined;
showRelocateConstituents({
oss: schema,
initialTarget: operation,
onSubmit: data => {
if (
positions.every(item => {
const operation = schema.operationByID.get(item.id)!;
return operation.position_x === item.position_x && operation.position_y === item.position_y;
})
) {
relocateConstituents({ itemID: schema.id, data }, () => toast.success(information.changesSaved));
} else {
updatePositions(
{
itemID: schema.id, //
positions: positions
},
() => relocateConstituents({ itemID: schema.id, data }, () => toast.success(information.changesSaved))
);
}
}
});
}
2024-06-04 23:00:22 +03:00
return (
2024-12-12 21:58:19 +03:00
<OssEditContext
2024-06-04 23:00:22 +03:00
value={{
schema,
2024-07-23 23:04:21 +03:00
selected,
navigateTab,
deleteSchema,
2024-07-26 00:34:08 +03:00
showTooltip,
setShowTooltip,
isOwned,
2024-06-04 23:00:22 +03:00
isMutable,
isAttachedToOSS: false,
2024-06-04 23:00:22 +03:00
2024-07-23 23:04:21 +03:00
setSelected,
2024-07-21 15:19:57 +03:00
navigateOperationSchema,
2024-07-23 23:04:21 +03:00
promptCreateOperation,
canDelete,
promptDeleteOperation,
2024-07-29 16:56:24 +03:00
promptEditInput,
2024-07-29 22:31:11 +03:00
promptEditOperation,
promptRelocateConstituents
2024-06-04 23:00:22 +03:00
}}
>
{children}
2024-12-12 21:58:19 +03:00
</OssEditContext>
2024-06-04 23:00:22 +03:00
);
};