UNPKG

@applicaster/zapp-react-native-utils

Version:

Applicaster Zapp React Native utilities package

293 lines (231 loc) • 7.85 kB
import { useContext, useEffect, useMemo, useRef } from "react"; import { BackHandler } from "react-native"; import { useContentTypes, usePickFromState, } from "@applicaster/zapp-react-native-redux/hooks"; import { HooksManager } from "@applicaster/zapp-react-native-utils/appUtils/HooksManager"; import { LONG_KEY_PRESS_TIMEOUT } from "@applicaster/quick-brick-core/const"; import { ZappHookModalContext } from "@applicaster/zapp-react-native-ui-components/Contexts"; import { HookModalContextT } from "@applicaster/zapp-react-native-ui-components/Contexts/ZappHookModalContext"; import { HOOKS_EVENTS } from "../../appUtils/HooksManager/constants"; import { getRiverFromRoute, getTargetRoute } from "../../navigationUtils"; import { useConnectionInfo } from "../connection"; import { isTV } from "@applicaster/zapp-react-native-utils/reactUtils"; import { useNavbarState } from "../screen"; export { useNavigation } from "./useNavigation"; export { useRoute } from "./useRoute"; export { usePathname } from "./usePathname"; export { useNavigationPluginData } from "./useNavigationPluginData"; export { useNavigationType } from "./useNavigationType"; export { useGetBottomTabBarHeight } from "./useGetBottomTabBarHeight"; export { useIsScreenActive } from "./useIsScreenActive"; export { useProfilerLogging } from "./useProfilerLogging"; export { useUniqueRouteSuffix, useNavbarId, useContentId, } from "./useUniqueRouteSuffix"; /** * This function helps to decide wether the navbar should be presented on the screen * based on route and / or screen Data * * Currently the scenarios include * - players => false * - search screen plugin => false * - hooks with `showNavBar: true` or `presentFullScreen` not set to true, defaults to no navbar for hook screens * - screens with `allow_screen_plugin_presentation` in general property => false * * @param {String} route current route of the screen * @param {Object} screenData payload associated with the currently presented screen * @returns {Boolean} */ export function isNavBarVisible( route: string, screenData: ZappRiver = {} as ZappRiver, showNavBar: boolean, canGoBack: boolean, videoModalState: QuickBrickVideoModalState | null = null ) { /* screenData is not actual navigator.data * or navigator.screenData, * but navigator.data.screen */ if (route.includes("playable")) { return false; } if ( videoModalState?.mode === "FULLSCREEN" && videoModalState?.visible === true ) { return false; } if (screenData?.type === "qb_search_screen" && !isTV()) { return false; } if (screenData?.plugin_type === "login" && isTV()) { return false; } /* Match if screen is Hook */ if (route.startsWith("/hooks/")) { const module = screenData?.module; if (module?.presentFullScreen) { return false; } return module?.showNavBar ?? false; } if (screenData?.hookPlugin) { const hookPlugin = screenData?.hookPlugin?.module; return ( hookPlugin?.showNavBar || hookPlugin?.presentFullScreen !== true || false ); } if ( screenData?.general?.allow_screen_plugin_presentation || screenData?.general?.hide_app_nav_bar ) { return false; } const showSecondaryLevel = !isTV() || screenData?.navigations?.some( (navigation) => navigation.styles?.show_secondary_level_menu ); if (canGoBack && !showSecondaryLevel) { return false; } if (typeof showNavBar === "boolean" && !showNavBar) { return false; } return true; } export const useBackHandler = (cb: () => boolean) => { useEffect(() => { BackHandler.addEventListener("hardwareBackPress", cb); return () => { BackHandler.removeEventListener("hardwareBackPress", cb); }; }, [cb]); }; type Callbacks = Partial<{ handleHookPresent: ({ route, payload, }: { route: string; payload: HookPluginProps; }) => void; handleHookError: (props: HookPluginProps & { error?: string }) => any; handleHookCancel: (props: Omit<HookPluginProps, "callback">) => any; handleHookComplete: ( props: Omit<HookPluginProps, "callback"> & { route: string } ) => any; }>; export const useZappHooksForEntry = ( entry: ZappEntry, callbacks?: Callbacks ) => { const { setState, resetState, setIsHooksExecutionInProgress, setIsPresentingFullScreen, setIsRunningInBackground, }: HookModalContextT = useContext(ZappHookModalContext.ReactContext); const { appData: { layoutVersion }, rivers, plugins, } = usePickFromState(["appData", "rivers", "plugins"]); const contentTypes = useContentTypes(); const isOnline = useConnectionInfo(true); useEffect(() => { resetState(); if (entry) { setIsHooksExecutionInProgress(true); if (!isOnline) { setIsHooksExecutionInProgress(false); // TODO: ideally move this logic to hooks manager // @ts-ignore return callbacks?.handleHookComplete?.({ payload: entry }); } const targetRoute = getTargetRoute(entry, "", { layoutVersion, contentTypes, }); const targetScreen = getRiverFromRoute({ route: targetRoute, rivers }); const hooksOptions = { rivers, plugins, targetScreen, }; const handleHookPresent = ({ route, payload }) => { setIsPresentingFullScreen(); setState({ path: route, screenData: payload, }); callbacks?.handleHookPresent?.({ route, payload }); }; const handleBackgroundHook = (hookProps) => { const { route, payload } = hookProps; setState({ path: route, screenData: payload, }); setIsRunningInBackground(); }; const handleHookError: Callbacks["handleHookError"] = (props) => { setIsHooksExecutionInProgress(false); callbacks?.handleHookError?.(props); }; const handleHookCancel: Callbacks["handleHookCancel"] = (props) => { setIsHooksExecutionInProgress(false); callbacks?.handleHookCancel?.(props); }; const handleHookComplete: Callbacks["handleHookComplete"] = (props) => { setIsHooksExecutionInProgress(false); callbacks?.handleHookComplete?.(props); }; const hookManager = HooksManager(hooksOptions); hookManager.subscriber .on(HOOKS_EVENTS.PRESENT_SCREEN_HOOK, handleHookPresent) .on(HOOKS_EVENTS.ERROR, handleHookError) .on(HOOKS_EVENTS.CANCEL, handleHookCancel) .on(HOOKS_EVENTS.START_BACKGROUND_HOOK, handleBackgroundHook) .on(HOOKS_EVENTS.COMPLETE, handleHookComplete); hookManager.handleHooks(entry); return () => { hookManager.subscriber .removeHandler(HOOKS_EVENTS.PRESENT_SCREEN_HOOK, handleHookPresent) .removeHandler(HOOKS_EVENTS.ERROR, handleHookError) .removeHandler(HOOKS_EVENTS.CANCEL, handleHookCancel) .removeHandler(HOOKS_EVENTS.COMPLETE, handleHookComplete); }; } }, [entry.id]); }; export const useRunIfLongPress = ( options = { timeout: LONG_KEY_PRESS_TIMEOUT } ) => { const pressTimeout = useRef<Nullable<ReturnType<typeof setTimeout>>>(null); const resetTimeout = () => { clearTimeout(pressTimeout.current as ReturnType<typeof setTimeout>); }; const startTimeout = (cb: () => void) => { pressTimeout.current = setTimeout(cb, options.timeout); }; const onPress = (cb: () => void) => { startTimeout(cb); }; const onPressOut = () => { resetTimeout(); }; return { onPress, onPressOut }; }; /** * @returns boolean - navbar visiblity status */ export const useIsNavBarVisible = (): boolean => { const { visible } = useNavbarState(); return useMemo(() => visible, [visible]); };