import * as React from "react"; import { Animated } from "react-native"; import { isFirstComponentScreenPicker } from "@applicaster/zapp-react-native-utils/componentsUtils"; import { useRefWithInitialValue } from "@applicaster/zapp-react-native-utils/reactHooks/state/useRefWithInitialValue"; import { useTheme } from "@applicaster/zapp-react-native-utils/theme"; import { Overlay } from "./Overlay"; import { ScreenRevealManager } from "./ScreenRevealManager"; import { emitScreenRevealManagerIsReadyToShow, emitScreenRevealManagerIsNotReadyToShow, } from "./utils"; export const TIMEOUT = 300; // 300 ms const HIDDEN = 0; // opacity = 0 export const SHOWN = 1; // opacity = 1 type Props = { componentsToRender: ZappUIComponent[]; backgroundColor?: string; /** * Renders every component at once instead of revealing them a few at a time. * * For a screen that builds its own components and holds all of them already - * a form, say - staggering buys nothing and costs: each rebuild of the list * tears it back down to the first few and lets the rest crawl in again. * * This is a flag rather than a count on purpose. A count would have to be * re-read whenever the number of components changes, while "do not stagger" * stays true whatever the screen ends up holding. */ disableIncrementalLoading?: boolean; }; export const withScreenRevealManager = (Component) => { return function WithScreenRevealManager(props: Props) { const { componentsToRender, disableIncrementalLoading } = props; const [isContentReadyToBeShown, setIsContentReadyToBeShown] = React.useState(false); const [isShowOverlay, setIsShowOverlay] = React.useState(true); const theme = useTheme(); const handleSetIsContentReadyToBeShown = React.useCallback(() => { setIsContentReadyToBeShown(true); }, []); const managerRef = useRefWithInitialValue( () => new ScreenRevealManager( componentsToRender, handleSetIsContentReadyToBeShown, disableIncrementalLoading ? componentsToRender.length : undefined ) ); const opacityRef = useRefWithInitialValue( () => new Animated.Value(SHOWN) ); React.useEffect(() => { if (!isContentReadyToBeShown) { emitScreenRevealManagerIsNotReadyToShow(); } else { emitScreenRevealManagerIsReadyToShow(); } }, [isContentReadyToBeShown]); React.useEffect(() => { if (isContentReadyToBeShown) { Animated.timing(opacityRef.current, { toValue: HIDDEN, duration: TIMEOUT, useNativeDriver: true, }).start(() => { setIsShowOverlay(false); }); } }, [isContentReadyToBeShown]); if (isFirstComponentScreenPicker(componentsToRender)) { // for screen-picker with have additional internal ComponentsMap, no need to add this wrapper return ; } return ( <> {isShowOverlay ? ( ) : null} ); }; };