2024-06-04 23:00:22 +03:00
|
|
|
'use client';
|
|
|
|
|
2025-01-27 15:03:48 +03:00
|
|
|
import { createContext, useContext, useEffect, useState } from 'react';
|
2024-06-04 23:00:22 +03:00
|
|
|
|
2025-02-10 01:32:55 +03:00
|
|
|
import { urls, useConceptNavigation } from '@/app';
|
|
|
|
import { useAuthSuspense } from '@/features/auth/backend/useAuth';
|
|
|
|
import { useDeleteItem } from '@/features/library/backend/useDeleteItem';
|
|
|
|
import { ILibraryItemEditor, LibraryItemID } from '@/features/library/models/library';
|
|
|
|
import { useLibrarySearchStore } from '@/features/library/stores/librarySearch';
|
|
|
|
import { RSTabID } from '@/features/rsform/pages/RSFormPage/RSEditContext';
|
|
|
|
import { UserRole } from '@/features/users/models/user';
|
2025-01-16 16:31:32 +03:00
|
|
|
import { useDialogsStore } from '@/stores/dialogs';
|
2025-01-14 21:58:16 +03:00
|
|
|
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';
|
2025-01-28 19:47:24 +03:00
|
|
|
import { prompts } from '@/utils/labels';
|
2024-06-04 23:00:22 +03:00
|
|
|
|
2025-02-10 01:32:55 +03:00
|
|
|
import { useInputUpdate } from '../../backend/useInputUpdate';
|
|
|
|
import { useOperationCreate } from '../../backend/useOperationCreate';
|
|
|
|
import { useOperationDelete } from '../../backend/useOperationDelete';
|
|
|
|
import { useOperationUpdate } from '../../backend/useOperationUpdate';
|
|
|
|
import { useOssSuspense } from '../../backend/useOSS';
|
|
|
|
import { useRelocateConstituents } from '../../backend/useRelocateConstituents';
|
|
|
|
import { useUpdatePositions } from '../../backend/useUpdatePositions';
|
|
|
|
import { IOperationPosition, IOperationSchema, OperationID, OperationType } from '../../models/oss';
|
|
|
|
import { calculateInsertPosition } from '../../models/ossAPI';
|
|
|
|
|
2025-01-27 15:03:48 +03:00
|
|
|
export enum OssTabID {
|
|
|
|
CARD = 0,
|
|
|
|
GRAPH = 1
|
|
|
|
}
|
2024-08-19 19:54:12 +03:00
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2024-08-05 23:53:41 +03:00
|
|
|
export interface IOssEditContext extends ILibraryItemEditor {
|
2025-01-27 15:03:48 +03:00
|
|
|
schema: IOperationSchema;
|
2024-07-23 23:04:21 +03:00
|
|
|
selected: OperationID[];
|
2024-06-04 23:00:22 +03:00
|
|
|
|
2025-01-27 15:03:48 +03:00
|
|
|
isOwned: boolean;
|
2024-06-04 23:00:22 +03:00
|
|
|
isMutable: boolean;
|
2024-08-05 23:53:41 +03:00
|
|
|
isAttachedToOSS: boolean;
|
2024-06-04 23:00:22 +03:00
|
|
|
|
2024-07-26 00:34:08 +03:00
|
|
|
showTooltip: boolean;
|
2024-11-21 00:26:16 +03:00
|
|
|
setShowTooltip: (newValue: boolean) => void;
|
2024-07-26 00:34:08 +03:00
|
|
|
|
2025-01-27 15:03:48 +03:00
|
|
|
navigateTab: (tab: OssTabID) => void;
|
|
|
|
navigateOperationSchema: (target: OperationID) => void;
|
2024-06-04 23:00:22 +03:00
|
|
|
|
2025-01-27 15:03:48 +03:00
|
|
|
deleteSchema: () => void;
|
2024-07-23 23:04:21 +03:00
|
|
|
setSelected: React.Dispatch<React.SetStateAction<OperationID[]>>;
|
|
|
|
|
2024-08-15 23:23:45 +03:00
|
|
|
canDelete: (target: OperationID) => boolean;
|
2025-01-27 15:03:48 +03:00
|
|
|
promptCreateOperation: (props: ICreateOperationPrompt) => void;
|
2024-08-15 23:23:45 +03:00
|
|
|
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 {
|
2025-01-27 15:03:48 +03:00
|
|
|
itemID: LibraryItemID;
|
2024-06-04 23:00:22 +03:00
|
|
|
}
|
|
|
|
|
2025-01-27 15:03:48 +03:00
|
|
|
export const OssEditState = ({ itemID, children }: React.PropsWithChildren<OssEditStateProps>) => {
|
2024-07-24 18:11:39 +03:00
|
|
|
const router = useConceptNavigation();
|
2025-01-14 21:58:16 +03:00
|
|
|
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);
|
2025-02-04 23:55:48 +03:00
|
|
|
const setSearchLocation = useLibrarySearchStore(state => state.setLocation);
|
|
|
|
const searchLocation = useLibrarySearchStore(state => state.location);
|
2025-01-28 19:47:24 +03:00
|
|
|
|
|
|
|
const { user } = useAuthSuspense();
|
2025-01-27 15:03:48 +03:00
|
|
|
const { schema } = useOssSuspense({ itemID: itemID });
|
2024-06-04 23:00:22 +03:00
|
|
|
|
2025-01-28 19:47:24 +03:00
|
|
|
const isOwned = !!user.id && user.id === schema.owner;
|
2025-01-27 15:03:48 +03:00
|
|
|
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);
|
2025-01-27 15:03:48 +03:00
|
|
|
const [selected, setSelected] = useState<OperationID[]>([]);
|
2024-08-02 11:17:39 +03:00
|
|
|
|
2025-01-16 16:31:32 +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);
|
|
|
|
|
2025-01-27 15:03:48 +03:00
|
|
|
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
|
|
|
|
2024-12-12 21:36:46 +03:00
|
|
|
useEffect(
|
2024-06-04 23:00:22 +03:00
|
|
|
() =>
|
2025-01-15 23:03:35 +03:00
|
|
|
adjustRole({
|
2025-01-27 15:03:48 +03:00
|
|
|
isOwner: isOwned,
|
2025-01-28 19:47:24 +03:00
|
|
|
isEditor: !!user.id && schema.editors.includes(user.id),
|
|
|
|
isStaff: user.is_staff,
|
2025-01-15 23:03:35 +03:00
|
|
|
adminMode: adminMode
|
2024-06-04 23:00:22 +03:00
|
|
|
}),
|
2025-01-27 15:03:48 +03:00
|
|
|
[schema, adjustRole, isOwned, user, adminMode]
|
2024-06-04 23:00:22 +03:00
|
|
|
);
|
|
|
|
|
2025-01-27 15:03:48 +03:00
|
|
|
function navigateTab(tab: OssTabID) {
|
|
|
|
const url = urls.oss_props({
|
|
|
|
id: schema.id,
|
|
|
|
tab: tab
|
|
|
|
});
|
|
|
|
router.push(url);
|
|
|
|
}
|
2024-06-04 23:00:22 +03:00
|
|
|
|
2025-01-27 15:03:48 +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
|
|
|
|
2025-01-27 15:03:48 +03:00
|
|
|
function deleteSchema() {
|
2025-01-28 19:47:24 +03:00
|
|
|
if (!window.confirm(prompts.deleteOSS)) {
|
2025-01-27 15:03:48 +03:00
|
|
|
return;
|
|
|
|
}
|
2025-02-04 23:55:48 +03:00
|
|
|
deleteItem(schema.id, () => {
|
|
|
|
if (searchLocation === schema.location) {
|
|
|
|
setSearchLocation('');
|
|
|
|
}
|
|
|
|
router.push(urls.library);
|
|
|
|
});
|
2025-01-27 15:03:48 +03:00
|
|
|
}
|
2024-07-24 18:11:39 +03:00
|
|
|
|
2025-01-27 15:03:48 +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 => {
|
|
|
|
if (callback) {
|
|
|
|
setTimeout(() => callback(operation.id), PARAMETER.refreshTimeout);
|
2024-07-23 23:04:21 +03:00
|
|
|
}
|
|
|
|
});
|
2025-01-27 15:03:48 +03:00
|
|
|
},
|
|
|
|
initialInputs: inputs
|
|
|
|
});
|
|
|
|
}
|
2025-01-16 16:31:32 +03:00
|
|
|
|
2025-01-27 15:03:48 +03:00
|
|
|
function canDelete(target: OperationID) {
|
|
|
|
const operation = schema.operationByID.get(target);
|
|
|
|
if (!operation) {
|
|
|
|
return false;
|
2025-01-16 16:31:32 +03:00
|
|
|
}
|
2025-01-27 15:03:48 +03:00
|
|
|
if (operation.operation_type === OperationType.INPUT) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return schema.graph.expandOutputs([target]).length === 0;
|
|
|
|
}
|
2025-01-16 16:31:32 +03:00
|
|
|
|
2025-01-27 15:03:48 +03:00
|
|
|
function promptEditOperation(target: OperationID, positions: IOperationPosition[]) {
|
|
|
|
const operation = schema.operationByID.get(target);
|
|
|
|
if (!operation) {
|
2025-01-16 16:31:32 +03:00
|
|
|
return;
|
|
|
|
}
|
2025-01-27 15:03:48 +03:00
|
|
|
showEditOperation({
|
|
|
|
oss: schema,
|
|
|
|
target: operation,
|
|
|
|
onSubmit: data => {
|
|
|
|
data.positions = positions;
|
2025-01-28 19:47:24 +03:00
|
|
|
operationUpdate({ itemID: schema.id, data });
|
2025-01-16 16:31:32 +03:00
|
|
|
}
|
2025-01-27 15:03:48 +03:00
|
|
|
});
|
|
|
|
}
|
2025-01-16 16:31:32 +03:00
|
|
|
|
2025-01-27 15:03:48 +03:00
|
|
|
function promptDeleteOperation(target: OperationID, positions: IOperationPosition[]) {
|
|
|
|
const operation = schema.operationByID.get(target);
|
|
|
|
if (!operation) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
showDeleteOperation({
|
|
|
|
target: operation,
|
|
|
|
onSubmit: (targetID, keepConstituents, deleteSchema) => {
|
2025-01-28 19:47:24 +03:00
|
|
|
operationDelete({
|
|
|
|
itemID: schema.id,
|
|
|
|
data: {
|
|
|
|
target: targetID,
|
|
|
|
positions: positions,
|
|
|
|
keep_constituents: keepConstituents,
|
|
|
|
delete_schema: deleteSchema
|
|
|
|
}
|
|
|
|
});
|
2025-01-16 16:31:32 +03:00
|
|
|
}
|
2025-01-27 15:03:48 +03:00
|
|
|
});
|
|
|
|
}
|
2025-01-16 16:31:32 +03:00
|
|
|
|
2025-01-27 15:03:48 +03:00
|
|
|
function promptEditInput(target: OperationID, positions: IOperationPosition[]) {
|
|
|
|
const operation = schema.operationByID.get(target);
|
|
|
|
if (!operation) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
showEditInput({
|
|
|
|
oss: schema,
|
|
|
|
target: operation,
|
|
|
|
onSubmit: (target, newInput) => {
|
2025-01-28 19:47:24 +03:00
|
|
|
inputUpdate({
|
|
|
|
itemID: schema.id,
|
|
|
|
data: {
|
|
|
|
target: target,
|
|
|
|
positions: positions,
|
|
|
|
input: newInput ?? null
|
|
|
|
}
|
|
|
|
});
|
2025-01-16 16:31:32 +03:00
|
|
|
}
|
2025-01-27 15:03:48 +03:00
|
|
|
});
|
|
|
|
}
|
2025-01-16 16:31:32 +03:00
|
|
|
|
2025-01-27 15:03:48 +03:00
|
|
|
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;
|
|
|
|
})
|
|
|
|
) {
|
2025-01-28 19:47:24 +03:00
|
|
|
relocateConstituents({ itemID: schema.id, data });
|
2025-01-27 15:03:48 +03:00
|
|
|
} else {
|
|
|
|
updatePositions(
|
|
|
|
{
|
2025-01-28 19:47:24 +03:00
|
|
|
isSilent: true,
|
2025-01-27 15:03:48 +03:00
|
|
|
itemID: schema.id, //
|
|
|
|
positions: positions
|
|
|
|
},
|
2025-01-28 19:47:24 +03:00
|
|
|
() => relocateConstituents({ itemID: schema.id, data })
|
2025-01-27 15:03:48 +03:00
|
|
|
);
|
|
|
|
}
|
2025-01-16 16:31:32 +03:00
|
|
|
}
|
2025-01-27 15:03:48 +03:00
|
|
|
});
|
|
|
|
}
|
2025-01-16 16:31:32 +03:00
|
|
|
|
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={{
|
2025-01-27 15:03:48 +03:00
|
|
|
schema,
|
2024-07-23 23:04:21 +03:00
|
|
|
selected,
|
|
|
|
|
2025-01-27 15:03:48 +03:00
|
|
|
navigateTab,
|
|
|
|
|
|
|
|
deleteSchema,
|
|
|
|
|
2024-07-26 00:34:08 +03:00
|
|
|
showTooltip,
|
|
|
|
setShowTooltip,
|
|
|
|
|
2025-01-27 15:03:48 +03:00
|
|
|
isOwned,
|
2024-06-04 23:00:22 +03:00
|
|
|
isMutable,
|
2024-08-05 23:53:41 +03:00
|
|
|
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
|
|
|
|
2025-01-27 15:03:48 +03:00
|
|
|
navigateOperationSchema,
|
2024-07-23 23:04:21 +03:00
|
|
|
promptCreateOperation,
|
2024-08-15 23:23:45 +03:00
|
|
|
canDelete,
|
|
|
|
promptDeleteOperation,
|
2024-07-29 16:56:24 +03:00
|
|
|
promptEditInput,
|
2024-07-29 22:31:11 +03:00
|
|
|
promptEditOperation,
|
2024-10-23 15:31:24 +03:00
|
|
|
promptRelocateConstituents
|
2024-06-04 23:00:22 +03:00
|
|
|
}}
|
|
|
|
>
|
2024-06-05 12:28:08 +03:00
|
|
|
{children}
|
2024-12-12 21:58:19 +03:00
|
|
|
</OssEditContext>
|
2024-06-04 23:00:22 +03:00
|
|
|
);
|
|
|
|
};
|