import { useCallback, useEffect, useRef, useState } from 'react'; import { usePersistedState } from '../../shared/hooks'; import type { VesselTypeCode } from '../../entities/vessel/model/types'; import type { MapToggleState } from '../../features/mapToggles/MapToggles'; import type { LegacyAlarmKind } from '../../features/legacyDashboard/model/types'; import { LEGACY_ALARM_KINDS } from '../../features/legacyDashboard/model/types'; import type { BaseMapId, Map3DSettings, MapProjectionId } from '../../widgets/map3d/Map3D'; import type { MapViewState } from '../../widgets/map3d/types'; import { DEFAULT_MAP_STYLE_SETTINGS } from '../../features/mapSettings/types'; import type { MapStyleSettings } from '../../features/mapSettings/types'; import { fmtDateTimeFull } from '../../shared/lib/datetime'; export type Bbox = [number, number, number, number]; export type FleetRelationSortMode = 'count' | 'range'; export function useDashboardState(uid: number | null) { // ── Map instance ── const [mapInstance, setMapInstance] = useState(null); const handleMapReady = useCallback((map: import('maplibre-gl').Map) => setMapInstance(map), []); // ── Viewport / API BBox ── const [viewBbox, setViewBbox] = useState(null); const [useViewportFilter, setUseViewportFilter] = useState(false); const [useApiBbox, setUseApiBbox] = useState(false); const [apiBbox, setApiBbox] = useState(undefined); // ── Selection & hover ── const [selectedMmsi, setSelectedMmsi] = useState(null); const [highlightedMmsiSet, setHighlightedMmsiSet] = useState([]); const [hoveredMmsiSet, setHoveredMmsiSet] = useState([]); const [hoveredFleetMmsiSet, setHoveredFleetMmsiSet] = useState([]); const [hoveredPairMmsiSet, setHoveredPairMmsiSet] = useState([]); const [hoveredFleetOwnerKey, setHoveredFleetOwnerKey] = useState(null); // ── Filters (persisted) ── const [typeEnabled, setTypeEnabled] = usePersistedState>( uid, 'typeEnabled', { PT: true, 'PT-S': true, GN: true, OT: true, PS: true, FC: true }, ); const [showTargets, setShowTargets] = usePersistedState(uid, 'showTargets', true); const [showOthers, setShowOthers] = usePersistedState(uid, 'showOthers', true); // ── Map settings (persisted) ── // 레거시 베이스맵 비활성 — 향후 위성/라이트 등 추가 시 재활용 // eslint-disable-next-line @typescript-eslint/no-unused-vars const [baseMap, _setBaseMap] = useState('enhanced'); const [projection, setProjection] = useState('mercator'); const [mapStyleSettings, setMapStyleSettings] = usePersistedState(uid, 'mapStyleSettings', DEFAULT_MAP_STYLE_SETTINGS); const [overlays, setOverlays] = usePersistedState(uid, 'overlays', { pairLines: true, pairRange: true, fcLines: true, zones: true, fleetCircles: true, predictVectors: true, shipLabels: true, subcables: false, }); const [settings, setSettings] = usePersistedState(uid, 'map3dSettings', { showShips: true, showDensity: false, showSeamark: false, }); const [mapView, setMapView] = usePersistedState(uid, 'mapView', null); // ── Sort & alarm filters (persisted) ── const [fleetRelationSortMode, setFleetRelationSortMode] = usePersistedState(uid, 'sortMode', 'count'); const [alarmKindEnabled, setAlarmKindEnabled] = usePersistedState>( uid, 'alarmKindEnabled', () => Object.fromEntries(LEGACY_ALARM_KINDS.map((k) => [k, true])) as Record, ); // ── Fleet focus ── const [fleetFocus, setFleetFocus] = useState<{ id: string | number; center: [number, number]; zoom?: number } | undefined>(undefined); // ── Cable ── const [hoveredCableId, setHoveredCableId] = useState(null); const [selectedCableId, setSelectedCableId] = useState(null); // ── Track context menu ── const [trackContextMenu, setTrackContextMenu] = useState<{ x: number; y: number; mmsi: number; vesselName: string } | null>(null); const handleOpenTrackMenu = useCallback((info: { x: number; y: number; mmsi: number; vesselName: string }) => setTrackContextMenu(info), []); const handleCloseTrackMenu = useCallback(() => setTrackContextMenu(null), []); // ── Projection loading ── const [isProjectionLoading, setIsProjectionLoading] = useState(false); const [isGlobeShipsReady, setIsGlobeShipsReady] = useState(false); const handleProjectionLoadingChange = useCallback((loading: boolean) => setIsProjectionLoading(loading), []); const showMapLoader = isProjectionLoading; const isProjectionToggleDisabled = !isGlobeShipsReady || isProjectionLoading; // ── Clock ── const [clock, setClock] = useState(() => fmtDateTimeFull(new Date())); useEffect(() => { const id = window.setInterval(() => setClock(fmtDateTimeFull(new Date())), 1000); return () => window.clearInterval(id); }, []); // ── Admin mode (7 clicks within 900ms) ── const [adminMode, setAdminMode] = useState(false); const clicksRef = useRef([]); const onLogoClick = () => { const now = Date.now(); clicksRef.current = clicksRef.current.filter((t) => now - t < 900); clicksRef.current.push(now); if (clicksRef.current.length >= 7) { clicksRef.current = []; setAdminMode((v) => !v); } }; // ── Helpers ── const setUniqueSorted = (items: number[]) => Array.from(new Set(items.filter((item) => Number.isFinite(item)))).sort((a, b) => a - b); const setSortedIfChanged = (next: number[]) => { const sorted = setUniqueSorted(next); return (prev: number[]) => (prev.length === sorted.length && prev.every((v, i) => v === sorted[i]) ? prev : sorted); }; const toggleHighlightedMmsi = (mmsi: number) => { setHighlightedMmsiSet((prev) => { const s = new Set(prev); if (s.has(mmsi)) s.delete(mmsi); else s.add(mmsi); return Array.from(s).sort((a, b) => a - b); }); }; return { mapInstance, handleMapReady, viewBbox, setViewBbox, useViewportFilter, setUseViewportFilter, useApiBbox, setUseApiBbox, apiBbox, setApiBbox, selectedMmsi, setSelectedMmsi, highlightedMmsiSet, hoveredMmsiSet, setHoveredMmsiSet, hoveredFleetMmsiSet, setHoveredFleetMmsiSet, hoveredPairMmsiSet, setHoveredPairMmsiSet, hoveredFleetOwnerKey, setHoveredFleetOwnerKey, typeEnabled, setTypeEnabled, showTargets, setShowTargets, showOthers, setShowOthers, baseMap, projection, setProjection, mapStyleSettings, setMapStyleSettings, overlays, setOverlays, settings, setSettings, mapView, setMapView, fleetRelationSortMode, setFleetRelationSortMode, alarmKindEnabled, setAlarmKindEnabled, fleetFocus, setFleetFocus, hoveredCableId, setHoveredCableId, selectedCableId, setSelectedCableId, trackContextMenu, handleOpenTrackMenu, handleCloseTrackMenu, handleProjectionLoadingChange, isGlobeShipsReady, setIsGlobeShipsReady, showMapLoader, isProjectionToggleDisabled, clock, adminMode, onLogoClick, setUniqueSorted, setSortedIfChanged, toggleHighlightedMmsi, }; }