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

ThreadSettingsRows.shared.tsx

163 lines6.0 KB

1import type { ReactNode } from "react";
2import { Pressable, View } from "react-native";
3import { AppText as Text } from "../../components/ui/AppText";
4import { SymbolView } from "../../components/ui/AppSymbol";
5import { cn } from "../../lib/cn";
6import type { ModelOption } from "../../lib/modelOptions";
7​
8export type ModelRowProps = {
9 readonly option: ModelOption;
10 readonly selected: boolean;
11 readonly onPress: () => void;
12 readonly isFavorite: boolean;
13 readonly favoritesLoaded: boolean;
14 readonly onToggleFavorite: () => void;
15 readonly onToggleHidden?: () => void;
16 readonly hiddenUpdatePending?: boolean;
17 readonly isFirst: boolean;
18 readonly isLast: boolean;
19};
20​
21export type ChoiceRowProps = {
22 readonly label: string;
23 readonly description?: string;
24 readonly selected: boolean;
25 readonly disabled?: boolean;
26 readonly onPress: () => void;
27 readonly isLast: boolean;
28};
29​
30type RowSelectionProps = {
31 readonly leadingSelection?: ReactNode;
32 readonly trailingSelection?: ReactNode;
33 readonly minimumHeight?: number;
34};
35​
36export function ModelRowContent(
37 props: ModelRowProps &
38 RowSelectionProps & {
39 readonly labelNumberOfLines: number;
40 readonly selectedClassName?: string;
41 },
42) {
43 return (
44 <View
45 style={props.minimumHeight === undefined ? undefined : { minHeight: props.minimumHeight }}
46 className={cn(
47 "mx-4 min-h-11 flex-row items-center gap-2 bg-grouped-card px-4",
48 props.selectedClassName,
49 props.isFirst && "rounded-t-2xl",
50 props.isLast ? "rounded-b-2xl" : "border-b border-border-subtle",
51 )}
52 >
53 <Pressable
54 accessibilityLabel={[props.option.label, props.option.subtitle].filter(Boolean).join(", ")}
55 accessibilityRole="radio"
56 accessibilityState={{
57 checked: props.selected,
58 disabled: props.option.isUnavailable === true || props.option.isHidden === true,
59 }}
60 className="min-h-11 min-w-0 flex-1 flex-row items-center gap-2 active:opacity-70"
61 disabled={props.option.isUnavailable || props.option.isHidden}
62 onPress={props.onPress}
63 >
64 {props.leadingSelection}
65 <View className="min-w-0 flex-1">
66 <View className="flex-row items-center gap-2">
67 <Text
68 className="min-w-0 shrink text-base font-t3-medium text-foreground"
69 numberOfLines={props.labelNumberOfLines}
70 >
71 {props.option.label}
72 </Text>
73 {props.option.isDefault ? (
74 <View className="rounded-md bg-subtle-strong px-1.5 py-0.5">
75 <Text className="text-3xs font-t3-bold text-foreground-muted">Default</Text>
76 </View>
77 ) : null}
78 {props.option.isLegacy ? (
79 <View className="rounded-md bg-subtle px-1.5 py-0.5">
80 <Text className="text-3xs font-t3-bold text-foreground-muted">Legacy</Text>
81 </View>
82 ) : null}
83 {props.option.isHidden ? (
84 <Text className="text-xs text-foreground-muted">Hidden</Text>
85 ) : null}
86 {props.option.isUnavailable ? (
87 <Text className="text-xs text-foreground">Unavailable</Text>
88 ) : null}
89 </View>
90 {props.option.subtitle ? (
91 <Text
92 className="text-xs text-foreground-muted"
93 numberOfLines={props.labelNumberOfLines}
94 >
95 {props.option.subtitle}
96 </Text>
97 ) : null}
98 </View>
99 {props.trailingSelection}
100 </Pressable>
101 {props.onToggleHidden ? (
102 <Pressable
103 accessibilityLabel={`${props.option.isHidden ? "Show" : "Hide"} model: ${props.option.providerLabel}, ${props.option.label}`}
104 accessibilityRole="button"
105 accessibilityState={{ disabled: props.hiddenUpdatePending === true }}
106 className="min-h-11 min-w-11 items-center justify-center"
107 disabled={props.hiddenUpdatePending}
108 onPress={props.onToggleHidden}
109 >
110 <SymbolView
111 name={props.option.isHidden ? "eye.slash" : "eye"}
112 size={18}
113 tintColorClassName={props.option.isHidden ? "accent-icon-subtle" : "accent-icon"}
114 type="monochrome"
115 />
116 </Pressable>
117 ) : null}
118 <Pressable
119 accessibilityLabel={`${props.isFavorite ? "Remove from" : "Add to"} favorites: ${
120 props.option.providerLabel
121 }, ${props.option.label}`}
122 accessibilityRole="button"
123 accessibilityState={{ disabled: !props.favoritesLoaded, selected: props.isFavorite }}
124 className="min-h-11 min-w-11 items-center justify-center"
125 disabled={!props.favoritesLoaded}
126 onPress={props.onToggleFavorite}
127 >
128 <SymbolView
129 name={props.isFavorite ? "star.fill" : "star"}
130 size={18}
131 tintColorClassName={props.isFavorite ? "accent-icon" : "accent-icon-subtle"}
132 type="monochrome"
133 />
134 </Pressable>
135 </View>
136 );
138​
139export function ChoiceRowContent(props: ChoiceRowProps & RowSelectionProps) {
140 return (
141 <Pressable
142 accessibilityLabel={props.description ? `${props.label}. ${props.description}` : props.label}
143 accessibilityRole="radio"
144 accessibilityState={{ checked: props.selected, disabled: props.disabled === true }}
145 disabled={props.disabled}
146 onPress={props.onPress}
147 style={props.minimumHeight === undefined ? undefined : { minHeight: props.minimumHeight }}
148 className={cn(
149 "min-h-14 flex-row items-center gap-3 bg-grouped-card px-4 py-3 active:bg-subtle",
150 !props.isLast && "border-b border-border-subtle",
151 )}
152 >
153 {props.leadingSelection}
154 <View className={cn("min-w-0 flex-1 gap-0.5", props.disabled && "opacity-50")}>
155 <Text className="text-base font-t3-medium text-foreground">{props.label}</Text>
156 {props.description ? (
157 <Text className="text-sm leading-5 text-foreground-muted">{props.description}</Text>
158 ) : null}
159 </View>
160 {props.trailingSelection}
161 </Pressable>
162 );