Skip to content
Source
Sign inSign up
Open History to see the last commit

WorkspaceRootScreen.android.tsx

145 lines6.0 KB

1import { useAndroidFloatingTabs } from "../../settings/appearance/AppearancePreferencesProvider";
2import { WorkspaceNoTabs } from "./WorkspaceNoTabs";
3import { useEnabledNavigationTabs } from "./navigation-tabs";
4import { NAVIGATION_TAB_ORDER } from "./navigation-tab-order";
5import { WorkspaceExtraTab } from "./WorkspaceExtraTab";
6import type { ReactNode } from "react";
7import { View } from "react-native";
8import { SafeAreaInsetsContext, useSafeAreaInsets } from "react-native-safe-area-context";
9import { NativeStackScreenOptions } from "../../../native/StackHeader";
10import { AppBar } from "../../../components/ui/AppBar";
11import { WorkspaceEmptyDetail } from "../WorkspaceEmptyDetail";
12import { ScreenContent } from "../../../components/ui/ScreenContent";
13import { useAndroidControlSizing } from "../../../components/ui/useAndroidControlSizing";
14import { HomeRouteScreen } from "../../home/HomeRouteScreen";
15import { PullRequestsScreen } from "../../pull-requests/PullRequestsScreen";
16import { PullRequestToolbar } from "./PullRequestToolbar.android";
17import { useAdaptiveWorkspaceLayout } from "../AdaptiveWorkspaceLayout";
18import { WorkspaceSettledContentWidthContext } from "../workspace-content-width";
19import { AndroidWorkspaceNavigation, FloatingWorkspaceTabs } from "./AndroidWorkspaceNavigation";
20import { FLOATING_TABS } from "./floating-workspace-tabs";
21import { WorkspaceTabsContext, useWorkspaceTabs } from "./WorkspaceTabsContext";
22import { WorkspaceTabPanes } from "./WorkspaceTabPanes";
23import { WorkspaceAppBarScope } from "../../../components/ui/WorkspaceAppBar.android";
24import { usePaneRole } from "../../../components/ui/Pane";
25import { AndroidWorkspaceSidebar } from "./AndroidWorkspaceSidebar.android";
26import type { WorkspaceTab } from "./workspace-tabs-state";
27import { useSelectWorkspaceTab } from "./useSelectWorkspaceTab";
28import { useHardwareBack } from "../../../lib/backHandler";
29​
30const TABS: readonly WorkspaceTab[] = NAVIGATION_TAB_ORDER;
31​
32export function WorkspaceRootScreen() {
33 return usePaneRole() === "sidebar" ? <AndroidWorkspaceSidebar /> : <WorkspaceRoot />;
35​
36function WorkspaceRoot() {
37 const floatingTabsEnabled = useAndroidFloatingTabs();
38 const { layout, startNewTask, workspacePageWidth } = useAdaptiveWorkspaceLayout();
39 const workspace = useWorkspaceTabs()!;
40 const enabled = useEnabledNavigationTabs();
41 const insets = useSafeAreaInsets();
42 const { fabClearance, fabSize } = useAndroidControlSizing();
43 const context = {
44 ...workspace,
45 bottomClearance: layout.usesSplitView
46 ? workspace.bottomClearance
47 : floatingTabsEnabled
48 ? insets.bottom + FLOATING_TABS.bottom + FLOATING_TABS.edge + fabSize
49 : workspace.state.selectedTab === "threads"
50 ? fabClearance + 16
51 : 16,
52 };
53 const tab = workspace.state.selectedTab;
54 const selectTab = useSelectWorkspaceTab();
55 // Tabs are panes of Home, not routes, so Back has nothing to pop and would leave the app.
56 // An open search keeps Back for itself.
57 useHardwareBack(
58 tab !== "threads" &&
59 enabled.includes("threads") &&
60 workspace.state.tabs[tab].searchMode === "resting",
61 () => {
62 selectTab("threads");
63 return true;
64 },
65 );
66​
67 return (
68 <WorkspaceTabsContext value={context}>
69 <View className={layout.usesSplitView ? "flex-1 bg-screen" : "flex-1 bg-header"}>
70 {!layout.usesSplitView ? (
71 <NativeStackScreenOptions options={{ headerShown: false }} />
72 ) : null}
73 <SafeAreaInsetsContext
74 value={
75 layout.usesSplitView
76 ? insets
77 : floatingTabsEnabled
78 ? tab === "usage"
79 ? { ...insets, bottom: context.bottomClearance }
80 : insets
81 : { ...insets, bottom: 0 }
82 }
83 >
84 {enabled.length === 0 ? (
85 layout.usesSplitView ? (
86 <View className="flex-1 bg-screen" />
87 ) : (
88 <WorkspaceNoTabs />
89 )
90 ) : (
91 <WorkspaceAppBarScope enabled={!layout.usesSplitView}>
92 <WorkspaceTabPanes
93 tabs={TABS}
94 selected={tab}
95 renderTab={(tab) =>
96 tab === "threads" ? (
97 <HomeRouteScreen />
98 ) : tab !== "pullRequests" ? (
99 <WorkspacePageFrame width={workspacePageWidth ?? null}>
100 <WorkspaceExtraTab tab={tab} />
101 </WorkspacePageFrame>
102 ) : layout.usesSplitView ? (
103 <>
104 <NativeStackScreenOptions options={{ headerShown: false }} />
105 <AppBar embedded title="Pull requests" />
106 <WorkspaceEmptyDetail tab="pullRequests" />
107 </>
108 ) : (
109 <PullRequestsScreen
110 renderFrame={(filters, list) => (
111 <>
112 <PullRequestToolbar filters={filters} />
113 <ScreenContent>{list}</ScreenContent>
114 </>
115 )}
116 />
117 )
118 }
119 />
120 </WorkspaceAppBarScope>
121 )}
122 </SafeAreaInsetsContext>
123 {layout.usesSplitView ? null : floatingTabsEnabled ? (
124 <FloatingWorkspaceTabs fillWidth onNewThread={startNewTask} />
125 ) : (
126 <AndroidWorkspaceNavigation onNewThread={startNewTask} />
127 )}
128 </View>
129 </WorkspaceTabsContext>
130 );
132​
133// Hidden, the page sits in the narrower pane beside the list; laying it out at the width it opens
134// at keeps measured bars and breakpoints from catching up a frame after it appears.
135function WorkspacePageFrame(props: {
136 readonly width: number | null;
137 readonly children: ReactNode;
138}) {
139 if (props.width === null) return props.children;
140 return (
141 <WorkspaceSettledContentWidthContext value={props.width}>
142 <View style={{ flex: 1, width: props.width }}>{props.children}</View>
143 </WorkspaceSettledContentWidthContext>
144 );