diff --git a/apps/mobile/src/features/cloud/T3ConnectProfilePage.tsx b/apps/mobile/src/features/cloud/T3ConnectProfilePage.tsx index 73e9f40eb192..bab46165cfaa 100644 --- a/apps/mobile/src/features/cloud/T3ConnectProfilePage.tsx +++ b/apps/mobile/src/features/cloud/T3ConnectProfilePage.tsx @@ -7,7 +7,7 @@ import type { MenuAction } from "@react-native-menu/menu"; import type { EnvironmentId } from "@t3tools/contracts"; import type { RelayClientEnvironmentRecord } from "@t3tools/contracts/relay"; import { type ReactNode, useRef, useState } from "react"; -import { Alert, Pressable, RefreshControl, ScrollView, Text, View } from "react-native"; +import { Pressable, RefreshControl, ScrollView, Text, View } from "react-native"; import { AppActivityIndicator } from "../../components/AppActivityIndicator"; import { SymbolView } from "../../components/AppSymbol"; @@ -95,7 +95,7 @@ export function T3ConnectProfilePage() { traceId, cause, }); - Alert.alert( + Dialog.alert( "Could not deregister server", traceId ? `${message}\n\nTrace ID: ${traceId}` : message, traceId diff --git a/apps/mobile/src/features/connection/ConnectionEnvironmentRow.tsx b/apps/mobile/src/features/connection/ConnectionEnvironmentRow.tsx index ef6c15ab7421..65cb19e38491 100644 --- a/apps/mobile/src/features/connection/ConnectionEnvironmentRow.tsx +++ b/apps/mobile/src/features/connection/ConnectionEnvironmentRow.tsx @@ -1,3 +1,4 @@ +import * as Dialog from "../../components/Dialog"; import { ConnectionTraceId } from "./ConnectionTraceId"; import { SymbolView } from "../../components/AppSymbol"; import type { AtomCommandResult } from "@t3tools/client-runtime/state/runtime"; @@ -6,7 +7,7 @@ import { useAtomValue } from "@effect/atom-react"; import * as Cause from "effect/Cause"; import { AsyncResult } from "effect/reactivity"; import { useCallback, useState } from "react"; -import { Platform, Alert, Pressable, View } from "react-native"; +import { Platform, Pressable, View } from "react-native"; import Animated, { FadeIn, FadeOut, LinearTransition } from "react-native-reanimated"; import { AppText as Text } from "../../components/AppText"; @@ -70,7 +71,7 @@ export function ConnectionEnvironmentRow(props: { return; } const error = Cause.squash(result.cause); - Alert.alert( + Dialog.alert( "Could not update environment", error instanceof Error ? error.message : "The environment could not be updated.", ); diff --git a/apps/mobile/src/features/connection/ConnectionsNewRouteScreen.tsx b/apps/mobile/src/features/connection/ConnectionsNewRouteScreen.tsx index 89580939d4c5..51f1b57d3b5a 100644 --- a/apps/mobile/src/features/connection/ConnectionsNewRouteScreen.tsx +++ b/apps/mobile/src/features/connection/ConnectionsNewRouteScreen.tsx @@ -1,3 +1,4 @@ +import * as Dialog from "../../components/Dialog"; import { ScreenScrollView as ScrollView } from "../../components/ScreenScrollView"; import { useAuth } from "@clerk/expo"; import { CameraView, useCameraPermissions } from "expo-camera"; @@ -12,7 +13,7 @@ import { import type { EnvironmentId } from "@t3tools/contracts"; import { AsyncResult } from "effect/reactivity"; import { useCallback, useEffect, useRef, useState } from "react"; -import { Alert, Linking, Pressable, StyleSheet, View } from "react-native"; +import { Linking, Pressable, StyleSheet, View } from "react-native"; import Svg, { Defs, RadialGradient, Rect, Stop } from "react-native-svg"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { cn } from "../../lib/cn"; @@ -116,7 +117,7 @@ export function ConnectionsNewRouteScreen({ return; } if (permission.canAskAgain) return; - Alert.alert( + Dialog.alert( "Camera access needed", "Camera access was denied for this app. Open Settings to enable it.", [ @@ -144,7 +145,7 @@ export function ConnectionsNewRouteScreen({ setScannerLocked(false); } catch (error) { void Haptics.notificationAsync(Haptics.NotificationFeedbackType.Error); - Alert.alert( + Dialog.alert( "Invalid QR code", error instanceof Error ? error.message : "Scanned QR code was not recognized.", ); diff --git a/apps/mobile/src/features/connection/GitHubRoutingSettings.tsx b/apps/mobile/src/features/connection/GitHubRoutingSettings.tsx index 3358fa1995d6..5952573952c0 100644 --- a/apps/mobile/src/features/connection/GitHubRoutingSettings.tsx +++ b/apps/mobile/src/features/connection/GitHubRoutingSettings.tsx @@ -1,3 +1,4 @@ +import * as Dialog from "../../components/Dialog"; import { useAtomValue } from "@effect/atom-react"; import { connectionCatalogDisplayUrl, @@ -6,7 +7,7 @@ import { type GitHubRoutingPermission, } from "@t3tools/client-runtime/connection"; import { useState } from "react"; -import { Alert, Pressable, View } from "react-native"; +import { Pressable, View } from "react-native"; import { AppText as Text } from "../../components/AppText"; import { SymbolView } from "../../components/AppSymbol"; @@ -86,7 +87,7 @@ export function GitHubRoutingSettings() { void update({ environmentId, permission: option.value }).then((result) => { setSaving(false); if (result._tag === "Failure") - Alert.alert( + Dialog.alert( "Could not save GitHub routing permission", "Try again before leaving this screen.", ); diff --git a/apps/mobile/src/features/settings/EnvironmentRoutesSection.tsx b/apps/mobile/src/features/settings/EnvironmentRoutesSection.tsx index 17b6bcadd880..204c1603ffe8 100644 --- a/apps/mobile/src/features/settings/EnvironmentRoutesSection.tsx +++ b/apps/mobile/src/features/settings/EnvironmentRoutesSection.tsx @@ -1,3 +1,4 @@ +import * as Dialog from "../../components/Dialog"; import { useAtomValue } from "@effect/atom-react"; import { type ConnectionRoute, @@ -14,7 +15,7 @@ import { AUTH_SCOPE_OPTIONS } from "@t3tools/shared/authScopeOptions"; import { AsyncResult } from "effect/reactivity"; import * as Option from "effect/Option"; import { useState } from "react"; -import { Alert, Platform, Pressable, View } from "react-native"; +import { Platform, Pressable, View } from "react-native"; import Reanimated, { ReduceMotion, useAnimatedStyle, withTiming } from "react-native-reanimated"; import { type AppSymbolName, SymbolView } from "../../components/AppSymbol"; @@ -118,7 +119,7 @@ export function EnvironmentRoutesSection({ return to; }; const confirmRemove = (route: ConnectionRoute) => - Alert.alert( + Dialog.alert( `Remove ${connectionRouteLabel(route)}?`, connectionRouteAddress(route) ?? undefined, [ diff --git a/apps/mobile/src/features/settings/SettingsAboutRouteScreen.tsx b/apps/mobile/src/features/settings/SettingsAboutRouteScreen.tsx index bbb70b236bea..bb0433ca9f05 100644 --- a/apps/mobile/src/features/settings/SettingsAboutRouteScreen.tsx +++ b/apps/mobile/src/features/settings/SettingsAboutRouteScreen.tsx @@ -1,7 +1,8 @@ +import * as Dialog from "../../components/Dialog"; import { ScreenScrollView as ScrollView } from "../../components/ScreenScrollView"; import Constants from "expo-constants"; import { useCallback, useEffect, useRef, useState } from "react"; -import { Alert, Pressable, View } from "react-native"; +import { Pressable, View } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { AppText as Text } from "../../components/AppText"; @@ -67,7 +68,7 @@ function AppSettingsSection() { // apply immediately instead of prompting. await runAppUpdateCheck({ applyMode: "immediate", - onFailure: (message) => Alert.alert("Update failed", message), + onFailure: (message) => Dialog.alert("Update failed", message), onStateChange: setUpdateState, }); } finally { diff --git a/apps/mobile/src/features/settings/SettingsEnvironmentDetailRouteScreen.tsx b/apps/mobile/src/features/settings/SettingsEnvironmentDetailRouteScreen.tsx index 1065a860a5e4..6053b7571a53 100644 --- a/apps/mobile/src/features/settings/SettingsEnvironmentDetailRouteScreen.tsx +++ b/apps/mobile/src/features/settings/SettingsEnvironmentDetailRouteScreen.tsx @@ -1,3 +1,4 @@ +import * as Dialog from "../../components/Dialog"; import { useAtomValue } from "@effect/atom-react"; import { useNavigation, type StaticScreenProps } from "@react-navigation/native"; import { @@ -9,7 +10,7 @@ import { import { squashAtomCommandFailure } from "@t3tools/client-runtime/state/runtime"; import { AsyncResult } from "effect/reactivity"; import { useEffect, useRef, useState } from "react"; -import { Alert, View } from "react-native"; +import { View } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { AppText as Text } from "../../components/AppText"; @@ -111,7 +112,7 @@ function EnvironmentDetail({ environmentId }: { readonly environmentId: Environm const targetVersion = checkedRelease?.targetVersion; if (disabled || !targetVersion || !capabilities || !supportsEnvironmentUpdate(capabilities)) return; - Alert.alert( + Dialog.alert( `Update ${environment?.environmentLabel ?? "environment"}?`, `Install T3 Code ${targetVersion}. ${capabilities.serverSelfUpdate === "desktop-managed" ? "The desktop app will close and relaunch." : "The server will restart and reconnect."} Running threads may be interrupted.`, [ diff --git a/apps/mobile/src/features/settings/SettingsNotificationsRouteScreen.tsx b/apps/mobile/src/features/settings/SettingsNotificationsRouteScreen.tsx index 25c2e03d46f7..b513cdda340e 100644 --- a/apps/mobile/src/features/settings/SettingsNotificationsRouteScreen.tsx +++ b/apps/mobile/src/features/settings/SettingsNotificationsRouteScreen.tsx @@ -1,3 +1,4 @@ +import * as Dialog from "../../components/Dialog"; import { AuthRelayWriteScope } from "@t3tools/contracts"; import { readEnvironmentScope } from "../../state/session"; import { ScreenScrollView as ScrollView } from "../../components/ScreenScrollView"; @@ -8,7 +9,7 @@ import { useNavigation } from "@react-navigation/native"; import * as Effect from "effect/Effect"; import { AsyncResult } from "effect/reactivity"; import { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react"; -import { Alert, AppState, Linking, Platform } from "react-native"; +import { AppState, Linking, Platform } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { AppText as Text } from "../../components/AppText"; @@ -162,7 +163,7 @@ function ConfiguredSettingsNotificationsRouteScreen() { if (result._tag === "Failure") { if (!isAtomCommandInterrupted(result)) { const error = squashAtomCommandFailure(result); - Alert.alert( + Dialog.alert( "Notifications unavailable", error instanceof Error ? error.message : "Could not request notification permission.", ); @@ -174,9 +175,9 @@ function ConfiguredSettingsNotificationsRouteScreen() { // Permission alone is not enough: the switch stays off until the relay // registration succeeds, so tell the user the truth about which happened. if (getAgentAwarenessRegistrationStatus() === "registered") { - Alert.alert("Notifications enabled", "Agent notifications are enabled for this device."); + Dialog.alert("Notifications enabled", "Agent notifications are enabled for this device."); } else { - Alert.alert( + Dialog.alert( "Couldn't finish enabling notifications", "Notification access was granted, but this device could not be registered with T3 Connect. Notifications will start once registration succeeds.", ); @@ -185,7 +186,7 @@ function ConfiguredSettingsNotificationsRouteScreen() { } if (result.value.type === "unsupported") { setNotificationStatus("unsupported"); - Alert.alert( + Dialog.alert( "Notifications unavailable", "Agent notifications are unavailable on this platform.", ); @@ -193,10 +194,10 @@ function ConfiguredSettingsNotificationsRouteScreen() { } setNotificationStatus("disabled"); if (result.value.canAskAgain) { - Alert.alert("Notifications disabled", "Notifications were not enabled."); + Dialog.alert("Notifications disabled", "Notifications were not enabled."); return; } - Alert.alert( + Dialog.alert( "Notifications disabled", "Notifications were denied for this app. Open Settings to enable them.", [ @@ -207,7 +208,7 @@ function ConfiguredSettingsNotificationsRouteScreen() { }, []); const promptSignIn = useCallback(() => { - Alert.alert( + Dialog.alert( "Sign in to T3 Connect", "Live Activity updates require T3 Connect so relay can deliver updates to this device.", [ @@ -234,7 +235,7 @@ function ConfiguredSettingsNotificationsRouteScreen() { if (permission._tag === "Failure") { setLiveActivityStatus("disabled"); const error = squashAtomCommandFailure(permission); - Alert.alert( + Dialog.alert( "Ongoing activity unavailable", error instanceof Error ? error.message : "Could not enable agent notifications.", ); @@ -242,7 +243,7 @@ function ConfiguredSettingsNotificationsRouteScreen() { } if (permission.value.type !== "granted") { setLiveActivityStatus("disabled"); - Alert.alert( + Dialog.alert( "Notification permission needed", "Enable notifications in system Settings to show ongoing agent activity.", [ @@ -258,7 +259,7 @@ function ConfiguredSettingsNotificationsRouteScreen() { if (tokenResult._tag === "Failure") { setLiveActivityStatus("disabled"); const error = squashAtomCommandFailure(tokenResult); - Alert.alert( + Dialog.alert( Platform.OS === "android" ? "Ongoing activity unavailable" : "Live Activities unavailable", error instanceof Error ? error.message : "Could not enable agent activity updates.", ); @@ -286,7 +287,7 @@ function ConfiguredSettingsNotificationsRouteScreen() { setLiveActivityStatus("disabled"); if (!isAtomCommandInterrupted(updateResult)) { const error = squashAtomCommandFailure(updateResult); - Alert.alert( + Dialog.alert( Platform.OS === "android" ? "Ongoing activity unavailable" : "Live Activities unavailable", @@ -303,14 +304,14 @@ function ConfiguredSettingsNotificationsRouteScreen() { // (the push-to-start token the relay needs) has not — don't claim Live // Activities are live until the device is actually registered. if (getAgentAwarenessRegistrationStatus() === "registered") { - Alert.alert( + Dialog.alert( Platform.OS === "android" ? "Ongoing activity enabled" : "Live Activities enabled", environmentCount > 0 ? "Agent activity updates are enabled for environments this connection can configure." : "Agent activity updates are enabled. Add an environment to start receiving updates.", ); } else { - Alert.alert( + Dialog.alert( "Couldn't finish enabling activity updates", "This device could not be registered with T3 Connect, so activity updates won't appear yet. They'll start once registration succeeds.", ); @@ -336,7 +337,7 @@ function ConfiguredSettingsNotificationsRouteScreen() { return; } - Alert.alert( + Dialog.alert( "Disable notifications", "Open system Settings to disable notifications for T3 Code.", [ @@ -490,7 +491,7 @@ function ConfiguredSettingsNotificationsRouteScreen() { label="Live Update Settings" onPress={() => { void openAndroidLiveUpdateSettings().catch(() => { - Alert.alert( + Dialog.alert( "Couldn't open Settings", "Open Android Settings, select T3 Code, then enable Live Updates in Notifications.", ); diff --git a/apps/mobile/src/features/settings/SettingsProviderAccountsRouteScreen.tsx b/apps/mobile/src/features/settings/SettingsProviderAccountsRouteScreen.tsx index e0f3e2ecc69e..0ce5bb17b949 100644 --- a/apps/mobile/src/features/settings/SettingsProviderAccountsRouteScreen.tsx +++ b/apps/mobile/src/features/settings/SettingsProviderAccountsRouteScreen.tsx @@ -1,3 +1,4 @@ +import * as Dialog from "../../components/Dialog"; import { isAtomCommandInterrupted, squashAtomCommandFailure, @@ -5,7 +6,7 @@ import { } from "@t3tools/client-runtime/state/runtime"; import type { ProviderAuthResponse, ServerProvider } from "@t3tools/contracts"; import { useRef, useState } from "react"; -import { Alert, Linking, Pressable, ScrollView, TextInput, View } from "react-native"; +import { Linking, Pressable, ScrollView, TextInput, View } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { AppText as Text } from "../../components/AppText"; @@ -333,7 +334,7 @@ function ProviderAccount({ tone="danger" disabled={disabled || state === null} onPress={() => - Alert.alert( + Dialog.alert( "Sign out?", `Running threads sharing this sign-in on ${environment.label} will stop. Thread history is kept.`, [ diff --git a/apps/mobile/src/features/settings/SettingsScheduledTasksRouteScreen.tsx b/apps/mobile/src/features/settings/SettingsScheduledTasksRouteScreen.tsx index 3de5a18e18c0..042710168c6f 100644 --- a/apps/mobile/src/features/settings/SettingsScheduledTasksRouteScreen.tsx +++ b/apps/mobile/src/features/settings/SettingsScheduledTasksRouteScreen.tsx @@ -1,3 +1,4 @@ +import * as Dialog from "../../components/Dialog"; import { useAtomValue } from "@effect/atom-react"; import { AuthOrchestrationOperateScope, @@ -36,7 +37,7 @@ import { } from "react"; import { useFocusEffect, useNavigation, usePreventRemove } from "@react-navigation/native"; import type { NativeStackNavigationProp } from "@react-navigation/native-stack"; -import { Alert, AppState, Platform, Pressable, TextInput as RNTextInput, View } from "react-native"; +import { AppState, Platform, Pressable, TextInput as RNTextInput, View } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { Button } from "../../components/Button"; @@ -447,10 +448,10 @@ function SettingsScheduledTaskEditorScreen({ title }: { readonly title: string } const preventRemove = !saved && (hasChanges || saving); usePreventRemove(preventRemove, ({ data }) => { if (saving) { - Alert.alert("Saving task", "Wait for the task to finish saving before leaving."); + Dialog.alert("Saving task", "Wait for the task to finish saving before leaving."); return; } - Alert.alert("Discard changes?", "Your unsaved changes will be lost.", [ + Dialog.alert("Discard changes?", "Your unsaved changes will be lost.", [ { text: "Keep editing", style: "cancel" }, { text: "Discard changes", @@ -609,7 +610,7 @@ function TaskForm({ const failure = (title: string, result: AtomCommandResult) => { if (result._tag === "Failure" && !isAtomCommandInterrupted(result)) { - Alert.alert(title, String(squashAtomCommandFailure(result))); + Dialog.alert(title, String(squashAtomCommandFailure(result))); } }; @@ -635,7 +636,7 @@ function TaskForm({ draft.schedule.mode === "webhook" && parseMaxDeliveryAge(draft.schedule.maxDeliveryAgeMinutes) === undefined ) { - Alert.alert( + Dialog.alert( "Invalid age limit", `Enter whole minutes from 1 to ${MAX_WEBHOOK_DELIVERY_AGE_MINUTES}, or leave it blank.`, ); @@ -649,14 +650,14 @@ function TaskForm({ !schedule || (draft.workspace === "existing_worktree" && !draft.checkoutPath.trim()) ) { - Alert.alert( + Dialog.alert( "Incomplete task", "Add a name, prompt, project, model, valid schedule, and checkout path if needed.", ); return; } if (!projects.some((project) => project.id === draft.projectId)) { - Alert.alert("Project unavailable", "Choose a project in this environment."); + Dialog.alert("Project unavailable", "Choose a project in this environment."); return; } const input: ScheduledTaskUpsertInput = { @@ -1089,7 +1090,7 @@ function WebhookScheduleDetails({ accessibilityState={{ disabled: disabled || rotating }} disabled={disabled || rotating} onPress={() => - Alert.alert("Rotate URL?", "The current URL stops working immediately.", [ + Dialog.alert("Rotate URL?", "The current URL stops working immediately.", [ { text: "Cancel", style: "cancel" }, { text: "Rotate", @@ -1099,7 +1100,7 @@ function WebhookScheduleDetails({ void rotate({ environmentId, input: { id: task.id } }).then((result) => { setRotating(false); if (result._tag === "Failure" && !isAtomCommandInterrupted(result)) { - Alert.alert( + Dialog.alert( "Could not rotate URL", String(squashAtomCommandFailure(result)), ); @@ -1161,7 +1162,7 @@ function EnvironmentTasks({ }); const failure = (title: string, result: AtomCommandResult) => { if (result._tag === "Failure" && !isAtomCommandInterrupted(result)) { - Alert.alert(title, String(squashAtomCommandFailure(result))); + Dialog.alert(title, String(squashAtomCommandFailure(result))); } }; @@ -1254,7 +1255,7 @@ function EnvironmentTasks({ if (action === "edit") { onEdit(task); } else if (action === "delete") { - Alert.alert("Delete task?", task.title, [ + Dialog.alert("Delete task?", task.title, [ { text: "Cancel", style: "cancel" }, { text: "Delete",