158 lines
5.3 KiB
JavaScript
158 lines
5.3 KiB
JavaScript
import { useCallback, useState } from "react";
|
|
import {
|
|
clampObjectToMap,
|
|
createClientObject,
|
|
getMapObjectDisplayLabel,
|
|
getObjectKey,
|
|
objectZoneId,
|
|
} from "../lib/locationMapUtils";
|
|
|
|
const EMPTY_ZONES = [];
|
|
|
|
function createHistorySnapshot(objects, selectedObjectKey) {
|
|
return {
|
|
objects: objects.map((object) => ({ ...object })),
|
|
selectedObjectKey: selectedObjectKey ?? null,
|
|
};
|
|
}
|
|
|
|
export default function useLocationMapObjectActions({
|
|
canManage,
|
|
hiddenAreaCount,
|
|
mapSize,
|
|
mapState,
|
|
objects,
|
|
selectedObject,
|
|
selectedObjectKey,
|
|
setFuture,
|
|
setHasUnsavedChanges,
|
|
setHistory,
|
|
setObjects,
|
|
setSelectedObjectKey,
|
|
toast,
|
|
}) {
|
|
const [pendingDeleteObject, setPendingDeleteObject] = useState(null);
|
|
const zones = mapState?.zones || EMPTY_ZONES;
|
|
|
|
const remember = useCallback((currentObjects = objects, currentSelectedObjectKey = selectedObjectKey) => {
|
|
setHistory((previous) => [
|
|
...previous.slice(-19),
|
|
createHistorySnapshot(currentObjects, currentSelectedObjectKey),
|
|
]);
|
|
setFuture([]);
|
|
}, [objects, selectedObjectKey, setFuture, setHistory]);
|
|
|
|
const updateObjects = useCallback((updater) => {
|
|
setHasUnsavedChanges(true);
|
|
setObjects((currentObjects) =>
|
|
updater(currentObjects).map((object) => clampObjectToMap(object, mapSize))
|
|
);
|
|
}, [mapSize, setHasUnsavedChanges, setObjects]);
|
|
|
|
const handleAddObject = useCallback(() => {
|
|
if (!canManage) return;
|
|
remember();
|
|
const usedZoneIds = new Set(objects.map((object) => objectZoneId(object)).filter(Boolean));
|
|
const nextZone = zones.find((zone) => !usedZoneIds.has(String(zone.id))) || zones[0];
|
|
const nextObject = clampObjectToMap(createClientObject(nextZone, objects.length), mapSize);
|
|
setObjects((currentObjects) => [...currentObjects, nextObject]);
|
|
setSelectedObjectKey(getObjectKey(nextObject));
|
|
setHasUnsavedChanges(true);
|
|
}, [canManage, mapSize, objects, remember, setHasUnsavedChanges, setObjects, setSelectedObjectKey, zones]);
|
|
|
|
const handleDuplicateObject = useCallback(() => {
|
|
if (!selectedObject) return;
|
|
remember();
|
|
const displayLabel = getMapObjectDisplayLabel(selectedObject, "Area");
|
|
const nextObject = {
|
|
...selectedObject,
|
|
id: undefined,
|
|
client_id: `copy-${Date.now()}`,
|
|
label: `${displayLabel} Copy`,
|
|
x: selectedObject.x + 28,
|
|
y: selectedObject.y + 28,
|
|
sort_order: objects.length + 1,
|
|
};
|
|
setObjects((currentObjects) => [...currentObjects, clampObjectToMap(nextObject, mapSize)]);
|
|
setSelectedObjectKey(getObjectKey(nextObject));
|
|
setHasUnsavedChanges(true);
|
|
}, [mapSize, objects.length, remember, selectedObject, setHasUnsavedChanges, setObjects, setSelectedObjectKey]);
|
|
|
|
const requestDeleteObject = useCallback(() => {
|
|
if (selectedObject) setPendingDeleteObject(selectedObject);
|
|
}, [selectedObject]);
|
|
|
|
const handleDeleteObject = useCallback(() => {
|
|
if (!pendingDeleteObject) return;
|
|
remember();
|
|
setObjects((currentObjects) =>
|
|
currentObjects.filter((object) => getObjectKey(object) !== getObjectKey(pendingDeleteObject))
|
|
);
|
|
setSelectedObjectKey(null);
|
|
setHasUnsavedChanges(true);
|
|
toast.success("Deleted map area", "Save the draft to keep this change.");
|
|
setPendingDeleteObject(null);
|
|
}, [pendingDeleteObject, remember, setHasUnsavedChanges, setObjects, setSelectedObjectKey, toast]);
|
|
|
|
const handleShowHiddenAreas = useCallback(() => {
|
|
if (!canManage || hiddenAreaCount === 0) return;
|
|
remember();
|
|
updateObjects((currentObjects) =>
|
|
currentObjects.map((object) => (
|
|
object.visible === false ? { ...object, visible: true } : object
|
|
))
|
|
);
|
|
toast.success("Showed hidden areas", "Save the draft to keep this change.");
|
|
}, [canManage, hiddenAreaCount, remember, toast, updateObjects]);
|
|
|
|
const handleObjectField = useCallback((field, value) => {
|
|
if (!selectedObject) return;
|
|
if (selectedObject[field] === value) return;
|
|
|
|
remember();
|
|
updateObjects((currentObjects) =>
|
|
currentObjects.map((object) => (
|
|
getObjectKey(object) === getObjectKey(selectedObject) ? { ...object, [field]: value } : object
|
|
))
|
|
);
|
|
}, [remember, selectedObject, updateObjects]);
|
|
|
|
const handleZoneLinkChange = useCallback((zoneId) => {
|
|
if (!selectedObject) return;
|
|
const zone = zones.find((candidate) => String(candidate.id) === String(zoneId));
|
|
const nextZoneId = zone?.id || null;
|
|
const nextZoneName = zone?.name || null;
|
|
const nextLabel = selectedObject.label ?? zone?.name ?? "";
|
|
if (
|
|
(selectedObject.zone_id || null) === nextZoneId &&
|
|
(selectedObject.zone_name || null) === nextZoneName &&
|
|
selectedObject.label === nextLabel
|
|
) {
|
|
return;
|
|
}
|
|
|
|
remember();
|
|
updateObjects((currentObjects) =>
|
|
currentObjects.map((object) => (
|
|
getObjectKey(object) === selectedObjectKey
|
|
? { ...object, zone_id: nextZoneId, zone_name: nextZoneName, label: object.label ?? zone?.name ?? "" }
|
|
: object
|
|
))
|
|
);
|
|
}, [remember, selectedObject, selectedObjectKey, updateObjects, zones]);
|
|
|
|
return {
|
|
handleAddObject,
|
|
handleDeleteObject,
|
|
handleDuplicateObject,
|
|
handleObjectField,
|
|
handleShowHiddenAreas,
|
|
handleZoneLinkChange,
|
|
pendingDeleteObject,
|
|
remember,
|
|
requestDeleteObject,
|
|
setPendingDeleteObject,
|
|
updateObjects,
|
|
};
|
|
}
|