feat: Implement undo and redo functionality for 2D scene controls
- Added useRedoHandler to manage redo actions, including socket communication for wall, floor, zone, and aisle updates. - Added useUndoHandler to manage undo actions, reversing the effects of previous actions with corresponding socket updates. - Created UndoRedo2DControls component to handle keyboard shortcuts for undo (Ctrl+Z) and redo (Ctrl+Y). - Established a Zustand store (useUndoRedo2DStore) to maintain undo and redo stacks, with methods for pushing, popping, and peeking actions.
This commit is contained in:
@@ -0,0 +1,355 @@
|
||||
import { useParams } from "react-router-dom";
|
||||
import { getUserData } from "../../../../../functions/getUserData";
|
||||
import { useVersionContext } from "../../../../builder/version/versionContext";
|
||||
import { useSceneContext } from "../../../sceneContext";
|
||||
import { useSocketStore } from "../../../../../store/builder/store";
|
||||
|
||||
// import { upsertWallApi } from "../../../../../services/factoryBuilder/wall/upsertWallApi";
|
||||
// import { deleteWallApi } from "../../../../../services/factoryBuilder/wall/deleteWallApi";
|
||||
|
||||
// import { upsertZoneApi } from "../../../../../services/factoryBuilder/zone/upsertZoneApi";
|
||||
// import { deleteZoneApi } from "../../../../../services/factoryBuilder/zone/deleteZoneApi";
|
||||
|
||||
// import { upsertFloorApi } from "../../../../../services/factoryBuilder/floor/upsertFloorApi";
|
||||
// import { deleteFloorApi } from "../../../../../services/factoryBuilder/floor/deleteFloorApi";
|
||||
|
||||
// import { upsertAisleApi } from "../../../../../services/factoryBuilder/aisle/upsertAisleApi";
|
||||
// import { deleteAisleApi } from "../../../../../services/factoryBuilder/aisle/deleteAisleApi";
|
||||
|
||||
function useRedoHandler() {
|
||||
const { undoRedo2DStore, wallStore, floorStore, zoneStore, aisleStore } = useSceneContext();
|
||||
const { redo2D, peekRedo2D } = undoRedo2DStore();
|
||||
const { addWall, removeWall, updateWall } = wallStore();
|
||||
const { addFloor, removeFloor, updateFloor } = floorStore();
|
||||
const { addZone, removeZone, updateZone } = zoneStore();
|
||||
const { addAisle, removeAisle, updateAisle } = aisleStore();
|
||||
const { selectedVersionStore } = useVersionContext();
|
||||
const { selectedVersion } = selectedVersionStore();
|
||||
const { userId, organization } = getUserData();
|
||||
const { projectId } = useParams();
|
||||
const { socket } = useSocketStore();
|
||||
|
||||
const handleRedo = () => {
|
||||
const redoData = peekRedo2D();
|
||||
if (!redoData) return;
|
||||
|
||||
if (redoData.type === 'Draw') {
|
||||
const { actions } = redoData;
|
||||
|
||||
actions.forEach(action => {
|
||||
const { actionType } = action;
|
||||
|
||||
if ('point' in action) {
|
||||
const point = action.point;
|
||||
|
||||
if (actionType === 'Line-Create') {
|
||||
handleCreate(point);
|
||||
} else if (actionType === 'Line-Update') {
|
||||
handleUpdate(point);
|
||||
} else if (actionType === 'Line-Delete') {
|
||||
handleRemove(point);
|
||||
}
|
||||
|
||||
} else if ('points' in action) {
|
||||
const points = action.points;
|
||||
|
||||
if (actionType === 'Lines-Create') {
|
||||
points.forEach(handleCreate);
|
||||
} else if (actionType === 'Lines-Update') {
|
||||
points.forEach(handleUpdate);
|
||||
} else if (actionType === 'Lines-Delete') {
|
||||
points.forEach(handleRemove);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else if (redoData.type === 'UI') {
|
||||
// Handle UI actions if needed
|
||||
}
|
||||
|
||||
redo2D();
|
||||
};
|
||||
|
||||
const handleCreate = (point: UndoRedo2DDataTypeSchema) => {
|
||||
switch (point.type) {
|
||||
case 'Wall': createWallFromBackend(point.lineData); break;
|
||||
case 'Floor': createFloorFromBackend(point.lineData); break;
|
||||
case 'Zone': createZoneFromBackend(point.lineData); break;
|
||||
case 'Aisle': createAisleFromBackend(point.lineData); break;
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemove = (point: UndoRedo2DDataTypeSchema) => {
|
||||
switch (point.type) {
|
||||
case 'Wall': removeWallFromBackend(point.lineData.wallUuid); break;
|
||||
case 'Floor': removeFloorFromBackend(point.lineData.floorUuid); break;
|
||||
case 'Zone': removeZoneFromBackend(point.lineData.zoneUuid); break;
|
||||
case 'Aisle': removeAisleFromBackend(point.lineData.aisleUuid); break;
|
||||
}
|
||||
};
|
||||
|
||||
const handleUpdate = (point: UndoRedo2DDataTypeSchema) => {
|
||||
if (!point.newData) return;
|
||||
switch (point.type) {
|
||||
case 'Wall': updateWallFromBackend(point.newData.wallUuid, point.newData); break;
|
||||
case 'Floor': updateFloorFromBackend(point.newData.floorUuid, point.newData); break;
|
||||
case 'Zone': updateZoneFromBackend(point.newData.zoneUuid, point.newData); break;
|
||||
case 'Aisle': updateAisleFromBackend(point.newData.aisleUuid, point.newData); break;
|
||||
}
|
||||
};
|
||||
|
||||
const createWallFromBackend = (wallData: Wall) => {
|
||||
addWall(wallData);
|
||||
if (projectId) {
|
||||
// API
|
||||
|
||||
// upsertWallApi(projectId, selectedVersion?.versionId || '', wallData);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
wallData: wallData,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Wall:add', data);
|
||||
}
|
||||
};
|
||||
|
||||
const removeWallFromBackend = (wallUuid: string) => {
|
||||
removeWall(wallUuid);
|
||||
if (projectId) {
|
||||
// API
|
||||
|
||||
// deleteWallApi(projectId, selectedVersion?.versionId || '', wallUuid);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
wallUuid: wallUuid,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Wall:delete', data);
|
||||
}
|
||||
};
|
||||
|
||||
const updateWallFromBackend = (wallUuid: string, updatedData: Wall) => {
|
||||
updateWall(wallUuid, updatedData);
|
||||
if (projectId) {
|
||||
// API
|
||||
|
||||
// upsertWallApi(projectId, selectedVersion?.versionId || '', updatedData);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
wallData: updatedData,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Wall:add', data);
|
||||
}
|
||||
};
|
||||
|
||||
const createFloorFromBackend = (floorData: Floor) => {
|
||||
addFloor(floorData);
|
||||
if (projectId) {
|
||||
// API
|
||||
|
||||
// upsertFloorApi(projectId, selectedVersion?.versionId || '', floorData);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
floorData: floorData,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Floor:add', data);
|
||||
}
|
||||
};
|
||||
|
||||
const removeFloorFromBackend = (floorUuid: string) => {
|
||||
removeFloor(floorUuid);
|
||||
if (projectId) {
|
||||
// API
|
||||
|
||||
// deleteFloorApi(projectId, selectedVersion?.versionId || '', floorUuid);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
floorUuid: floorUuid,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Floor:delete', data);
|
||||
}
|
||||
};
|
||||
|
||||
const updateFloorFromBackend = (floorUuid: string, updatedData: Floor) => {
|
||||
updateFloor(floorUuid, updatedData);
|
||||
if (projectId) {
|
||||
// API
|
||||
|
||||
// upsertFloorApi(projectId, selectedVersion?.versionId || '', updatedData);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
floorData: updatedData,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:model-Floor:add', data);
|
||||
}
|
||||
};
|
||||
|
||||
const createZoneFromBackend = (zoneData: Zone) => {
|
||||
addZone(zoneData);
|
||||
if (projectId) {
|
||||
// API
|
||||
|
||||
// upsertZoneApi(projectId, selectedVersion?.versionId || '', zoneData);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
zoneData: zoneData,
|
||||
projectId: projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId: userId,
|
||||
organization: organization
|
||||
}
|
||||
|
||||
socket.emit('v1:zone:add', data);
|
||||
}
|
||||
};
|
||||
|
||||
const removeZoneFromBackend = (zoneUuid: string) => {
|
||||
removeZone(zoneUuid);
|
||||
if (projectId) {
|
||||
// API
|
||||
|
||||
// deleteZoneApi(projectId, selectedVersion?.versionId || '', zoneUuid);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
zoneUuid,
|
||||
projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId,
|
||||
organization
|
||||
};
|
||||
|
||||
socket.emit('v1:zone:delete', data);
|
||||
}
|
||||
};
|
||||
|
||||
const updateZoneFromBackend = (zoneUuid: string, updatedData: Zone) => {
|
||||
updateZone(zoneUuid, updatedData);
|
||||
if (projectId) {
|
||||
// API
|
||||
|
||||
// upsertZoneApi(projectId, selectedVersion?.versionId || '', updatedData);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
zoneData: updatedData,
|
||||
projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId,
|
||||
organization
|
||||
};
|
||||
|
||||
socket.emit('v1:zone:add', data);
|
||||
}
|
||||
};
|
||||
|
||||
const createAisleFromBackend = (aisleData: Aisle) => {
|
||||
addAisle(aisleData);
|
||||
if (projectId) {
|
||||
// API
|
||||
|
||||
// upsertAisleApi(projectId, selectedVersion?.versionId || '', aisleData);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
aisleData,
|
||||
projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId,
|
||||
organization
|
||||
};
|
||||
|
||||
socket.emit('v1:model-aisle:add', data);
|
||||
}
|
||||
};
|
||||
|
||||
const removeAisleFromBackend = (aisleUuid: string) => {
|
||||
removeAisle(aisleUuid);
|
||||
if (projectId) {
|
||||
// API
|
||||
|
||||
// deleteAisleApi(projectId, selectedVersion?.versionId || '', aisleUuid);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
aisleUuid,
|
||||
projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId,
|
||||
organization
|
||||
};
|
||||
|
||||
socket.emit('v1:model-aisle:delete', data);
|
||||
}
|
||||
};
|
||||
|
||||
const updateAisleFromBackend = (aisleUuid: string, updatedData: Aisle) => {
|
||||
updateAisle(aisleUuid, updatedData);
|
||||
if (projectId) {
|
||||
// API
|
||||
|
||||
// upsertAisleApi(projectId, selectedVersion?.versionId || '', updatedData);
|
||||
|
||||
// SOCKET
|
||||
|
||||
const data = {
|
||||
aisleData: updatedData,
|
||||
projectId,
|
||||
versionId: selectedVersion?.versionId || '',
|
||||
userId,
|
||||
organization
|
||||
};
|
||||
|
||||
socket.emit('v1:model-aisle:add', data);
|
||||
}
|
||||
};
|
||||
|
||||
return { handleRedo };
|
||||
}
|
||||
|
||||
export default useRedoHandler;
|
||||
Reference in New Issue
Block a user