Files
frontend/apps/sn/app/(map-layout)/map/lib/use-map-runtime-bridge.ts

140 lines
4.8 KiB
TypeScript

"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<any>;
setMapReady: Dispatch<SetStateAction<boolean>>;
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]);
}