import { useCallback, useEffect, RefObject } from 'react'; import { Platform } from 'react-native'; import { WebView } from 'react-native-webview'; import { EmbedClientMessages, EmbeddedClientMessageTypes, EmbeddedHostMessageTypes, } from '../models/EmbeddedMessageTypes'; import { KomoEvent } from '../models/KomoEvent'; import { useLatest } from './useLatest'; export type UseEmbedMessageHandlerProps = { embedId: string; shareClickUrl?: string; isOpen: boolean; onClose: () => void; onKomoEvent?: (eventData: KomoEvent) => void; onWindowMessage?: (eventData: any) => void; setExperienceLoading: (loading: boolean) => void; setShowHeader: (show: boolean) => void; iframeRef: RefObject; webviewRef: RefObject; }; export const useEmbedMessageHandler = ({ embedId, shareClickUrl, isOpen, onClose, onKomoEvent, onWindowMessage, setExperienceLoading, setShowHeader, iframeRef, webviewRef, }: UseEmbedMessageHandlerProps) => { const handleWindowMessage = useLatest((e: any) => { if (!e) return; if (!onWindowMessage && !onKomoEvent) return; if (e.message === 'komo-event') { if (!e.payload?.eventName) return; onKomoEvent?.({ eventName: e.payload.eventName, eventData: e.payload.eventData, extensionData: e.payload.extensionData, }); onWindowMessage?.(e); } else { onWindowMessage?.(e); } }); const handleMessage = useCallback( (data: EmbedClientMessages) => { try { switch (data.message) { case EmbeddedClientMessageTypes.RequestInit: if (Platform.OS === 'web') { iframeRef.current?.contentWindow?.postMessage( { message: EmbeddedHostMessageTypes.Init, url: window.location.href, title: document.title, embedId, }, '*' ); break; } // the postMessage method on the webview ref is not working on android // https://github.com/react-native-webview/react-native-webview/issues/3776 if (Platform.OS === 'android') { webviewRef.current?.injectJavaScript( `window.postMessage('${JSON.stringify({ message: EmbeddedHostMessageTypes.Init, shareClickUrl, embedId, })}', '*'); true;` ); break; } webviewRef.current?.postMessage?.( JSON.stringify({ message: EmbeddedHostMessageTypes.Init, shareClickUrl, embedId, }) ); break; case EmbeddedClientMessageTypes.ExperienceStateChange: switch (data.value) { case 'complete': onClose(); break; case 'loading': setExperienceLoading(true); break; case 'ready': //Give a small delay. Sometimes we see a flash of card cover setTimeout(() => { setExperienceLoading(false); }, 500); } break; case EmbeddedClientMessageTypes.CloseModal: onClose(); break; case EmbeddedClientMessageTypes.ShowHeader: setShowHeader(data.showHeader); break; case EmbeddedClientMessageTypes.WindowMessage: handleWindowMessage.current?.(data.payload); break; default: break; } } catch (e) { console.error(e); } }, [ embedId, shareClickUrl, onClose, setExperienceLoading, setShowHeader, handleWindowMessage, iframeRef, webviewRef, ] ); // Handle iframe messages on web platform useEffect(() => { if (Platform.OS !== 'web' || !isOpen) return; function handleIframeMessage(e: MessageEvent) { if (e.data !== Object(e.data)) { return; } if (e.data?.source === 'react-devtools-content-script') { //Ignore messages from react devtools return; } if ( typeof e.data?.message !== 'string' || !e.data?.message.startsWith('komo:') ) { return; } handleMessage(e.data); } window.addEventListener('message', handleIframeMessage, false); return () => window.removeEventListener('message', handleIframeMessage, false); }, [isOpen, handleMessage]); return { handleMessage, handleWindowMessage: handleWindowMessage.current, }; };