import React, { useState, useRef, useEffect } from 'react'; import { View, ViewStyle, StyleSheet } from 'react-native'; import { ComponentStyleProp, LLComponentStyleFn, useStyles, useWidget, } from '@livelike/react-native'; import { WebView } from 'react-native-webview'; export type LLSocialEmbedBodyStyles = { webview: ViewStyle; container: ViewStyle; }; export type LLSocialEmbedWidgetBodyProps = ComponentStyleProp & { widgetId: string; }; export function LLSocialEmbedWidgetBody({ widgetId, styles: stylesProp, }: LLSocialEmbedWidgetBodyProps) { const styles = useStyles({ componentStylesFn: getSocialEmbedWidgetBodyStyles, stylesProp, }); const widget = useWidget({ widgetId }); const webViewRef = useRef(null); const [webViewHeight, setWebViewHeight] = useState(100); // Initial height useEffect(() => { return () => { webViewRef.current?.injectJavaScript(cleanupObserver); }; }, []); if (!widget?.items?.length) { return undefined; } const cleanupObserver = ` (function() { if (window.embedObserver) { alert('disconnecting'); window.embedObserver.disconnect(); window.embedObserver = null; } })(); `; const generateInjectedJavaScript = () => { return ` (function() { const scriptArr = []; function isScript(el) { if (el.nodeName === 'SCRIPT') { const cleanedUrl = el.src.replace('file://', 'https://'); if (!scriptArr.includes(cleanedUrl)) { scriptArr.push(cleanedUrl); const script = document.createElement('script'); script.src = cleanedUrl; document.body.insertAdjacentElement('beforeend', script); } } } function allDescendants(node) { for (let i = 0; i < node.childNodes.length; i++) { const child = node.childNodes[i]; isScript(child); node.hasChildNodes() && allDescendants(child); } } function removeTwitterFooter(el) { const tweet = el.querySelector('twitter-widget'); if ( tweet && tweet.shadowRoot && tweet.shadowRoot.querySelector('.TweetInfo') ) { tweet.shadowRoot.querySelector('.TweetInfo').style.display = 'none'; tweet.shadowRoot.querySelector('.CallToAction').style.display = 'none'; } else { setTimeout(() => removeTwitterFooter(el), 100); } } function updateHeight() { const height = document.documentElement.scrollHeight; window.ReactNativeWebView.postMessage(JSON.stringify({ type: 'height', height })); } function applyProviderEffects(item) { if (item.oembed.provider_name === 'Twitter') { setTimeout(() => { removeTwitterFooter(document.body); updateHeight(); }, 500); } } function insertItemEmbeds() { const item = ${JSON.stringify(widget.items[0])}; const el = document.getElementById('embed'); if (!el) return; const holder = document.createElement('div'); if (item.oembed && item.oembed.html) { holder.innerHTML = item.oembed.html; allDescendants(holder); el.innerHTML = item.oembed.html; applyProviderEffects(item) window.embedObserver = new MutationObserver(() => { updateHeight(); }); window.embedObserver.observe(document.body, { attributes: true, childList: true, subtree: true }); // Initial height update setTimeout(updateHeight, 100); } } insertItemEmbeds(); return true; })(); `; }; const onMessage = (event) => { try { const data = JSON.parse(event.nativeEvent.data); if (data.type === 'height') { setWebViewHeight(data.height); } } catch (error) { console.log('WebView message error:', error); } }; return (
`, }} injectedJavaScript={generateInjectedJavaScript()} javaScriptEnabled={true} allowsInlineMediaPlayback={true} mediaPlaybackRequiresUserAction={true} onMessage={onMessage} scrollEnabled={false} />
); } const getSocialEmbedWidgetBodyStyles: LLComponentStyleFn< LLSocialEmbedBodyStyles > = ({ theme }) => StyleSheet.create({ container: { display: 'flex', flexDirection: 'column', marginBottom: 16, paddingHorizontal: 16, }, webview: { width: '100%', marginBottom: 8, opacity: 0.99, overflow: 'hidden', }, });