authorgravatar for danpvernon@gmail.comDaniel Vernon <danpvernon@gmail.com> 2026-08-15 13:00:18+01:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-08-15 12:00:18+00:00
log4db50757c0b618293997a7f81bcfe30b68356969
tree0d89eb0d9e7027ec427055260a3188d63da4c2c7
parent270489b887420db3319898ab4046516e4c457711
signature Signed by PGP key B5690EEEBB952194

fix(mobile): explain iOS-only settings on Android (#4981)


4 files changed, 40 insertions(+), 1 deletions(-)

apps/mobile/src/features/settings/SettingsRouteScreen.logic.test.ts created+19
......@@ -0,0 +1,19 @@
1import { describe, expect, it } from "vite-plus/test";
2
3import { resolveAgentAwarenessPlatformPresentation } from "./SettingsRouteScreen.logic";
4
5describe("resolveAgentAwarenessPlatformPresentation", () => {
6 it("explains that agent awareness settings are unavailable on Android", () => {
7 expect(resolveAgentAwarenessPlatformPresentation("android")).toEqual({
8 supported: false,
9 subtitle: "iOS only",
10 });
11 });
12
13 it("leaves supported iOS settings unchanged", () => {
14 expect(resolveAgentAwarenessPlatformPresentation("ios")).toEqual({
15 supported: true,
16 subtitle: undefined,
17 });
18 });
19});
apps/mobile/src/features/settings/SettingsRouteScreen.logic.ts created+8
......@@ -0,0 +1,8 @@
1export function resolveAgentAwarenessPlatformPresentation(platform: string): {
2 readonly supported: boolean;
3 readonly subtitle: string | undefined;
4} {
5 return platform === "ios"
6 ? { supported: true, subtitle: undefined }
7 : { supported: false, subtitle: "iOS only" };
8}
apps/mobile/src/features/settings/SettingsRouteScreen.tsx+6
......@@ -46,6 +46,7 @@ import { useSavedRemoteConnections } from "../../state/use-remote-environment-re
4646import { SettingsRow } from "./components/SettingsRow";
4747import { SettingsSection } from "./components/SettingsSection";
4848import { SettingsSwitchRow } from "./components/SettingsSwitchRow";
49import { resolveAgentAwarenessPlatformPresentation } from "./SettingsRouteScreen.logic";
4950
5051type NotificationStatus = "checking" | "enabled" | "disabled" | "unsupported";
5152type LiveActivityStatus = "checking" | "enabled" | "disabled" | "signed-out" | "linking";
......@@ -144,6 +145,7 @@ function ConfiguredSettingsRouteScreen() {
144145 const preferencesResult = useAtomValue(mobilePreferencesAtom);
145146 const savePreferences = useAtomSet(updateMobilePreferencesAtom);
146147 const agentAwarenessPushAvailable = supportsAgentAwarenessPush();
148 const agentAwarenessPlatform = resolveAgentAwarenessPlatformPresentation(Platform.OS);
147149 const insets = useSafeAreaInsets();
148150 const navigation = useNavigation();
149151 const { getToken, isLoaded, isSignedIn } = useAuth({ treatPendingAsSignedOut: false });
......@@ -473,10 +475,12 @@ function ConfiguredSettingsRouteScreen() {
473475 icon="bell.badge"
474476 label="Device Notifications"
475477 disabled={
478 !agentAwarenessPlatform.supported ||
476479 !agentAwarenessPushAvailable ||
477480 notificationStatus === "checking" ||
478481 notificationStatus === "unsupported"
479482 }
483 subtitle={agentAwarenessPlatform.subtitle}
480484 // Only reads as on when this device is actually registered with the
481485 // relay; otherwise notifications cannot be delivered regardless of
482486 // the local iOS permission.
......@@ -487,6 +491,7 @@ function ConfiguredSettingsRouteScreen() {
487491 />
488492 <SettingsSwitchRow
489493 disabled={
494 !agentAwarenessPlatform.supported ||
490495 !agentAwarenessPushAvailable ||
491496 !isLoaded ||
492497 liveActivityStatus === "checking" ||
......@@ -494,6 +499,7 @@ function ConfiguredSettingsRouteScreen() {
494499 }
495500 icon="bolt.circle"
496501 label="Live Activity Updates"
502 subtitle={agentAwarenessPlatform.subtitle}
497503 // Same gate: a saved preference is meaningless until the device
498504 // registration the relay needs to push updates has succeeded.
499505 value={
apps/mobile/src/features/settings/components/SettingsSwitchRow.tsx+7-1
......@@ -11,6 +11,7 @@ export function SettingsSwitchRow(props: {
1111 readonly disabled?: boolean;
1212 readonly icon: SymbolName;
1313 readonly label: string;
14 readonly subtitle?: string;
1415 readonly value: boolean;
1516 readonly onValueChange: (value: boolean) => void;
1617}) {
......@@ -27,7 +28,12 @@ export function SettingsSwitchRow(props: {
2728 }
2829 >
2930 <SymbolView name={props.icon} size={22} tintColor={icon} type="monochrome" weight="regular" />
30 <Text className="flex-1 text-lg text-foreground">{props.label}</Text>
31 <View className="min-w-0 flex-1">
32 <Text className="text-lg text-foreground">{props.label}</Text>
33 {props.subtitle ? (
34 <Text className="text-sm text-foreground-muted">{props.subtitle}</Text>
35 ) : null}
36 </View>
3137 <Switch
3238 disabled={props.disabled}
3339 ios_backgroundColor={track}