authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-10-11 22:31:14-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-10-11 22:31:14-07:00
log9a2b9a2d4040c7e78c03a95d59b76b92e6bf2957
tree4151df94449e4e56bb398509f4a7ac96d267a18b
parenta39fd7be954379dfedab0f1d1e2fd8c02d748e9b
signature Signed by PGP key B5690EEEBB952194

refactor(mobile): share themed toggle rows in settings (#18511)


3 files changed, 53 insertions(+), 77 deletions(-)

apps/mobile/src/components/ListToggleRow.tsx created+45
...@@ -0,0 +1,45 @@
1import { Pressable } from "react-native";
2
3import { AppText } from "./AppText";
4import { ListRowLayout, type ListRowContentProps } from "./ListRowLayout";
5import { ThemedSwitch } from "./ThemedSwitch";
6
7/** A null value means the selected environments disagree; activation sets them all on. */
8export function ListToggleRow(
9 props: Omit<ListRowContentProps, "detail"> & {
10 readonly value: boolean | null;
11 readonly onValueChange: (value: boolean) => void;
12 },
13) {
14 return (
15 <ListRowLayout
16 icon={props.icon}
17 label={props.label}
18 description={props.description}
19 disabled={props.disabled}
20 accessory={
21 props.value === null ? (
22 <Pressable
23 accessibilityLabel={`Set ${props.label} on for selected environments`}
24 accessibilityRole="button"
25 accessibilityHint={props.description}
26 accessibilityState={{ disabled: Boolean(props.disabled) }}
27 disabled={props.disabled}
28 className="rounded-full bg-subtle px-3 py-2 active:opacity-70"
29 onPress={() => props.onValueChange(true)}
30 >
31 <AppText className="text-sm font-t3-medium text-foreground">Mixed · Set on</AppText>
32 </Pressable>
33 ) : (
34 <ThemedSwitch
35 accessibilityLabel={props.label}
36 accessibilityHint={props.description}
37 disabled={props.disabled}
38 onValueChange={props.onValueChange}
39 value={props.value}
40 />
41 )
42 }
43 />
44 );
45}
apps/mobile/src/features/settings/components/SettingsControlRow.tsx deleted-39
...@@ -1,39 +0,0 @@
1import type { ComponentProps, ReactNode } from "react";
2import { Platform, View } from "react-native";
3
4import { SymbolView } from "../../../components/AppSymbol";
5import { AppText as Text } from "../../../components/AppText";
6import { cn } from "../../../lib/cn";
7
8export function SettingsControlRow(props: {
9 readonly disabled?: boolean;
10 readonly icon: ComponentProps<typeof SymbolView>["name"];
11 readonly label: string;
12 readonly subtitle?: string;
13 readonly children: ReactNode;
14}) {
15 return (
16 <View
17 className={cn(
18 "flex-row items-center gap-4 p-4 android:min-h-14 android:py-3",
19 props.subtitle && "android:min-h-18",
20 props.disabled && "opacity-[0.45]",
21 )}
22 >
23 <SymbolView
24 name={props.icon}
25 size={Platform.OS === "android" ? 24 : 22}
26 tintColorClassName="accent-icon"
27 type="monochrome"
28 weight="regular"
29 />
30 <View className="min-w-0 flex-1 android:gap-1">
31 <Text className="text-lg text-foreground android:text-base">{props.label}</Text>
32 {props.subtitle ? (
33 <Text className="text-sm text-foreground-muted">{props.subtitle}</Text>
34 ) : null}
35 </View>
36 {props.children}
37 </View>
38 );
39}
apps/mobile/src/features/settings/components/SettingsSwitchRow.tsx+8-38
...@@ -1,42 +1,12 @@...@@ -1,42 +1,12 @@
1import type { ComponentProps } from "react";1import type { ComponentProps } from "react";
2import { Pressable } from "react-native";
32
4import { AppText as Text } from "../../../components/AppText";3import { ListToggleRow } from "../../../components/ListToggleRow";
5import { ThemedSwitch } from "../../../components/ThemedSwitch";
6import { SettingsControlRow } from "./SettingsControlRow";
74
8export function SettingsSwitchRow(5export function SettingsSwitchRow({
9 props: Omit<ComponentProps<typeof SettingsControlRow>, "children"> & {6 subtitle,
10 readonly value: boolean | null;7 ...props
11 readonly onValueChange: (value: boolean) => void;8}: Omit<ComponentProps<typeof ListToggleRow>, "description"> & {
12 },9 readonly subtitle?: string;
13) {10}) {
14 return (11 return <ListToggleRow {...props} description={subtitle} />;
15 <SettingsControlRow
16 disabled={props.disabled}
17 icon={props.icon}
18 label={props.label}
19 subtitle={props.subtitle}
20 >
21 {props.value === null ? (
22 <Pressable
23 accessibilityLabel={`Set ${props.label} on for selected environments`}
24 accessibilityRole="button"
25 disabled={props.disabled}
26 className="rounded-full bg-subtle px-3 py-2 active:opacity-70"
27 onPress={() => props.onValueChange(true)}
28 >
29 <Text className="text-sm font-t3-medium text-foreground">Mixed · Set on</Text>
30 </Pressable>
31 ) : (
32 <ThemedSwitch
33 style={{ alignSelf: "center" }}
34 accessibilityLabel={props.label}
35 disabled={props.disabled}
36 onValueChange={props.onValueChange}
37 value={props.value}
38 />
39 )}
40 </SettingsControlRow>
41 );
42}12}