Files
wifi-densepose/mobile/src/screens/MATScreen/useMatBridge.ts
2026-03-02 12:58:47 +02:00

119 lines
2.9 KiB
TypeScript

import { useCallback, useRef, useState } from 'react';
import type { WebView, WebViewMessageEvent } from 'react-native-webview';
import type { Alert, Survivor } from '@/types/mat';
import type { SensingFrame } from '@/types/sensing';
type MatBridgeMessageType = 'CREATE_EVENT' | 'ADD_ZONE' | 'FRAME_UPDATE';
type MatIncomingType = 'READY' | 'SURVIVOR_DETECTED' | 'ALERT_GENERATED';
type MatIncomingMessage = {
type: MatIncomingType;
payload?: unknown;
};
type MatOutgoingMessage = {
type: MatBridgeMessageType;
payload?: unknown;
};
type UseMatBridgeOptions = {
onSurvivorDetected?: (survivor: Survivor) => void;
onAlertGenerated?: (alert: Alert) => void;
};
const safeParseJson = (value: string): unknown | null => {
try {
return JSON.parse(value);
} catch {
return null;
}
};
export const useMatBridge = ({ onAlertGenerated, onSurvivorDetected }: UseMatBridgeOptions = {}) => {
const webViewRef = useRef<WebView | null>(null);
const isReadyRef = useRef(false);
const queuedMessages = useRef<string[]>([]);
const [ready, setReady] = useState(false);
const flush = useCallback(() => {
if (!webViewRef.current || !isReadyRef.current) {
return;
}
while (queuedMessages.current.length > 0) {
const payload = queuedMessages.current.shift();
if (payload) {
webViewRef.current.postMessage(payload);
}
}
}, []);
const sendMessage = useCallback(
(message: MatOutgoingMessage) => {
const payload = JSON.stringify(message);
if (isReadyRef.current && webViewRef.current) {
webViewRef.current.postMessage(payload);
return;
}
queuedMessages.current.push(payload);
},
[],
);
const sendFrameUpdate = useCallback(
(frame: SensingFrame) => {
sendMessage({ type: 'FRAME_UPDATE', payload: frame });
},
[sendMessage],
);
const postEvent = useCallback(
(type: 'CREATE_EVENT' | 'ADD_ZONE') => {
return (payload: unknown) => {
sendMessage({
type,
payload,
});
};
},
[sendMessage],
);
const onMessage = useCallback(
(event: WebViewMessageEvent) => {
const payload = safeParseJson(event.nativeEvent.data);
if (!payload || typeof payload !== 'object') {
return;
}
const message = payload as MatIncomingMessage;
if (message.type === 'READY') {
isReadyRef.current = true;
setReady(true);
flush();
return;
}
if (message.type === 'SURVIVOR_DETECTED') {
onSurvivorDetected?.(message.payload as Survivor);
return;
}
if (message.type === 'ALERT_GENERATED') {
onAlertGenerated?.(message.payload as Alert);
}
},
[flush, onAlertGenerated, onSurvivorDetected],
);
return {
webViewRef,
ready,
onMessage,
sendMessage,
sendFrameUpdate,
postEvent,
};
};