2024-06-07 20:17:03 +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:28 +03:00
|
|
|
import { urls } from '@/app/urls';
|
2024-06-07 20:17:03 +03:00
|
|
|
import { useAccessMode } from '@/context/AccessModeContext';
|
|
|
|
import { useAuth } from '@/context/AuthContext';
|
2024-06-26 19:47:05 +03:00
|
|
|
import { useConceptOptions } from '@/context/ConceptOptionsContext';
|
2024-07-24 18:11:28 +03:00
|
|
|
import { useConceptNavigation } from '@/context/NavigationContext';
|
2024-06-07 20:17:03 +03:00
|
|
|
import { useOSS } from '@/context/OssContext';
|
|
|
|
import DlgChangeLocation from '@/dialogs/DlgChangeLocation';
|
2024-07-21 15:17:36 +03:00
|
|
|
import DlgCreateOperation from '@/dialogs/DlgCreateOperation';
|
2024-06-07 20:17:03 +03:00
|
|
|
import DlgEditEditors from '@/dialogs/DlgEditEditors';
|
|
|
|
import { AccessPolicy } from '@/models/library';
|
2024-07-20 18:26:32 +03:00
|
|
|
import { Position2D } from '@/models/miscellaneous';
|
2024-07-23 23:03:58 +03:00
|
|
|
import { IOperationCreateData, IOperationPosition, IOperationSchema, OperationID } from '@/models/oss';
|
2024-06-07 20:17:03 +03:00
|
|
|
import { UserID, UserLevel } from '@/models/user';
|
|
|
|
import { information } from '@/utils/labels';
|
|
|
|
|
2024-06-27 14:43:06 +03:00
|
|
|
export interface IOssEditContext {
|
2024-06-07 20:17:03 +03:00
|
|
|
schema?: IOperationSchema;
|
2024-07-23 23:03:58 +03:00
|
|
|
selected: OperationID[];
|
2024-06-07 20:17:03 +03:00
|
|
|
|
|
|
|
isMutable: boolean;
|
|
|
|
isProcessing: boolean;
|
|
|
|
|
2024-07-26 00:33:22 +03:00
|
|
|
showTooltip: boolean;
|
|
|
|
setShowTooltip: React.Dispatch<React.SetStateAction<boolean>>;
|
|
|
|
|
2024-06-07 20:17:03 +03:00
|
|
|
setOwner: (newOwner: UserID) => void;
|
|
|
|
setAccessPolicy: (newPolicy: AccessPolicy) => void;
|
|
|
|
promptEditors: () => void;
|
|
|
|
promptLocation: () => void;
|
|
|
|
toggleSubscribe: () => void;
|
|
|
|
|
2024-07-23 23:03:58 +03:00
|
|
|
setSelected: React.Dispatch<React.SetStateAction<OperationID[]>>;
|
|
|
|
|
2024-06-07 20:17:03 +03:00
|
|
|
share: () => void;
|
2024-07-20 18:26:32 +03:00
|
|
|
|
2024-07-24 18:11:28 +03:00
|
|
|
openOperationSchema: (target: OperationID) => void;
|
|
|
|
|
2024-07-23 23:03:58 +03:00
|
|
|
savePositions: (positions: IOperationPosition[], callback?: () => void) => void;
|
2024-07-21 15:17:36 +03:00
|
|
|
promptCreateOperation: (x: number, y: number, positions: IOperationPosition[]) => void;
|
2024-07-23 23:03:58 +03:00
|
|
|
deleteOperation: (target: OperationID, positions: IOperationPosition[]) => void;
|
2024-07-28 00:37:33 +03:00
|
|
|
createInput: (target: OperationID, positions: IOperationPosition[]) => void;
|
2024-06-07 20:17:03 +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:03:58 +03:00
|
|
|
selected: OperationID[];
|
|
|
|
setSelected: React.Dispatch<React.SetStateAction<OperationID[]>>;
|
2024-06-07 20:17:03 +03:00
|
|
|
children: React.ReactNode;
|
|
|
|
}
|
|
|
|
|
2024-07-23 23:03:58 +03:00
|
|
|
export const OssEditState = ({ selected, setSelected, children }: OssEditStateProps) => {
|
2024-07-24 18:11:28 +03:00
|
|
|
const router = useConceptNavigation();
|
2024-06-07 20:17:03 +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:33:22 +03:00
|
|
|
const [showTooltip, setShowTooltip] = useState(true);
|
|
|
|
|
2024-06-07 20:17:03 +03:00
|
|
|
const [showEditEditors, setShowEditEditors] = useState(false);
|
|
|
|
const [showEditLocation, setShowEditLocation] = useState(false);
|
|
|
|
|
2024-07-20 18:26:32 +03:00
|
|
|
const [showCreateOperation, setShowCreateOperation] = useState(false);
|
|
|
|
const [insertPosition, setInsertPosition] = useState<Position2D>({ x: 0, y: 0 });
|
2024-07-21 15:17:36 +03:00
|
|
|
const [positions, setPositions] = useState<IOperationPosition[]>([]);
|
2024-07-20 18:26:32 +03:00
|
|
|
|
2024-06-07 20:17:03 +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));
|
|
|
|
},
|
|
|
|
[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))
|
|
|
|
.catch(console.error);
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
const toggleSubscribe = useCallback(() => {
|
|
|
|
if (model.isSubscribed) {
|
|
|
|
model.unsubscribe(() => toast.success(information.unsubscribed));
|
|
|
|
} else {
|
|
|
|
model.subscribe(() => toast.success(information.subscribed));
|
|
|
|
}
|
|
|
|
}, [model]);
|
|
|
|
|
|
|
|
const setOwner = useCallback(
|
|
|
|
(newOwner: UserID) => {
|
|
|
|
model.setOwner(newOwner, () => toast.success(information.changesSaved));
|
|
|
|
},
|
|
|
|
[model]
|
|
|
|
);
|
|
|
|
|
|
|
|
const setAccessPolicy = useCallback(
|
|
|
|
(newPolicy: AccessPolicy) => {
|
|
|
|
model.setAccessPolicy(newPolicy, () => toast.success(information.changesSaved));
|
|
|
|
},
|
|
|
|
[model]
|
|
|
|
);
|
|
|
|
|
|
|
|
const setEditors = useCallback(
|
|
|
|
(newEditors: UserID[]) => {
|
|
|
|
model.setEditors(newEditors, () => toast.success(information.changesSaved));
|
|
|
|
},
|
|
|
|
[model]
|
|
|
|
);
|
|
|
|
|
2024-07-24 18:11:28 +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:03:58 +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:17:36 +03:00
|
|
|
const promptCreateOperation = useCallback((x: number, y: number, positions: IOperationPosition[]) => {
|
2024-07-20 18:26:32 +03:00
|
|
|
setInsertPosition({ x: x, y: y });
|
2024-07-21 15:17:36 +03:00
|
|
|
setPositions(positions);
|
2024-07-20 18:26:32 +03:00
|
|
|
setShowCreateOperation(true);
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
const handleCreateOperation = useCallback(
|
|
|
|
(data: IOperationCreateData) => {
|
|
|
|
model.createOperation(data, operation => toast.success(information.newOperation(operation.alias)));
|
|
|
|
},
|
|
|
|
[model]
|
|
|
|
);
|
|
|
|
|
2024-07-23 23:03:58 +03:00
|
|
|
const deleteOperation = useCallback(
|
|
|
|
(target: OperationID, positions: IOperationPosition[]) => {
|
|
|
|
model.deleteOperation({ target: target, positions: positions }, () =>
|
|
|
|
toast.success(information.operationDestroyed)
|
|
|
|
);
|
|
|
|
},
|
|
|
|
[model]
|
|
|
|
);
|
|
|
|
|
2024-07-28 00:37:33 +03:00
|
|
|
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-06-07 20:17:03 +03:00
|
|
|
return (
|
|
|
|
<OssEditContext.Provider
|
|
|
|
value={{
|
|
|
|
schema: model.schema,
|
2024-07-23 23:03:58 +03:00
|
|
|
selected,
|
|
|
|
|
2024-07-26 00:33:22 +03:00
|
|
|
showTooltip,
|
|
|
|
setShowTooltip,
|
|
|
|
|
2024-06-07 20:17:03 +03:00
|
|
|
isMutable,
|
|
|
|
isProcessing: model.processing,
|
|
|
|
|
|
|
|
toggleSubscribe,
|
|
|
|
setOwner,
|
|
|
|
setAccessPolicy,
|
|
|
|
promptEditors,
|
|
|
|
promptLocation,
|
|
|
|
|
2024-07-20 18:26:32 +03:00
|
|
|
share,
|
2024-07-23 23:03:58 +03:00
|
|
|
setSelected,
|
2024-07-20 18:26:32 +03:00
|
|
|
|
2024-07-24 18:11:28 +03:00
|
|
|
openOperationSchema,
|
2024-07-23 23:03:58 +03:00
|
|
|
savePositions,
|
|
|
|
promptCreateOperation,
|
2024-07-28 00:37:33 +03:00
|
|
|
deleteOperation,
|
|
|
|
createInput
|
2024-06-07 20:17:03 +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:17:36 +03:00
|
|
|
{showCreateOperation ? (
|
|
|
|
<DlgCreateOperation
|
|
|
|
hideWindow={() => setShowCreateOperation(false)}
|
|
|
|
oss={model.schema}
|
|
|
|
positions={positions}
|
|
|
|
insertPosition={insertPosition}
|
|
|
|
onCreate={handleCreateOperation}
|
|
|
|
/>
|
|
|
|
) : null}
|
2024-06-07 20:17:03 +03:00
|
|
|
</AnimatePresence>
|
|
|
|
) : null}
|
|
|
|
|
|
|
|
{children}
|
|
|
|
</OssEditContext.Provider>
|
|
|
|
);
|
|
|
|
};
|