import { createNativeBottomTabNavigator, type NativeBottomTabNavigationEventMap, type NativeBottomTabNavigationOptions, } from "@bottom-tabs/react-navigation"; import { Stack, useSegments, withLayoutContext } from "expo-router"; import type { ParamListBase, TabNavigationState, } from "expo-router/react-navigation"; import { useCallback, useEffect, useMemo } from "react"; import { useTranslation } from "react-i18next"; import { Platform, View } from "react-native"; import { SystemBars } from "react-native-edge-to-edge"; import type { TVNavBarTab } from "@/components/tv/TVNavBar"; import { TVNavBar } from "@/components/tv/TVNavBar"; import { Colors } from "@/constants/Colors"; import useRouter from "@/hooks/useAppRouter"; import { isTabRoute, useTVHomeBackHandler, useTVTabRootBackHandler, } from "@/hooks/useTVBackHandler"; import { useSettings } from "@/utils/atoms/settings"; import { eventBus } from "@/utils/eventBus"; // Music components are not available on tvOS (TrackPlayer not supported) const MiniPlayerBar = Platform.isTV ? () => null : require("@/components/music/MiniPlayerBar").MiniPlayerBar; const MusicPlaybackEngine = Platform.isTV ? () => null : require("@/components/music/MusicPlaybackEngine").MusicPlaybackEngine; const { Navigator } = createNativeBottomTabNavigator(); export const NativeTabs = withLayoutContext< NativeBottomTabNavigationOptions, typeof Navigator, TabNavigationState, NativeBottomTabNavigationEventMap >(Navigator); const IS_ANDROID_TV = Platform.isTV && Platform.OS === "android"; function TVTabLayout() { const { settings } = useSettings(); const { t } = useTranslation(); const segments = useSegments(); const router = useRouter(); const currentTab = segments.find(isTabRoute); const lastSegment = segments[segments.length - 1] ?? ""; const atTabRoot = isTabRoute(lastSegment) || lastSegment === "index"; const tabs: TVNavBarTab[] = useMemo( () => [ { key: "(home)", label: t("tabs.home") }, { key: "(search)", label: t("tabs.search") }, { key: "(favorites)", label: t("tabs.favorites") }, !settings?.streamyStatsServerUrl || settings?.hideWatchlistsTab ? null : { key: "(watchlists)", label: t("watchlists.title") }, { key: "(libraries)", label: t("tabs.library") }, !settings?.showCustomMenuLinks ? null : { key: "(custom-links)", label: t("tabs.custom_links") }, { key: "(settings)", label: t("tabs.settings") }, ].filter((tab): tab is TVNavBarTab => tab !== null), [ settings?.streamyStatsServerUrl, settings?.hideWatchlistsTab, settings?.showCustomMenuLinks, t, ], ); const activeTabKey = currentTab ?? "(home)"; const visibleKeys = useMemo( () => new Set(tabs.map((tab) => tab.key)), [tabs], ); const handleTabChange = useCallback( (key: string) => { if (key === currentTab) return; if (key === "(home)") eventBus.emit("scrollToTop"); if (key === "(search)") eventBus.emit("searchTabPressed"); router.replace(`/(auth)/(tabs)/${key}`); }, [currentTab, router], ); const navigateHome = useCallback(() => { router.replace("/(auth)/(tabs)/(home)"); }, [router]); useTVTabRootBackHandler(navigateHome, atTabRoot, currentTab); // If current tab is no longer visible (setting changed), navigate to home useEffect(() => { if (!visibleKeys.has(activeTabKey) && activeTabKey !== "(home)") { router.replace("/(auth)/(tabs)/(home)"); } }, [visibleKeys, activeTabKey, router]); return ( ); } export default function TabLayout() { const { settings } = useSettings(); const { t } = useTranslation(); // Must be called before any conditional return (rules of hooks) useTVHomeBackHandler(); if (IS_ANDROID_TV) { return ; } return ( ); }