"use client"; import { fitMapSelection, mapSearchNavigationOptions } from "@/features/map-search/lib/map-navigation"; import type { useFloodPredictionMap } from "@/features/fldng-predc-prst/lib/use-flood-prediction-map"; import { useEffect, type MutableRefObject, type Dispatch, type SetStateAction, } from "react"; import GisApp from "@common/business_components/ui/map/lib/gis.core"; import { WINDOW_POPUP_MESSAGE_TYPE } from "@/features/window-popup/model/types"; type MapRuntimeBridgeOptions = { /** 공통 Gis가 생성한 지도 참조. 생성/파괴 소유권은 Gis에 유지한다. */ gisMaps: MutableRefObject; setMapReady: Dispatch>; handleFloodPredictionPopupSelection: ReturnType< typeof useFloodPredictionMap >["handleFloodPredictionPopupSelection"]; }; /** 지도 준비 감지와 정보검색/GIS 업로드 메시지 연결을 소유한다. * 기존 300ms 준비 polling 및 callback 변경 시 구독 갱신/정리 순서를 보존한다. * 지도 객체 자체를 별도로 만들거나 전역 store에 저장하지 않는다. */ export function useMapRuntimeBridge({ gisMaps, setMapReady, handleFloodPredictionPopupSelection, }: MapRuntimeBridgeOptions) { useEffect(() => { const timer = window.setInterval(() => { if (gisMaps.current) { setMapReady(true); window.clearInterval(timer); } }, 300); const handlePopupMessage = async (event: MessageEvent) => { if (event.origin !== window.location.origin) return; const data = event.data; const core = GisApp.Module.layer as any; if (data?.type === WINDOW_POPUP_MESSAGE_TYPE.MAP_SEARCH_SELECT_FEATURE) { const layerNm = data.payload?.layerNm; const fid = data.payload?.fid; if (!layerNm || !fid || !core?.showSearchResultsFromGeoJSON) return; await core.showSearchResultsFromGeoJSON(layerNm, fid, mapSearchNavigationOptions(gisMaps.current)); return; } if (data?.type === WINDOW_POPUP_MESSAGE_TYPE.MAP_SEARCH_SHOW_MULTIPLE) { const layerNm = data.payload?.layerNm; const fidsRaw = data.payload?.fids; if (!layerNm || !fidsRaw || !core?.showMultipleSearchResultsFromGeoJSON) return; let fids: string[] = []; try { fids = JSON.parse(fidsRaw); } catch (error) { console.error("다중 시설 메시지 파싱 실패:", error); return; } await core.showMultipleSearchResultsFromGeoJSON(layerNm, fids, mapSearchNavigationOptions(gisMaps.current)); return; } if (data?.type === WINDOW_POPUP_MESSAGE_TYPE.MAP_SEARCH_FIT_EXTENT) { const extent = Array.isArray(data.payload?.extent) ? data.payload.extent.map(Number) : []; const map = gisMaps.current; if ( extent.length !== 4 || extent.some((value: number) => !Number.isFinite(value)) || !map?.getView ) { return; } fitMapSelection(map, extent, { duration: 300, padding: [50, 50, 50, 50], maxZoom: 16, }); return; } if ( data?.type === WINDOW_POPUP_MESSAGE_TYPE.MAP_SEARCH_SHOW_FLOOD_GROUP ) { await handleFloodPredictionPopupSelection(data.payload, { source: event.source as Window | null, origin: event.origin, }); return; } if (data?.type === WINDOW_POPUP_MESSAGE_TYPE.GIS_UPLOAD_COMPLETED) { const rawLayerTypeNames = data.payload?.refreshLayerTypeNames; let layerTypeNames = ["tb_ms_swage_ppl"]; if (rawLayerTypeNames) { try { const parsed = JSON.parse(rawLayerTypeNames); if (Array.isArray(parsed) && parsed.length > 0) { layerTypeNames = parsed .map((item) => String(item ?? "").trim()) .filter(Boolean); } } catch (error) { console.error("GIS 업로드 레이어 refresh 메시지 파싱 실패:", error); } } const refreshTargetNames = new Set(layerTypeNames); gisMaps.current?.getAllLayers?.().forEach((layer: any) => { const layerTypeName = String(layer?.get?.("lyrTyNm") ?? "").trim(); if (!refreshTargetNames.has(layerTypeName)) return; const source = layer.getSource?.(); source?.updateParams?.({ _t: Date.now() }); source?.refresh?.(); source?.changed?.(); layer.changed?.(); }); } }; window.addEventListener("message", handlePopupMessage); return () => { window.clearInterval(timer); window.removeEventListener("message", handlePopupMessage); }; }, [gisMaps, setMapReady, handleFloodPredictionPopupSelection]); }