1import { requireNativeView } from "expo";
2import { CommonActions, useNavigation } from "@react-navigation/native";
3import { androidWorkspaceNavigationState } from "../../lib/android-workspace-navigation";
4import {
5 Keyboard,
6 processColor,
7 View,
8 type NativeSyntheticEvent,
9 type ViewProps,
10} from "react-native";
11import { useSafeAreaInsets } from "react-native-safe-area-context";
12import { useKeyboardState } from "react-native-keyboard-controller";
13
14import { useAndroidControlSizing } from "../../components/useAndroidControlSizing";
15import { androidWorkspaceNavigationColors } from "./android-workspace-navigation-colors";
16import { useAppearancePreferences } from "../settings/appearance/AppearancePreferencesProvider";
17import { useWorkspaceTabs } from "./WorkspaceTabsContext";
18import type { WorkspaceTab } from "./workspace-tabs-state";
19
20export const ANDROID_WORKSPACE_RAIL_WIDTH = 80;
21
22interface 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
34const NativeNavigation = requireNativeView<NativeNavigationProps>("T3WorkspaceNavigation");
35
36function nativeColor(value: string) {
37 const color = processColor(value);
38 return typeof color === "number" ? color : 0;
39}
40
41export 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}