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, }; }