import React, { useCallback, useMemo, useState, useEffect } from "react"; import { ActivityIndicator, StyleProp, ViewStyle } from "react-native"; import WebView from "react-native-webview"; import { View, Text } from "../.."; import { murmurHash } from "../../utils/stylesheet"; import styles from "./styles"; type Props = { errorMessage?: string; html: string; initialWidth?: number; style?: StyleProp; }; const script = ` var MutationObserver = window.MutationObserver || window.WebKitMutationObserver; function reportSize() { var target = document.body.firstElementChild || document.querySelector("body"); var dimensions = target.getBoundingClientRect(); var data = JSON.stringify({ id: embedId, height: Math.round(dimensions.top + dimensions.bottom), width: Math.round(dimensions.left + dimensions.right), }); if (window.ReactNativeWebView) { window.ReactNativeWebView.postMessage(data); } else { window.parent.postMessage(data, "*"); } } var observer = new MutationObserver(reportSize); observer.observe(document, { subtree: true, attributes: true }); window.parent.addEventListener("load", reportSize); setTimeout(reportSize, 3000); // backup `; const Embed = ({ html, errorMessage, initialWidth, style }: Props) => { // react-native-web-webview attaches the onMessage handler to the global window, // so we need to be able distinguish which messages are for which Embed instance. const embedId = useMemo(() => murmurHash(html || ""), [html]); const [dimensions, setDimensions] = useState({ width: initialWidth || "100%", height: 0, }); const [loading, setLoading] = useState(true); useEffect(() => { if (!html) return; const width = (html.match(/ width=(["']?)(\d+%?)(?:px)?\1/) || [])[2]; const height = (html.match(/ height=(["']?)(\d+)(?:px)?\1/) || [])[2]; if (height) setLoading(false); setDimensions((current) => ({ width: width || current.width, height: +height || current.height, })); }, [html]); const onWebViewMessage = useCallback( ({ nativeEvent: { data } }) => { if (typeof data === "string") { try { const json = JSON.parse(data); if (json.id === embedId) { setDimensions({ width: json.width, height: json.height, }); setLoading(false); } } catch (err) {} } }, [embedId] ); const source = ` ${html || ""} `; const embedDimensions = { height: loading || errorMessage ? 32 : dimensions.height, width: loading || errorMessage ? "100%" : dimensions.width, }; return ( {loading && !errorMessage && } {errorMessage ? ( {errorMessage} ) : ( setLoading(false)} onMessage={onWebViewMessage} automaticallyAdjustContentInsets={false} source={{ html: source }} containerStyle={embedDimensions} /> )} ); }; export default Embed;