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

380 lines
12 KiB
TypeScript
Raw Normal View History

2024-06-04 23:00:22 +03:00
'use client';
import { AnimatePresence } from 'framer-motion';
import { createContext, useCallback, useContext, useLayoutEffect, useMemo, useState } from 'react';
import { toast } from 'react-toastify';
2024-07-24 18:11:39 +03:00
import { urls } from '@/app/urls';
2024-06-04 23:00:22 +03:00
import { useAccessMode } from '@/context/AccessModeContext';
import { useAuth } from '@/context/AuthContext';
import { useConceptOptions } from '@/context/ConceptOptionsContext';
2024-07-24 18:11:39 +03:00
import { useConceptNavigation } from '@/context/NavigationContext';
2024-06-04 23:00:22 +03:00
import { useOSS } from '@/context/OssContext';
2024-07-28 21:30:10 +03:00
import DlgChangeInputSchema from '@/dialogs/DlgChangeInputSchema';
2024-06-04 23:00:22 +03:00
import DlgChangeLocation from '@/dialogs/DlgChangeLocation';
2024-07-21 15:19:57 +03:00
import DlgCreateOperation from '@/dialogs/DlgCreateOperation';
2024-06-04 23:00:22 +03:00
import DlgEditEditors from '@/dialogs/DlgEditEditors';
2024-07-29 16:56:24 +03:00
import DlgEditOperation from '@/dialogs/DlgEditOperation';
2024-07-28 21:30:10 +03:00
import { AccessPolicy, LibraryItemID } from '@/models/library';
2024-07-21 15:19:57 +03:00
import { Position2D } from '@/models/miscellaneous';
2024-07-28 21:30:10 +03:00
import {
IOperationCreateData,
IOperationPosition,
IOperationSchema,
IOperationSetInputData,
2024-07-29 22:31:11 +03:00
IOperationUpdateData,
2024-07-28 21:30:10 +03:00
OperationID
} from '@/models/oss';
2024-06-04 23:00:22 +03:00
import { UserID, UserLevel } from '@/models/user';
import { information } from '@/utils/labels';
2024-06-04 23:00:22 +03:00
2024-07-14 14:41:05 +03:00
export interface IOssEditContext {
2024-06-04 23:00:22 +03:00
schema?: IOperationSchema;
2024-07-23 23:04:21 +03:00
selected: OperationID[];
2024-06-04 23:00:22 +03:00
isMutable: boolean;
isProcessing: boolean;
2024-07-26 00:34:08 +03:00
showTooltip: boolean;
setShowTooltip: React.Dispatch<React.SetStateAction<boolean>>;
2024-06-04 23:00:22 +03:00
setOwner: (newOwner: UserID) => void;
setAccessPolicy: (newPolicy: AccessPolicy) => void;
promptEditors: () => void;
promptLocation: () => void;
toggleSubscribe: () => void;
2024-07-23 23:04:21 +03:00
setSelected: React.Dispatch<React.SetStateAction<OperationID[]>>;
2024-06-04 23:00:22 +03:00
share: () => void;
2024-07-21 15:19:57 +03:00
2024-07-24 18:11:39 +03:00
openOperationSchema: (target: OperationID) => void;
2024-07-23 23:04:21 +03:00
savePositions: (positions: IOperationPosition[], callback?: () => void) => void;
2024-07-21 15:19:57 +03:00
promptCreateOperation: (x: number, y: number, positions: IOperationPosition[]) => void;
2024-07-23 23:04:21 +03:00
deleteOperation: (target: OperationID, positions: IOperationPosition[]) => void;
createInput: (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;
executeOperation: (target: OperationID, positions: IOperationPosition[]) => void;
executeAll: (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) {
throw new Error('useOssEdit has to be used within <OssEditState.Provider>');
}
return context;
};
interface OssEditStateProps {
// isModified: boolean;
2024-07-23 23:04:21 +03:00
selected: OperationID[];
setSelected: React.Dispatch<React.SetStateAction<OperationID[]>>;
2024-06-04 23:00:22 +03:00
children: React.ReactNode;
}
2024-07-23 23:04:21 +03:00
export const OssEditState = ({ selected, setSelected, children }: 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 } = useConceptOptions();
const { accessLevel, setAccessLevel } = useAccessMode();
const model = useOSS();
const isMutable = useMemo(
() => accessLevel > UserLevel.READER && !model.schema?.read_only,
[accessLevel, model.schema?.read_only]
);
2024-07-26 00:34:08 +03:00
const [showTooltip, setShowTooltip] = useState(true);
2024-06-04 23:00:22 +03:00
const [showEditEditors, setShowEditEditors] = useState(false);
const [showEditLocation, setShowEditLocation] = useState(false);
2024-07-28 21:30:10 +03:00
const [showEditInput, setShowEditInput] = useState(false);
2024-07-29 16:56:24 +03:00
const [showEditOperation, setShowEditOperation] = useState(false);
2024-06-04 23:00:22 +03:00
2024-07-21 15:19:57 +03:00
const [showCreateOperation, setShowCreateOperation] = useState(false);
const [insertPosition, setInsertPosition] = useState<Position2D>({ x: 0, y: 0 });
const [positions, setPositions] = useState<IOperationPosition[]>([]);
2024-07-28 21:30:10 +03:00
const [targetOperationID, setTargetOperationID] = useState<OperationID | undefined>(undefined);
const targetOperation = useMemo(
() => (targetOperationID ? model.schema?.operationByID.get(targetOperationID) : undefined),
[model, targetOperationID]
);
2024-07-21 15:19:57 +03:00
2024-06-04 23:00:22 +03:00
useLayoutEffect(
() =>
setAccessLevel(prev => {
if (
prev === UserLevel.EDITOR &&
(model.isOwned || user?.is_staff || (user && model.schema?.editors.includes(user.id)))
) {
return UserLevel.EDITOR;
} else if (user?.is_staff && (prev === UserLevel.ADMIN || adminMode)) {
return UserLevel.ADMIN;
} else if (model.isOwned) {
return UserLevel.OWNER;
} else if (user?.id && model.schema?.editors.includes(user?.id)) {
return UserLevel.EDITOR;
} else {
return UserLevel.READER;
}
}),
[model.schema, setAccessLevel, model.isOwned, user, adminMode]
);
const handleSetLocation = useCallback(
(newLocation: string) => {
if (!model.schema) {
return;
}
model.setLocation(newLocation, () => toast.success(information.moveComplete));
2024-06-04 23:00:22 +03:00
},
[model]
);
const promptEditors = useCallback(() => {
setShowEditEditors(true);
}, []);
const promptLocation = useCallback(() => {
setShowEditLocation(true);
}, []);
const share = useCallback(() => {
const currentRef = window.location.href;
const url = currentRef.includes('?') ? currentRef + '&share' : currentRef + '?share';
navigator.clipboard
.writeText(url)
.then(() => toast.success(information.linkReady))
2024-06-04 23:00:22 +03:00
.catch(console.error);
}, []);
const toggleSubscribe = useCallback(() => {
if (model.isSubscribed) {
model.unsubscribe(() => toast.success(information.unsubscribed));
2024-06-04 23:00:22 +03:00
} else {
model.subscribe(() => toast.success(information.subscribed));
2024-06-04 23:00:22 +03:00
}
}, [model]);
const setOwner = useCallback(
(newOwner: UserID) => {
model.setOwner(newOwner, () => toast.success(information.changesSaved));
2024-06-04 23:00:22 +03:00
},
[model]
);
const setAccessPolicy = useCallback(
(newPolicy: AccessPolicy) => {
model.setAccessPolicy(newPolicy, () => toast.success(information.changesSaved));
2024-06-04 23:00:22 +03:00
},
[model]
);
const setEditors = useCallback(
(newEditors: UserID[]) => {
model.setEditors(newEditors, () => toast.success(information.changesSaved));
2024-06-04 23:00:22 +03:00
},
[model]
);
2024-07-24 18:11:39 +03:00
const openOperationSchema = useCallback(
(target: OperationID) => {
const node = model.schema?.operationByID.get(target);
if (!node || !node.result) {
return;
}
router.push(urls.schema(node.result));
},
[router, model]
);
2024-07-23 23:04:21 +03:00
const savePositions = useCallback(
(positions: IOperationPosition[], callback?: () => void) => {
model.savePositions({ positions: positions }, () => {
positions.forEach(item => {
const operation = model.schema?.operationByID.get(item.id);
if (operation) {
operation.position_x = item.position_x;
operation.position_y = item.position_y;
}
});
toast.success(information.changesSaved);
if (callback) callback();
});
},
[model]
);
2024-07-21 15:19:57 +03:00
const promptCreateOperation = useCallback((x: number, y: number, positions: IOperationPosition[]) => {
setInsertPosition({ x: x, y: y });
setPositions(positions);
setShowCreateOperation(true);
}, []);
const handleCreateOperation = useCallback(
(data: IOperationCreateData) => {
2024-07-29 16:56:24 +03:00
data.positions = positions;
data.item_data.position_x = insertPosition.x;
data.item_data.position_y = insertPosition.y;
2024-07-21 15:19:57 +03:00
model.createOperation(data, operation => toast.success(information.newOperation(operation.alias)));
},
2024-07-29 16:56:24 +03:00
[model, positions, insertPosition]
2024-07-21 15:19:57 +03:00
);
2024-07-29 16:56:24 +03:00
const promptEditOperation = useCallback((target: OperationID, positions: IOperationPosition[]) => {
setPositions(positions);
setTargetOperationID(target);
setShowEditOperation(true);
}, []);
2024-07-29 22:31:11 +03:00
const handleEditOperation = useCallback(
(data: IOperationUpdateData) => {
data.positions = positions;
model.updateOperation(data, () => toast.success(information.changesSaved));
},
[model, positions]
);
2024-07-29 16:56:24 +03:00
2024-07-23 23:04:21 +03:00
const deleteOperation = useCallback(
(target: OperationID, positions: IOperationPosition[]) => {
model.deleteOperation({ target: target, positions: positions }, () =>
toast.success(information.operationDestroyed)
);
},
[model]
);
const createInput = useCallback(
(target: OperationID, positions: IOperationPosition[]) => {
model.createInput({ target: target, positions: positions }, new_schema => {
toast.success(information.newLibraryItem);
router.push(urls.schema(new_schema.id));
});
},
[model, router]
);
2024-07-28 21:30:10 +03:00
const promptEditInput = useCallback((target: OperationID, positions: IOperationPosition[]) => {
setPositions(positions);
setTargetOperationID(target);
setShowEditInput(true);
}, []);
const setTargetInput = useCallback(
(newInput: LibraryItemID | undefined, syncText: boolean) => {
if (!targetOperationID) {
return;
}
const data: IOperationSetInputData = {
target: targetOperationID,
positions: positions,
sync_text: syncText,
input: newInput ?? null
};
model.setInput(data, () => toast.success(information.changesSaved));
},
[model, targetOperationID, positions]
);
const executeOperation = useCallback(
2024-07-29 22:31:11 +03:00
(target: OperationID, positions: IOperationPosition[]) => {
const data = {
target: target,
positions: positions
};
model.executeOperation(data, () => toast.success(information.operationExecuted));
},
[model]
);
const executeAll = useCallback(
(positions: IOperationPosition[]) => {
const data = { positions: positions };
model.executeAll(data, () => toast.success(information.allOperationExecuted));
2024-07-29 22:31:11 +03:00
},
[model]
);
2024-06-04 23:00:22 +03:00
return (
<OssEditContext.Provider
value={{
schema: model.schema,
2024-07-23 23:04:21 +03:00
selected,
2024-07-26 00:34:08 +03:00
showTooltip,
setShowTooltip,
2024-06-04 23:00:22 +03:00
isMutable,
isProcessing: model.processing,
toggleSubscribe,
setOwner,
setAccessPolicy,
promptEditors,
promptLocation,
2024-07-21 15:19:57 +03:00
share,
2024-07-23 23:04:21 +03:00
setSelected,
2024-07-21 15:19:57 +03:00
2024-07-24 18:11:39 +03:00
openOperationSchema,
2024-07-23 23:04:21 +03:00
savePositions,
promptCreateOperation,
deleteOperation,
2024-07-28 21:30:10 +03:00
createInput,
2024-07-29 16:56:24 +03:00
promptEditInput,
2024-07-29 22:31:11 +03:00
promptEditOperation,
executeOperation,
executeAll
2024-06-04 23:00:22 +03:00
}}
>
{model.schema ? (
<AnimatePresence>
{showEditEditors ? (
<DlgEditEditors
hideWindow={() => setShowEditEditors(false)}
editors={model.schema.editors}
setEditors={setEditors}
/>
) : null}
{showEditLocation ? (
<DlgChangeLocation
hideWindow={() => setShowEditLocation(false)}
initial={model.schema.location}
onChangeLocation={handleSetLocation}
/>
) : null}
2024-07-21 15:19:57 +03:00
{showCreateOperation ? (
<DlgCreateOperation
hideWindow={() => setShowCreateOperation(false)}
oss={model.schema}
onCreate={handleCreateOperation}
/>
) : null}
2024-07-28 21:30:10 +03:00
{showEditInput ? (
<DlgChangeInputSchema
hideWindow={() => setShowEditInput(false)}
oss={model.schema}
target={targetOperation!}
onSubmit={setTargetInput}
/>
) : null}
2024-07-29 16:56:24 +03:00
{showEditOperation ? (
<DlgEditOperation
hideWindow={() => setShowEditOperation(false)}
oss={model.schema}
target={targetOperation!}
onSubmit={handleEditOperation}
/>
) : null}
2024-06-04 23:00:22 +03:00
</AnimatePresence>
) : null}
{children}
2024-06-04 23:00:22 +03:00
</OssEditContext.Provider>
);
};