/* Description: This is a React Native Visibility Sensor Component, which helps in detecting whether a given component is visible within the viewport or not. It is useful for implementing lazy loading or triggering specific actions when a component comes into view. */ import React, { FC, ReactNode, useEffect, useRef, useState } from "react"; import { Dimensions, View } from "react-native"; import { useIsRTL } from "@applicaster/zapp-react-native-utils/localizationUtils"; import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils"; export interface IDimensionData { rectTop: number; rectBottom: number; rectWidth: number; fullWidth: number; } export interface Props { /** The component that needs to be in the viewport */ children: ReactNode; checkPlayerPosition: ({ position, isFullyVisible }) => void; onViewportEnter: ({ dimensions, window }) => void; onViewportLeave: ({ dimensions, window }) => void; } const RNView = View as any; const VisibilitySensorComponent: FC = ({ children, checkPlayerPosition, onViewportEnter, onViewportLeave, }: Props) => { const myView: any = useRef(null); const [lastValue, setLastValue] = useState(false); const [dimensions, setDimensions] = useState({ rectTop: 0, rectBottom: 0, rectWidth: 0, fullWidth: 0, }); const isRTL: boolean = useIsRTL(); let interval: any = null; const startWatching = () => { if (interval) { return; } interval = setInterval(() => { if (!myView || !myView.current) { return; } // TODO: Not the best way. When not in viewport passed random values myView.current.measure( async ( _x: number, _y: number, width: number, height: number, pageX: number, pageY: number ) => { setDimensions({ rectTop: pageY, rectBottom: pageY + height, rectWidth: pageX + width, fullWidth: width, }); } ); }, 1000); }; const stopWatching = () => { clearInterval(interval); }; const isInViewPort = () => { const window = Dimensions.get("window"); if (dimensions.fullWidth === 0) { return; } const isFullyVisible = dimensions.rectBottom !== 0 && dimensions.rectTop > 0 && dimensions.rectBottom <= window.height && dimensions.rectWidth >= dimensions.fullWidth && window.width >= dimensions.rectWidth; const widthOffset = isRTL ? window.width - dimensions.rectWidth : dimensions.rectWidth; const xOffset = Math.round((widthOffset / window.width) * 100) / 100; const yOffset = Math.round((dimensions.rectTop / window.height) * 100) / 100; if (lastValue !== isFullyVisible) { setLastValue(isFullyVisible); if (isFullyVisible) { onViewportEnter({ dimensions, window }); } else { onViewportLeave({ dimensions, window }); } } checkPlayerPosition({ isFullyVisible, position: { xOffset, yOffset } }); }; useEffect(() => { if (!isTV()) { startWatching(); isInViewPort(); return stopWatching; } }, [dimensions.rectTop, dimensions.rectBottom, dimensions.rectWidth]); const flexStyle = { flex: 1 }; return ( {children} ); }; const VisibilitySensor = React.memo(VisibilitySensorComponent); export default VisibilitySensor;