import type { EnvironmentId, SidebarProjectGroupingMode, SidebarThreadSortOrder,} from "@t3tools/contracts";import { DEFAULT_SIDEBAR_PROJECT_GROUPING_MODE, DEFAULT_SIDEBAR_PROJECT_SORT_ORDER, DEFAULT_SIDEBAR_THREAD_SORT_ORDER,} from "@t3tools/contracts";import { Stack } from "expo-router";import { Text as RNText, View } from "react-native"; import { useThemeColor } from "../../lib/useThemeColor";import { MOBILE_TYPOGRAPHY } from "../../lib/typography";import type { HomeProjectSortOrder } from "./homeThreadList"; export interface HomeHeaderEnvironment { readonly environmentId: EnvironmentId; readonly label: string;} const PROJECT_SORT_OPTIONS: ReadonlyArray<{ readonly value: HomeProjectSortOrder; readonly label: string;}> = [ { value: "updated_at", label: "Last user message" }, { value: "created_at", label: "Created at" },]; const THREAD_SORT_OPTIONS: ReadonlyArray<{ readonly value: SidebarThreadSortOrder; readonly label: string;}> = [ { value: "updated_at", label: "Last user message" }, { value: "created_at", label: "Created at" },]; const PROJECT_GROUPING_OPTIONS: ReadonlyArray<{ readonly value: SidebarProjectGroupingMode; readonly label: string; readonly subtitle: string;}> = [ { value: "repository", label: "Group by repository", subtitle: "Combine matching repositories across environments", }, { value: "repository_path", label: "Group by repository path", subtitle: "Combine only matching paths within a repository", }, { value: "separate", label: "Keep separate", subtitle: "Show every project path separately", },]; export function HomeHeader(props: { readonly environments: ReadonlyArray<HomeHeaderEnvironment>; readonly selectedEnvironmentId: EnvironmentId | null; readonly projectSortOrder: HomeProjectSortOrder; readonly threadSortOrder: SidebarThreadSortOrder; readonly projectGroupingMode: SidebarProjectGroupingMode; readonly onSearchQueryChange: (query: string) => void; readonly onEnvironmentChange: (environmentId: EnvironmentId | null) => void; readonly onProjectSortOrderChange: (sortOrder: HomeProjectSortOrder) => void; readonly onThreadSortOrderChange: (sortOrder: SidebarThreadSortOrder) => void; readonly onProjectGroupingModeChange: (mode: SidebarProjectGroupingMode) => void; readonly onOpenSettings: () => void; readonly onStartNewTask: () => void;}) { const iconColor = useThemeColor("--color-icon"); const mutedColor = useThemeColor("--color-foreground-muted"); const subtleColor = useThemeColor("--color-subtle"); const hasCustomListOptions = props.selectedEnvironmentId !== null || props.projectSortOrder !== DEFAULT_SIDEBAR_PROJECT_SORT_ORDER || props.threadSortOrder !== DEFAULT_SIDEBAR_THREAD_SORT_ORDER || props.projectGroupingMode !== DEFAULT_SIDEBAR_PROJECT_GROUPING_MODE; return ( <> <Stack.Screen options={{ headerShown: true, headerTransparent: true, headerStyle: { backgroundColor: "transparent" }, headerShadowVisible: false, headerTintColor: iconColor, headerTitle: "", headerSearchBarOptions: { placeholder: "Search threads", hideNavigationBar: false, onChangeText: (event) => { props.onSearchQueryChange(event.nativeEvent.text); }, onCancelButtonPress: () => { props.onSearchQueryChange(""); }, allowToolbarIntegration: true, }, }} /> <Stack.Toolbar placement="left"> <Stack.Toolbar.View hidesSharedBackground> <View style={{ width: 128, height: 32, flexDirection: "row", alignItems: "center", gap: 8, }} > <RNText style={{ fontFamily: "DMSans_700Bold", fontSize: MOBILE_TYPOGRAPHY.headline.fontSize, color: iconColor, letterSpacing: -0.4, }} > T3 Code </RNText> <View style={{ backgroundColor: subtleColor, borderRadius: 99, paddingHorizontal: 8, paddingVertical: 3, }} > <RNText style={{ fontFamily: "DMSans_700Bold", fontSize: MOBILE_TYPOGRAPHY.micro.fontSize, color: mutedColor, letterSpacing: 1.1, textTransform: "uppercase", }} > Alpha </RNText> </View> </View> </Stack.Toolbar.View> </Stack.Toolbar> <Stack.Toolbar placement="right"> <Stack.Toolbar.Menu accessibilityLabel="Filter and sort threads" icon={ hasCustomListOptions ? "line.3.horizontal.decrease.circle.fill" : "line.3.horizontal.decrease.circle" } separateBackground title="Thread list options" > <Stack.Toolbar.Menu title="Environment"> <Stack.Toolbar.Label>Environment</Stack.Toolbar.Label> <Stack.Toolbar.MenuAction isOn={props.selectedEnvironmentId === null} onPress={() => props.onEnvironmentChange(null)} subtitle="Show threads from every environment" > <Stack.Toolbar.Label>All environments</Stack.Toolbar.Label> </Stack.Toolbar.MenuAction> {props.environments.map((environment) => ( <Stack.Toolbar.MenuAction key={environment.environmentId} isOn={props.selectedEnvironmentId === environment.environmentId} onPress={() => props.onEnvironmentChange(environment.environmentId)} > <Stack.Toolbar.Label>{environment.label}</Stack.Toolbar.Label> </Stack.Toolbar.MenuAction> ))} </Stack.Toolbar.Menu> <Stack.Toolbar.Menu title="Sort projects"> <Stack.Toolbar.Label>Sort projects</Stack.Toolbar.Label> {PROJECT_SORT_OPTIONS.map((option) => ( <Stack.Toolbar.MenuAction key={option.value} isOn={props.projectSortOrder === option.value} onPress={() => props.onProjectSortOrderChange(option.value)} > <Stack.Toolbar.Label>{option.label}</Stack.Toolbar.Label> </Stack.Toolbar.MenuAction> ))} </Stack.Toolbar.Menu> <Stack.Toolbar.Menu title="Sort threads"> <Stack.Toolbar.Label>Sort threads</Stack.Toolbar.Label> {THREAD_SORT_OPTIONS.map((option) => ( <Stack.Toolbar.MenuAction key={option.value} isOn={props.threadSortOrder === option.value} onPress={() => props.onThreadSortOrderChange(option.value)} > <Stack.Toolbar.Label>{option.label}</Stack.Toolbar.Label> </Stack.Toolbar.MenuAction> ))} </Stack.Toolbar.Menu> <Stack.Toolbar.Menu title="Group projects"> <Stack.Toolbar.Label>Group projects</Stack.Toolbar.Label> {PROJECT_GROUPING_OPTIONS.map((option) => ( <Stack.Toolbar.MenuAction key={option.value} isOn={props.projectGroupingMode === option.value} onPress={() => props.onProjectGroupingModeChange(option.value)} subtitle={option.subtitle} > <Stack.Toolbar.Label>{option.label}</Stack.Toolbar.Label> </Stack.Toolbar.MenuAction> ))} </Stack.Toolbar.Menu> </Stack.Toolbar.Menu> <Stack.Toolbar.Button accessibilityLabel="Open settings" icon="gearshape" onPress={props.onOpenSettings} separateBackground /> </Stack.Toolbar> <Stack.Toolbar placement="bottom"> <Stack.Toolbar.SearchBarSlot /> <Stack.Toolbar.Spacer width={8} sharesBackground={false} /> <Stack.Toolbar.Button accessibilityLabel="New task" icon="square.and.pencil" onPress={props.onStartNewTask} separateBackground /> </Stack.Toolbar> </> );}
import type {
EnvironmentId,
SidebarProjectGroupingMode,
SidebarThreadSortOrder,
} from "@t3tools/contracts";
import {
DEFAULT_SIDEBAR_PROJECT_GROUPING_MODE,
DEFAULT_SIDEBAR_PROJECT_SORT_ORDER,
DEFAULT_SIDEBAR_THREAD_SORT_ORDER,
} from "@t3tools/contracts";
import { Stack } from "expo-router";
import { Text as RNText, View } from "react-native";
import { useThemeColor } from "../../lib/useThemeColor";
import { MOBILE_TYPOGRAPHY } from "../../lib/typography";
import type { HomeProjectSortOrder } from "./homeThreadList";
export interface HomeHeaderEnvironment {
readonly environmentId: EnvironmentId;
readonly label: string;
}
const PROJECT_SORT_OPTIONS: ReadonlyArray<{
readonly value: HomeProjectSortOrder;
readonly label: string;
}> = [
{ value: "updated_at", label: "Last user message" },
{ value: "created_at", label: "Created at" },
];
const THREAD_SORT_OPTIONS: ReadonlyArray<{
readonly value: SidebarThreadSortOrder;
readonly label: string;
}> = [
{ value: "updated_at", label: "Last user message" },
{ value: "created_at", label: "Created at" },
];
const PROJECT_GROUPING_OPTIONS: ReadonlyArray<{
readonly value: SidebarProjectGroupingMode;
readonly label: string;
readonly subtitle: string;
}> = [
{
value: "repository",
label: "Group by repository",
subtitle: "Combine matching repositories across environments",
},
{
value: "repository_path",
label: "Group by repository path",
subtitle: "Combine only matching paths within a repository",
},
{
value: "separate",
label: "Keep separate",
subtitle: "Show every project path separately",
},
];
export function HomeHeader(props: {
readonly environments: ReadonlyArray<HomeHeaderEnvironment>;
readonly selectedEnvironmentId: EnvironmentId | null;
readonly projectSortOrder: HomeProjectSortOrder;
readonly threadSortOrder: SidebarThreadSortOrder;
readonly projectGroupingMode: SidebarProjectGroupingMode;
readonly onSearchQueryChange: (query: string) => void;
readonly onEnvironmentChange: (environmentId: EnvironmentId | null) => void;
readonly onProjectSortOrderChange: (sortOrder: HomeProjectSortOrder) => void;
readonly onThreadSortOrderChange: (sortOrder: SidebarThreadSortOrder) => void;
readonly onProjectGroupingModeChange: (mode: SidebarProjectGroupingMode) => void;
readonly onOpenSettings: () => void;
readonly onStartNewTask: () => void;
}) {
const iconColor = useThemeColor("--color-icon");
const mutedColor = useThemeColor("--color-foreground-muted");
const subtleColor = useThemeColor("--color-subtle");
const hasCustomListOptions =
props.selectedEnvironmentId !== null ||
props.projectSortOrder !== DEFAULT_SIDEBAR_PROJECT_SORT_ORDER ||
props.threadSortOrder !== DEFAULT_SIDEBAR_THREAD_SORT_ORDER ||
props.projectGroupingMode !== DEFAULT_SIDEBAR_PROJECT_GROUPING_MODE;
return (
<>
<Stack.Screen
options={{
headerShown: true,
headerTransparent: true,
headerStyle: { backgroundColor: "transparent" },
headerShadowVisible: false,
headerTintColor: iconColor,
headerTitle: "",
headerSearchBarOptions: {
placeholder: "Search threads",
hideNavigationBar: false,
onChangeText: (event) => {
props.onSearchQueryChange(event.nativeEvent.text);
},
onCancelButtonPress: () => {
props.onSearchQueryChange("");
},
allowToolbarIntegration: true,
},
}}
/>
<Stack.Toolbar placement="left">
<Stack.Toolbar.View hidesSharedBackground>
<View
style={{
width: 128,
height: 32,
flexDirection: "row",
alignItems: "center",
gap: 8,
}}
>
<RNText
style={{
fontFamily: "DMSans_700Bold",
fontSize: MOBILE_TYPOGRAPHY.headline.fontSize,
color: iconColor,
letterSpacing: -0.4,
}}
>
T3 Code
</RNText>
<View
style={{
backgroundColor: subtleColor,
borderRadius: 99,
paddingHorizontal: 8,
paddingVertical: 3,
}}
>
<RNText
style={{
fontFamily: "DMSans_700Bold",
fontSize: MOBILE_TYPOGRAPHY.micro.fontSize,
color: mutedColor,
letterSpacing: 1.1,
textTransform: "uppercase",
}}
>
Alpha
</RNText>
</View>
</View>
</Stack.Toolbar.View>
</Stack.Toolbar>
<Stack.Toolbar placement="right">
<Stack.Toolbar.Menu
accessibilityLabel="Filter and sort threads"
icon={
hasCustomListOptions
? "line.3.horizontal.decrease.circle.fill"
: "line.3.horizontal.decrease.circle"
}
separateBackground
title="Thread list options"
>
<Stack.Toolbar.Menu title="Environment">
<Stack.Toolbar.Label>Environment</Stack.Toolbar.Label>
<Stack.Toolbar.MenuAction
isOn={props.selectedEnvironmentId === null}
onPress={() => props.onEnvironmentChange(null)}
subtitle="Show threads from every environment"
>
<Stack.Toolbar.Label>All environments</Stack.Toolbar.Label>
</Stack.Toolbar.MenuAction>
{props.environments.map((environment) => (
<Stack.Toolbar.MenuAction
key={environment.environmentId}
isOn={props.selectedEnvironmentId === environment.environmentId}
onPress={() => props.onEnvironmentChange(environment.environmentId)}
>
<Stack.Toolbar.Label>{environment.label}</Stack.Toolbar.Label>
</Stack.Toolbar.MenuAction>
))}
</Stack.Toolbar.Menu>
<Stack.Toolbar.Menu title="Sort projects">
<Stack.Toolbar.Label>Sort projects</Stack.Toolbar.Label>
{PROJECT_SORT_OPTIONS.map((option) => (
<Stack.Toolbar.MenuAction
key={option.value}
isOn={props.projectSortOrder === option.value}
onPress={() => props.onProjectSortOrderChange(option.value)}
>
<Stack.Toolbar.Label>{option.label}</Stack.Toolbar.Label>
</Stack.Toolbar.MenuAction>
))}
</Stack.Toolbar.Menu>
<Stack.Toolbar.Menu title="Sort threads">
<Stack.Toolbar.Label>Sort threads</Stack.Toolbar.Label>
{THREAD_SORT_OPTIONS.map((option) => (
<Stack.Toolbar.MenuAction
key={option.value}
isOn={props.threadSortOrder === option.value}
onPress={() => props.onThreadSortOrderChange(option.value)}
>
<Stack.Toolbar.Label>{option.label}</Stack.Toolbar.Label>
</Stack.Toolbar.MenuAction>
))}
</Stack.Toolbar.Menu>
<Stack.Toolbar.Menu title="Group projects">
<Stack.Toolbar.Label>Group projects</Stack.Toolbar.Label>
{PROJECT_GROUPING_OPTIONS.map((option) => (
<Stack.Toolbar.MenuAction
key={option.value}
isOn={props.projectGroupingMode === option.value}
onPress={() => props.onProjectGroupingModeChange(option.value)}
subtitle={option.subtitle}
>
<Stack.Toolbar.Label>{option.label}</Stack.Toolbar.Label>
</Stack.Toolbar.MenuAction>
))}
</Stack.Toolbar.Menu>
</Stack.Toolbar.Menu>
<Stack.Toolbar.Button
accessibilityLabel="Open settings"
icon="gearshape"
onPress={props.onOpenSettings}
separateBackground
/>
</Stack.Toolbar>
<Stack.Toolbar placement="bottom">
<Stack.Toolbar.SearchBarSlot />
<Stack.Toolbar.Spacer width={8} sharesBackground={false} />
<Stack.Toolbar.Button
accessibilityLabel="New task"
icon="square.and.pencil"
onPress={props.onStartNewTask}
separateBackground
/>
</Stack.Toolbar>
</>
);
}