| author | |
| committer | |
| log | 1a2a03f44b7ef6e3159a67a262c1586dbd3d3d5b |
| tree | dcef67997eb5df90efaa4010b8bccc1c0fe421ee |
| parent | 18dcc43ace0a7b0d93f2fd61907b2a4e27522936 |
| signature | Signed by PGP key B5690EEEBB952194 |
3 files changed, 81 insertions(+), 83 deletions(-)
apps/mobile/src/features/connection/ConnectionsRouteScreen.tsx+10-41| ... | ... | @@ -1,17 +1,14 @@ |
| 1 | 1 | import { ScreenScrollView as ScrollView } from "../../components/ScreenScrollView"; |
| 2 | 2 | import { NativeHeaderToolbar } from "../../native/StackHeader"; |
| 3 | 3 | import { useNavigation } from "@react-navigation/native"; |
| 4 | import { SymbolView } from "../../components/AppSymbol"; | |
| 5 | 4 | import type { EnvironmentId } from "@t3tools/contracts"; |
| 6 | 5 | import { useCallback, useState } from "react"; |
| 7 | 6 | import { Platform, View } from "react-native"; |
| 8 | 7 | import { useSafeAreaInsets } from "react-native-safe-area-context"; |
| 9 | 8 | |
| 10 | 9 | import { AndroidScreenHeader } from "../../components/AndroidScreenHeader"; |
| 11 | import { AppText as Text } from "../../components/AppText"; | |
| 12 | import { cn } from "../../lib/cn"; | |
| 13 | 10 | import { useRemoteConnections } from "../../state/use-remote-environment-registry"; |
| 14 | import { ConnectionEnvironmentRow } from "./ConnectionEnvironmentRow"; | |
| 11 | import { LocalEnvironmentList } from "./LocalEnvironmentList"; | |
| 15 | 12 | import { GitHubRoutingSettings } from "./GitHubRoutingSettings"; |
| 16 | 13 | |
| 17 | 14 | export function ConnectionsRouteScreen() { |
| ... | ... | @@ -24,7 +21,6 @@ export function ConnectionsRouteScreen() { |
| 24 | 21 | } = useRemoteConnections(); |
| 25 | 22 | const navigation = useNavigation(); |
| 26 | 23 | const insets = useSafeAreaInsets(); |
| 27 | const hasEnvironments = connectedEnvironments.length > 0; | |
| 28 | 24 | const [expandedId, setExpandedId] = useState<EnvironmentId | null>(null); |
| 29 | 25 | const handleToggle = useCallback((environmentId: EnvironmentId) => { |
| 30 | 26 | setExpandedId((prev) => (prev === environmentId ? null : environmentId)); |
| ... | ... | @@ -63,42 +59,15 @@ export function ConnectionsRouteScreen() { |
| 63 | 59 | paddingTop: 16, |
| 64 | 60 | }} |
| 65 | 61 | > |
| 66 | {hasEnvironments ? ( | |
| 67 | <View collapsable={false} className="overflow-hidden rounded-[24px] bg-card"> | |
| 68 | {connectedEnvironments.map((environment, index) => ( | |
| 69 | <View | |
| 70 | key={environment.environmentId} | |
| 71 | collapsable={false} | |
| 72 | className={cn(index !== 0 && "border-t border-border")} | |
| 73 | > | |
| 74 | <ConnectionEnvironmentRow | |
| 75 | environment={environment} | |
| 76 | expanded={expandedId === environment.environmentId} | |
| 77 | onToggle={() => handleToggle(environment.environmentId)} | |
| 78 | onReconnect={onReconnectEnvironment} | |
| 79 | onRemove={onRemoveEnvironmentPress} | |
| 80 | onSetEnabled={onSetEnvironmentEnabled} | |
| 81 | onUpdate={onUpdateEnvironment} | |
| 82 | /> | |
| 83 | </View> | |
| 84 | ))} | |
| 85 | </View> | |
| 86 | ) : ( | |
| 87 | <View collapsable={false} className="items-center gap-3 rounded-[24px] bg-card px-6 py-8"> | |
| 88 | <View className="h-12 w-12 items-center justify-center rounded-[16px] bg-subtle"> | |
| 89 | <SymbolView | |
| 90 | name="point.3.connected.trianglepath.dotted" | |
| 91 | size={20} | |
| 92 | tintColorClassName={"accent-icon-muted"} | |
| 93 | type="monochrome" | |
| 94 | /> | |
| 95 | </View> | |
| 96 | <Text className="text-center text-sm leading-normal text-foreground-muted"> | |
| 97 | No environments connected yet.{"\n"}Tap{" "} | |
| 98 | <Text className="font-t3-bold text-foreground">+</Text> to add one. | |
| 99 | </Text> | |
| 100 | </View> | |
| 101 | )} | |
| 62 | <LocalEnvironmentList | |
| 63 | environments={connectedEnvironments} | |
| 64 | expandedId={expandedId} | |
| 65 | onToggle={handleToggle} | |
| 66 | onReconnect={onReconnectEnvironment} | |
| 67 | onRemove={onRemoveEnvironmentPress} | |
| 68 | onSetEnabled={onSetEnvironmentEnabled} | |
| 69 | onUpdate={onUpdateEnvironment} | |
| 70 | /> | |
| 102 | 71 | <GitHubRoutingSettings /> |
| 103 | 72 | </ScrollView> |
| 104 | 73 | </View> |
apps/mobile/src/features/connection/LocalEnvironmentList.tsx created+60| ... | ... | @@ -0,0 +1,60 @@ |
| 1 | import type { EnvironmentId } from "@t3tools/contracts"; | |
| 2 | import type { ComponentProps } from "react"; | |
| 3 | import { View } from "react-native"; | |
| 4 | ||
| 5 | import { SymbolView } from "../../components/AppSymbol"; | |
| 6 | import { AppText as Text } from "../../components/AppText"; | |
| 7 | import { cn } from "../../lib/cn"; | |
| 8 | import { ConnectionEnvironmentRow } from "./ConnectionEnvironmentRow"; | |
| 9 | ||
| 10 | type EnvironmentRowProps = ComponentProps<typeof ConnectionEnvironmentRow>; | |
| 11 | ||
| 12 | /** Shared list and empty state for environment management entry points. */ | |
| 13 | export function LocalEnvironmentList({ | |
| 14 | environments, | |
| 15 | expandedId, | |
| 16 | onToggle, | |
| 17 | ...rowActions | |
| 18 | }: Pick<EnvironmentRowProps, "onReconnect" | "onRemove" | "onSetEnabled" | "onUpdate"> & { | |
| 19 | readonly environments: ReadonlyArray<EnvironmentRowProps["environment"]>; | |
| 20 | readonly expandedId: EnvironmentId | null; | |
| 21 | readonly onToggle: (environmentId: EnvironmentId) => void; | |
| 22 | }) { | |
| 23 | if (environments.length === 0) { | |
| 24 | return ( | |
| 25 | <View collapsable={false} className="items-center gap-3 rounded-[24px] bg-card px-6 py-8"> | |
| 26 | <View className="h-12 w-12 items-center justify-center rounded-[16px] bg-subtle"> | |
| 27 | <SymbolView | |
| 28 | name="point.3.connected.trianglepath.dotted" | |
| 29 | size={20} | |
| 30 | tintColorClassName="accent-icon-muted" | |
| 31 | type="monochrome" | |
| 32 | /> | |
| 33 | </View> | |
| 34 | <Text className="text-center text-sm leading-normal text-foreground-muted"> | |
| 35 | No environments connected yet.{"\n"}Tap{" "} | |
| 36 | <Text className="font-t3-bold text-foreground">+</Text> to add one. | |
| 37 | </Text> | |
| 38 | </View> | |
| 39 | ); | |
| 40 | } | |
| 41 | ||
| 42 | return ( | |
| 43 | <View collapsable={false} className="overflow-hidden rounded-[24px] bg-card"> | |
| 44 | {environments.map((environment, index) => ( | |
| 45 | <View | |
| 46 | key={environment.environmentId} | |
| 47 | collapsable={false} | |
| 48 | className={cn(index !== 0 && "border-t border-border")} | |
| 49 | > | |
| 50 | <ConnectionEnvironmentRow | |
| 51 | environment={environment} | |
| 52 | expanded={expandedId === environment.environmentId} | |
| 53 | onToggle={() => onToggle(environment.environmentId)} | |
| 54 | {...rowActions} | |
| 55 | /> | |
| 56 | </View> | |
| 57 | ))} | |
| 58 | </View> | |
| 59 | ); | |
| 60 | } |
apps/mobile/src/features/settings/SettingsEnvironmentsRouteScreen.tsx+11-42| ... | ... | @@ -1,19 +1,16 @@ |
| 1 | 1 | import { ScreenScrollView as ScrollView } from "../../components/ScreenScrollView"; |
| 2 | 2 | import { NativeHeaderToolbar } from "../../native/StackHeader"; |
| 3 | 3 | import { useNavigation } from "@react-navigation/native"; |
| 4 | import { SymbolView } from "../../components/AppSymbol"; | |
| 5 | 4 | import type { EnvironmentId } from "@t3tools/contracts"; |
| 6 | 5 | import { useCallback, useState } from "react"; |
| 7 | import { Platform, View } from "react-native"; | |
| 6 | import { Platform } from "react-native"; | |
| 8 | 7 | import { useSafeAreaInsets } from "react-native-safe-area-context"; |
| 9 | 8 | |
| 10 | import { AppText as Text } from "../../components/AppText"; | |
| 11 | 9 | import { SettingsScreen } from "./components/SettingsScreen"; |
| 12 | 10 | import { CloudEnvironmentRows } from "../connection/CloudEnvironmentRows"; |
| 13 | import { ConnectionEnvironmentRow } from "../connection/ConnectionEnvironmentRow"; | |
| 11 | import { LocalEnvironmentList } from "../connection/LocalEnvironmentList"; | |
| 14 | 12 | import { GitHubRoutingSettings } from "../connection/GitHubRoutingSettings"; |
| 15 | 13 | import { splitEnvironmentSections } from "../connection/environmentSections"; |
| 16 | import { cn } from "../../lib/cn"; | |
| 17 | 14 | import { useUniwindTheme } from "../../lib/useUniwindTheme"; |
| 18 | 15 | import { useRemoteConnections } from "../../state/use-remote-environment-registry"; |
| 19 | 16 | import { |
| ... | ... | @@ -45,7 +42,6 @@ export function SettingsEnvironmentsRouteScreen() { |
| 45 | 42 | const connectedCloudEnvironments = SHOWCASE_ENABLED |
| 46 | 43 | ? SHOWCASE_CONNECTED_CLOUD_ENVIRONMENTS |
| 47 | 44 | : environmentSections.connectedCloudEnvironments; |
| 48 | const hasLocalEnvironments = localEnvironments.length > 0; | |
| 49 | 45 | const [expandedId, setExpandedId] = useState<EnvironmentId | null>(null); |
| 50 | 46 | const headerIconColor = useUniwindTheme()["--color-icon"]; |
| 51 | 47 | |
| ... | ... | @@ -118,42 +114,15 @@ export function SettingsEnvironmentsRouteScreen() { |
| 118 | 114 | paddingBottom: Math.max(insets.bottom, 18) + 18, |
| 119 | 115 | }} |
| 120 | 116 | > |
| 121 | {hasLocalEnvironments ? ( | |
| 122 | <View collapsable={false} className="overflow-hidden rounded-[24px] bg-card"> | |
| 123 | {localEnvironments.map((environment, index) => ( | |
| 124 | <View | |
| 125 | key={environment.environmentId} | |
| 126 | collapsable={false} | |
| 127 | className={cn(index !== 0 && "border-t border-border")} | |
| 128 | > | |
| 129 | <ConnectionEnvironmentRow | |
| 130 | environment={environment} | |
| 131 | expanded={expandedId === environment.environmentId} | |
| 132 | onToggle={() => handleToggle(environment.environmentId)} | |
| 133 | onReconnect={onReconnectEnvironment} | |
| 134 | onRemove={onRemoveEnvironmentPress} | |
| 135 | onSetEnabled={onSetEnvironmentEnabled} | |
| 136 | onUpdate={handleUpdateEnvironment} | |
| 137 | /> | |
| 138 | </View> | |
| 139 | ))} | |
| 140 | </View> | |
| 141 | ) : ( | |
| 142 | <View collapsable={false} className="items-center gap-3 rounded-[24px] bg-card px-6 py-8"> | |
| 143 | <View className="h-12 w-12 items-center justify-center rounded-[16px] bg-subtle"> | |
| 144 | <SymbolView | |
| 145 | name="point.3.connected.trianglepath.dotted" | |
| 146 | size={20} | |
| 147 | tintColorClassName={"accent-icon-muted"} | |
| 148 | type="monochrome" | |
| 149 | /> | |
| 150 | </View> | |
| 151 | <Text className="text-center text-sm leading-normal text-foreground-muted"> | |
| 152 | No environments connected yet.{"\n"}Tap{" "} | |
| 153 | <Text className="font-t3-bold text-foreground">+</Text> to add one. | |
| 154 | </Text> | |
| 155 | </View> | |
| 156 | )} | |
| 117 | <LocalEnvironmentList | |
| 118 | environments={localEnvironments} | |
| 119 | expandedId={expandedId} | |
| 120 | onToggle={handleToggle} | |
| 121 | onReconnect={onReconnectEnvironment} | |
| 122 | onRemove={onRemoveEnvironmentPress} | |
| 123 | onSetEnabled={onSetEnvironmentEnabled} | |
| 124 | onUpdate={handleUpdateEnvironment} | |
| 125 | /> | |
| 157 | 126 | |
| 158 | 127 | {/* Always mounted: already-connected relay environments must stay |
| 159 | 128 | visible (and removable) even when cloud config is missing or the |