import * as React from "react"; import { NativeModules, StyleSheet, View } from "react-native"; import { getXray } from "@applicaster/zapp-react-native-utils/logger"; import { isApplePlatform, isWeb } from "../reactUtils"; import { useRivers } from "../reactHooks"; const layoutReducer = (state, { payload }) => { return state.map((item, index, _state) => ({ height: index === payload.index ? payload.height : item.height, y: index > 0 ? _state.slice(0, index).reduce((acc, value) => acc + value.height, 0) : 0, })); }; type Props = { children: React.ReactNode; numberOfComponents: number; riverId: string; }; type ItemProps = { children: React.ReactNode; index: number; }; type MeasurementContext = { onLayout: (index: number) => (event) => void; }; const styles = StyleSheet.create({ container: { flex: 1, }, }); const { Logger } = getXray(); const logger = new Logger("general", "ui"); let notified = false; export const MeasurementsSettersContext = React.createContext({ onLayout: function (_index): (event: any) => void { return undefined; }, }); const moduleInitTime = Date.now(); const { ReactNativeEventBusBridge = null } = NativeModules; export const ScreenLoadingMeasurements = ({ children, numberOfComponents, riverId, }: Props) => { const rivers = useRivers(); const screenData = rivers?.[riverId]; const [itemsLayout, dispatchItemLayout] = React.useReducer( layoutReducer, new Array(numberOfComponents).fill({ height: 0, y: 0 }) ); const [listHeight, setListHeight] = React.useState(0); const onItemLayout = React.useCallback( (index) => (event) => { const { height } = event.nativeEvent.layout; if (!notified) { // Don't need to set state when event was already sent (optimization) dispatchItemLayout({ payload: { index, height } }); } }, [] ); const onListLayout = React.useCallback( (event) => { const { height } = event.nativeEvent.layout; const newValue = isWeb() ? 1080 : height; if (newValue !== listHeight) { setListHeight(isWeb() ? 1080 : height); } }, [listHeight] ); React.useEffect(() => { if (!notified) { const finishLoadingVisible = !!itemsLayout.find((item) => item.y > listHeight) || itemsLayout.every((item) => item.height > 0); if (finishLoadingVisible) { if (screenData?.home) { if (isWeb()) { logger.sendAudienceEvent("app_home_loaded", { loading_duration: Date.now() - moduleInitTime, }); } else { const event = isApplePlatform() ? { type: "eventsBus.audience.appHomeLoaded", source: "StartupTracker", data: { loading_duration: Date.now() - moduleInitTime, timestamp: Date.now(), }, } : { type: "StartupTracker.Milestone", source: "StartupTracker", data: { id: "app_home_loaded", data: { loading_duration: Date.now() - moduleInitTime, }, timestamp: Date.now(), }, }; ReactNativeEventBusBridge?.postEvent(event); } } notified = true; } } }, [itemsLayout, listHeight]); const contextValue = React.useMemo(() => ({ onLayout: onItemLayout }), []); return ( {children} ); }; export const ScreenLoadingMeasurementsListItemWrapper = ({ children, index, }: ItemProps) => { const { onLayout } = React.useContext(MeasurementsSettersContext); return ( {children} ); };