137 lines
4.8 KiB
JavaScript
137 lines
4.8 KiB
JavaScript
import { useCallback, useEffect, useRef, useState } from "react";
|
|
import { getLocationMap } from "../api/locationMaps";
|
|
import getApiErrorMessage from "../lib/getApiErrorMessage";
|
|
import { getDefaultMapDraft, getMapDraftSnapshot } from "../lib/locationMapDraftStateUtils";
|
|
import { DEFAULT_MAP_FILTERS } from "../lib/locationMapUtils";
|
|
|
|
export default function useLocationMapDraftState({
|
|
activeHousehold,
|
|
hasLoaded,
|
|
householdLoading,
|
|
locationId,
|
|
toast,
|
|
}) {
|
|
const [mapState, setMapState] = useState(null);
|
|
const [loading, setLoading] = useState(true);
|
|
const [loadError, setLoadError] = useState(null);
|
|
const [saving, setSaving] = useState(false);
|
|
const [savingAction, setSavingAction] = useState(null);
|
|
const [mode, setMode] = useState("setup");
|
|
const [editorTool, setEditorTool] = useState("pan");
|
|
const [previewDraft, setPreviewDraft] = useState(false);
|
|
const [mapDraft, setMapDraft] = useState(getDefaultMapDraft);
|
|
const [objects, setObjects] = useState([]);
|
|
const [selectedObjectKey, setSelectedObjectKey] = useState(null);
|
|
const [filters, setFilters] = useState(DEFAULT_MAP_FILTERS);
|
|
const [layersOpen, setLayersOpen] = useState(false);
|
|
const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
|
|
const [zoom, setZoom] = useState(0.75);
|
|
const [history, setHistory] = useState([]);
|
|
const [future, setFuture] = useState([]);
|
|
const [dragState, setDragState] = useState(null);
|
|
const toastRef = useRef(toast);
|
|
const loadRequestRef = useRef(0);
|
|
|
|
const resetDraftState = useCallback(() => {
|
|
setObjects([]);
|
|
setSelectedObjectKey(null);
|
|
setHistory([]);
|
|
setFuture([]);
|
|
setHasUnsavedChanges(false);
|
|
setMapDraft(getDefaultMapDraft());
|
|
}, []);
|
|
|
|
const syncMapDraftFromState = useCallback((nextState, nextMode, nextPreviewDraft, options = {}) => {
|
|
const snapshot = getMapDraftSnapshot(nextState, nextMode, nextPreviewDraft, options);
|
|
if (!snapshot) return;
|
|
|
|
setMapDraft(snapshot.mapDraft);
|
|
setObjects(snapshot.objects);
|
|
setSelectedObjectKey(snapshot.selectedObjectKey);
|
|
setHistory([]);
|
|
setFuture([]);
|
|
setHasUnsavedChanges(false);
|
|
}, []);
|
|
|
|
const loadMap = useCallback(async () => {
|
|
const requestId = loadRequestRef.current + 1;
|
|
loadRequestRef.current = requestId;
|
|
|
|
if (!activeHousehold?.id || !locationId) {
|
|
setLoading(false);
|
|
return;
|
|
}
|
|
|
|
setLoading(true);
|
|
setLoadError(null);
|
|
setSaving(false);
|
|
setSavingAction(null);
|
|
setFilters({ ...DEFAULT_MAP_FILTERS });
|
|
setLayersOpen(false);
|
|
try {
|
|
const response = await getLocationMap(activeHousehold.id, locationId);
|
|
if (loadRequestRef.current !== requestId) return;
|
|
|
|
const nextState = response.data;
|
|
const nextCanManage = Boolean(
|
|
nextState.can_manage ?? ["owner", "admin"].includes(activeHousehold?.role)
|
|
);
|
|
setMapState(nextState);
|
|
setEditorTool("pan");
|
|
|
|
if (!nextState.draft_map && !nextState.published_map) {
|
|
setMode("setup");
|
|
setPreviewDraft(false);
|
|
resetDraftState();
|
|
return;
|
|
}
|
|
|
|
if (nextState.published_map) {
|
|
setMode("view");
|
|
setPreviewDraft(false);
|
|
syncMapDraftFromState(nextState, "view", false);
|
|
} else if (nextState.draft_map && nextCanManage) {
|
|
setMode("setup");
|
|
setPreviewDraft(true);
|
|
syncMapDraftFromState(nextState, "edit", true);
|
|
} else {
|
|
setMode("setup");
|
|
setPreviewDraft(false);
|
|
resetDraftState();
|
|
}
|
|
} catch (error) {
|
|
if (loadRequestRef.current !== requestId) return;
|
|
|
|
const message = getApiErrorMessage(error, "Failed to load map");
|
|
toastRef.current.error("Load map failed", message);
|
|
setMapState(null);
|
|
resetDraftState();
|
|
setLoadError(message);
|
|
} finally {
|
|
if (loadRequestRef.current === requestId) {
|
|
setLoading(false);
|
|
}
|
|
}
|
|
}, [activeHousehold?.id, activeHousehold?.role, locationId, resetDraftState, syncMapDraftFromState]);
|
|
|
|
useEffect(() => { toastRef.current = toast; }, [toast]);
|
|
|
|
useEffect(() => {
|
|
if (!householdLoading && hasLoaded) {
|
|
loadMap();
|
|
}
|
|
}, [hasLoaded, householdLoading, loadMap]);
|
|
|
|
const beginSaving = useCallback((action) => { setSavingAction(action); setSaving(true); }, []);
|
|
|
|
const endSaving = useCallback(() => { setSaving(false); setSavingAction(null); }, []);
|
|
|
|
return {
|
|
beginSaving, dragState, editorTool, endSaving, filters, future, hasUnsavedChanges, history, layersOpen,
|
|
loadError, loadMap, loading, mapDraft, mapState, mode, objects, previewDraft, saving,
|
|
savingAction, selectedObjectKey, setDragState, setEditorTool, setFilters, setFuture,
|
|
setHasUnsavedChanges, setHistory, setLayersOpen, setMapState, setMode, setObjects,
|
|
setPreviewDraft, setSelectedObjectKey, setZoom, syncMapDraftFromState, zoom,
|
|
};
|
|
}
|