| 1 | import { requireNativeView } from "expo"; |
| 2 | import { CommonActions, useNavigation } from "@react-navigation/native"; |
| 3 | import { androidWorkspaceNavigationState } from "../../lib/android-workspace-navigation"; |
| 4 | import { |
| 5 | Keyboard, |
| 6 | processColor, |
| 7 | View, |
| 8 | type NativeSyntheticEvent, |
| 9 | type ViewProps, |
| 10 | } from "react-native"; |
| 11 | import { useSafeAreaInsets } from "react-native-safe-area-context"; |
| 12 | import { useKeyboardState } from "react-native-keyboard-controller"; |
| 13 | |
| 14 | import { useAndroidControlSizing } from "../../components/useAndroidControlSizing"; |
| 15 | import { androidWorkspaceNavigationColors } from "./android-workspace-navigation-colors"; |
| 16 | import { useAppearancePreferences } from "../settings/appearance/AppearancePreferencesProvider"; |
| 17 | import { useWorkspaceTabs } from "./WorkspaceTabsContext"; |
| 18 | import type { WorkspaceTab } from "./workspace-tabs-state"; |
| 19 | |
| 20 | export const ANDROID_WORKSPACE_RAIL_WIDTH = 80; |
| 21 | |
| 22 | interface NativeNavigationProps extends ViewProps { |
| 23 | readonly rail: boolean; |
| 24 | readonly selectedTab: WorkspaceTab; |
| 25 | readonly dark: boolean; |
| 26 | readonly textScale: number; |
| 27 | readonly containerColor: number; |
| 28 | readonly selectedColor: number; |
| 29 | readonly indicatorColor: number; |
| 30 | readonly unselectedColor: number; |
| 31 | readonly onTabSelected: (event: NativeSyntheticEvent<{ readonly tab: WorkspaceTab }>) => void; |
| 32 | } |
| 33 | |
| 34 | const NativeNavigation = requireNativeView<NativeNavigationProps>("T3WorkspaceNavigation"); |
| 35 | |
| 36 | function nativeColor(value: string) { |
| 37 | const color = processColor(value); |
| 38 | return typeof color === "number" ? color : 0; |
| 39 | } |
| 40 | |
| 41 | export function AndroidWorkspaceNavigation(props: { |
| 42 | readonly rail?: boolean; |
| 43 | readonly onSelect?: () => void; |
| 44 | }) { |
| 45 | const workspace = useWorkspaceTabs()!; |
| 46 | const navigation = useNavigation(); |
| 47 | const insets = useSafeAreaInsets(); |
| 48 | const keyboardVisible = useKeyboardState((state) => state.isVisible); |
| 49 | const { themeAppearance, themeVariables: colors } = useAppearancePreferences(); |
| 50 | const { scale } = useAndroidControlSizing(); |
| 51 | const rail = Boolean(props.rail); |
| 52 | const { containerColor, indicatorColor, selectedColor, unselectedColor } = |
| 53 | androidWorkspaceNavigationColors(colors, rail); |
| 54 | if (!rail && keyboardVisible) return null; |
| 55 | |
| 56 | return ( |
| 57 | <View |
| 58 | style={{ |
| 59 | backgroundColor: containerColor, |
| 60 | ...(rail |
| 61 | ? { |
| 62 | flex: 1, |
| 63 | width: ANDROID_WORKSPACE_RAIL_WIDTH * Math.max(1, scale), |
| 64 | paddingTop: insets.top, |
| 65 | paddingBottom: insets.bottom, |
| 66 | } |
| 67 | : { paddingBottom: insets.bottom }), |
| 68 | }} |
| 69 | > |
| 70 | <NativeNavigation |
| 71 | rail={rail} |
| 72 | selectedTab={workspace.state.selectedTab} |
| 73 | dark={themeAppearance === "dark"} |
| 74 | textScale={scale} |
| 75 | containerColor={nativeColor(containerColor)} |
| 76 | selectedColor={nativeColor(selectedColor)} |
| 77 | indicatorColor={nativeColor(indicatorColor)} |
| 78 | unselectedColor={nativeColor(unselectedColor)} |
| 79 | onTabSelected={(event) => { |
| 80 | Keyboard.dismiss(); |
| 81 | const tab = event.nativeEvent.tab; |
| 82 | if (tab !== workspace.state.selectedTab) { |
| 83 | workspace.dispatch({ type: "select", tab }); |
| 84 | navigation.dispatch((state) => |
| 85 | CommonActions.reset(androidWorkspaceNavigationState(state)), |
| 86 | ); |
| 87 | } |
| 88 | props.onSelect?.(); |
| 89 | }} |
| 90 | style={rail ? { flex: 1 } : { height: 80 * Math.max(1, scale) }} |
| 91 | /> |
| 92 | </View> |
| 93 | ); |
| 94 | } |