1import type { MenuAction } from "@react-native-menu/menu";
2import { useCallback, useMemo } from "react";
3import { NativeStackScreenOptions } from "../../native/StackHeader";
4import { MaterialThreadListToolbar } from "./MaterialThreadListToolbar";
5import type { HomeHeaderProps } from "./HomeHeader.types";
6
7export type { HomeHeaderEnvironment } from "./HomeHeader.types";
8
9function checkedMenuState(checked: boolean) {
10 return checked ? ("on" as const) : undefined;
11}
12
13export function HomeHeader(props: HomeHeaderProps) {
14 // The list uses a fixed creation order and ignores sort/group options, so
15 // the filter menu only carries the filters and the "customized" icon state
16 // keys off those alone.
17 const hasCustomListOptions =
18 props.selectedEnvironmentId !== null || props.selectedProjectKey !== null;
19 const menuActions = useMemo<MenuAction[]>(
20 () => [
21 {
22 id: "environment",
23 title: "Environment",
24 subactions: [
25 {
26 id: "environment:all",
27 title: "All environments",
28 state: checkedMenuState(props.selectedEnvironmentId === null),
29 },
30 ...props.environments.map((environment) => ({
31 id: `environment:${environment.environmentId}`,
32 title: environment.label,
33 state: checkedMenuState(props.selectedEnvironmentId === environment.environmentId),
34 })),
35 ],
36 },
37 ...(props.projects.length === 0
38 ? []
39 : ([
40 {
41 id: "project",
42 title: "Project",
43 subactions: [
44 {
45 id: "project:all",
46 title: "All projects",
47 state: checkedMenuState(props.selectedProjectKey === null),
48 },
49 ...props.projects.map((project) => ({
50 id: `project:${project.key}`,
51 title: project.label,
52 state: checkedMenuState(props.selectedProjectKey === project.key),
53 })),
54 ],
55 },
56 ] satisfies MenuAction[])),
57 ],
58 [props.environments, props.projects, props.selectedEnvironmentId, props.selectedProjectKey],
59 );
60 const handleMenuAction = useCallback(
61 (event: { nativeEvent: { event: string } }) => {
62 const id = event.nativeEvent.event;
63 if (id === "environment:all") {
64 props.onEnvironmentChange(null);
65 return;
66 }
67
68 if (id.startsWith("environment:")) {
69 const environmentId = id.slice("environment:".length);
70 const environment = props.environments.find(
71 (candidate) => candidate.environmentId === environmentId,
72 );
73 if (environment) {
74 props.onEnvironmentChange(environment.environmentId);
75 }
76 return;
77 }
78
79 if (id === "project:all") {
80 props.onProjectChange(null);
81 return;
82 }
83
84 if (id.startsWith("project:")) {
85 const projectKey = id.slice("project:".length);
86 if (props.projects.some((project) => project.key === projectKey)) {
87 props.onProjectChange(projectKey);
88 }
89 return;
90 }
91 },
92 [props],
93 );
94
95 return (
96 <>
97 <NativeStackScreenOptions options={{ headerShown: false }} />
98 <MaterialThreadListToolbar
99 searchQuery={props.searchQuery}
100 onSearchQueryChange={props.onSearchQueryChange}
101 filterActions={menuActions}
102 filterCustomized={hasCustomListOptions}
103 onFilterAction={handleMenuAction}
104 onOpenSettings={props.onOpenSettings}
105 onOpenEnvironments={props.onOpenEnvironments}
106 />
107 </>
108 );
109}