| 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,17 +1,14 @@ |
| 1 | import { ScreenScrollView as ScrollView } from "../../components/ScreenScrollView"; | 1 | import { ScreenScrollView as ScrollView } from "../../components/ScreenScrollView"; |
| 2 | import { NativeHeaderToolbar } from "../../native/StackHeader"; | 2 | import { NativeHeaderToolbar } from "../../native/StackHeader"; |
| 3 | import { useNavigation } from "@react-navigation/native"; | 3 | import { useNavigation } from "@react-navigation/native"; |
| 4 | import { SymbolView } from "../../components/AppSymbol"; | ||
| 5 | import type { EnvironmentId } from "@t3tools/contracts"; | 4 | import type { EnvironmentId } from "@t3tools/contracts"; |
| 6 | import { useCallback, useState } from "react"; | 5 | import { useCallback, useState } from "react"; |
| 7 | import { Platform, View } from "react-native"; | 6 | import { Platform, View } from "react-native"; |
| 8 | import { useSafeAreaInsets } from "react-native-safe-area-context"; | 7 | import { useSafeAreaInsets } from "react-native-safe-area-context"; |
| 9 | 8 | ||
| 10 | import { AndroidScreenHeader } from "../../components/AndroidScreenHeader"; | 9 | import { AndroidScreenHeader } from "../../components/AndroidScreenHeader"; |
| 11 | import { AppText as Text } from "../../components/AppText"; | ||
| 12 | import { cn } from "../../lib/cn"; | ||
| 13 | import { useRemoteConnections } from "../../state/use-remote-environment-registry"; | 10 | import { useRemoteConnections } from "../../state/use-remote-environment-registry"; |
| 14 | import { ConnectionEnvironmentRow } from "./ConnectionEnvironmentRow"; | 11 | import { LocalEnvironmentList } from "./LocalEnvironmentList"; |
| 15 | import { GitHubRoutingSettings } from "./GitHubRoutingSettings"; | 12 | import { GitHubRoutingSettings } from "./GitHubRoutingSettings"; |
| 16 | 13 | ||
| 17 | export function ConnectionsRouteScreen() { | 14 | export function ConnectionsRouteScreen() { |
| ... | @@ -24,7 +21,6 @@ export function ConnectionsRouteScreen() { | ... | @@ -24,7 +21,6 @@ export function ConnectionsRouteScreen() { |
| 24 | } = useRemoteConnections(); | 21 | } = useRemoteConnections(); |
| 25 | const navigation = useNavigation(); | 22 | const navigation = useNavigation(); |
| 26 | const insets = useSafeAreaInsets(); | 23 | const insets = useSafeAreaInsets(); |
| 27 | const hasEnvironments = connectedEnvironments.length > 0; | ||
| 28 | const [expandedId, setExpandedId] = useState<EnvironmentId | null>(null); | 24 | const [expandedId, setExpandedId] = useState<EnvironmentId | null>(null); |
| 29 | const handleToggle = useCallback((environmentId: EnvironmentId) => { | 25 | const handleToggle = useCallback((environmentId: EnvironmentId) => { |
| 30 | setExpandedId((prev) => (prev === environmentId ? null : environmentId)); | 26 | setExpandedId((prev) => (prev === environmentId ? null : environmentId)); |
| ... | @@ -63,42 +59,15 @@ export function ConnectionsRouteScreen() { | ... | @@ -63,42 +59,15 @@ export function ConnectionsRouteScreen() { |
| 63 | paddingTop: 16, | 59 | paddingTop: 16, |
| 64 | }} | 60 | }} |
| 65 | > | 61 | > |
| 66 | {hasEnvironments ? ( | 62 | <LocalEnvironmentList |
| 67 | <View collapsable={false} className="overflow-hidden rounded-[24px] bg-card"> | 63 | environments={connectedEnvironments} |
| 68 | {connectedEnvironments.map((environment, index) => ( | 64 | expandedId={expandedId} |
| 69 | <View | 65 | onToggle={handleToggle} |
| 70 | key={environment.environmentId} | 66 | onReconnect={onReconnectEnvironment} |
| 71 | collapsable={false} | 67 | onRemove={onRemoveEnvironmentPress} |
| 72 | className={cn(index !== 0 && "border-t border-border")} | 68 | onSetEnabled={onSetEnvironmentEnabled} |
| 73 | > | 69 | onUpdate={onUpdateEnvironment} |
| 74 | <ConnectionEnvironmentRow | 70 | /> |
| 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 | )} | ||
| 102 | <GitHubRoutingSettings /> | 71 | <GitHubRoutingSettings /> |
| 103 | </ScrollView> | 72 | </ScrollView> |
| 104 | </View> | 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,19 +1,16 @@ |
| 1 | import { ScreenScrollView as ScrollView } from "../../components/ScreenScrollView"; | 1 | import { ScreenScrollView as ScrollView } from "../../components/ScreenScrollView"; |
| 2 | import { NativeHeaderToolbar } from "../../native/StackHeader"; | 2 | import { NativeHeaderToolbar } from "../../native/StackHeader"; |
| 3 | import { useNavigation } from "@react-navigation/native"; | 3 | import { useNavigation } from "@react-navigation/native"; |
| 4 | import { SymbolView } from "../../components/AppSymbol"; | ||
| 5 | import type { EnvironmentId } from "@t3tools/contracts"; | 4 | import type { EnvironmentId } from "@t3tools/contracts"; |
| 6 | import { useCallback, useState } from "react"; | 5 | import { useCallback, useState } from "react"; |
| 7 | import { Platform, View } from "react-native"; | 6 | import { Platform } from "react-native"; |
| 8 | import { useSafeAreaInsets } from "react-native-safe-area-context"; | 7 | import { useSafeAreaInsets } from "react-native-safe-area-context"; |
| 9 | 8 | ||
| 10 | import { AppText as Text } from "../../components/AppText"; | ||
| 11 | import { SettingsScreen } from "./components/SettingsScreen"; | 9 | import { SettingsScreen } from "./components/SettingsScreen"; |
| 12 | import { CloudEnvironmentRows } from "../connection/CloudEnvironmentRows"; | 10 | import { CloudEnvironmentRows } from "../connection/CloudEnvironmentRows"; |
| 13 | import { ConnectionEnvironmentRow } from "../connection/ConnectionEnvironmentRow"; | 11 | import { LocalEnvironmentList } from "../connection/LocalEnvironmentList"; |
| 14 | import { GitHubRoutingSettings } from "../connection/GitHubRoutingSettings"; | 12 | import { GitHubRoutingSettings } from "../connection/GitHubRoutingSettings"; |
| 15 | import { splitEnvironmentSections } from "../connection/environmentSections"; | 13 | import { splitEnvironmentSections } from "../connection/environmentSections"; |
| 16 | import { cn } from "../../lib/cn"; | ||
| 17 | import { useUniwindTheme } from "../../lib/useUniwindTheme"; | 14 | import { useUniwindTheme } from "../../lib/useUniwindTheme"; |
| 18 | import { useRemoteConnections } from "../../state/use-remote-environment-registry"; | 15 | import { useRemoteConnections } from "../../state/use-remote-environment-registry"; |
| 19 | import { | 16 | import { |
| ... | @@ -45,7 +42,6 @@ export function SettingsEnvironmentsRouteScreen() { | ... | @@ -45,7 +42,6 @@ export function SettingsEnvironmentsRouteScreen() { |
| 45 | const connectedCloudEnvironments = SHOWCASE_ENABLED | 42 | const connectedCloudEnvironments = SHOWCASE_ENABLED |
| 46 | ? SHOWCASE_CONNECTED_CLOUD_ENVIRONMENTS | 43 | ? SHOWCASE_CONNECTED_CLOUD_ENVIRONMENTS |
| 47 | : environmentSections.connectedCloudEnvironments; | 44 | : environmentSections.connectedCloudEnvironments; |
| 48 | const hasLocalEnvironments = localEnvironments.length > 0; | ||
| 49 | const [expandedId, setExpandedId] = useState<EnvironmentId | null>(null); | 45 | const [expandedId, setExpandedId] = useState<EnvironmentId | null>(null); |
| 50 | const headerIconColor = useUniwindTheme()["--color-icon"]; | 46 | const headerIconColor = useUniwindTheme()["--color-icon"]; |
| 51 | 47 | ||
| ... | @@ -118,42 +114,15 @@ export function SettingsEnvironmentsRouteScreen() { | ... | @@ -118,42 +114,15 @@ export function SettingsEnvironmentsRouteScreen() { |
| 118 | paddingBottom: Math.max(insets.bottom, 18) + 18, | 114 | paddingBottom: Math.max(insets.bottom, 18) + 18, |
| 119 | }} | 115 | }} |
| 120 | > | 116 | > |
| 121 | {hasLocalEnvironments ? ( | 117 | <LocalEnvironmentList |
| 122 | <View collapsable={false} className="overflow-hidden rounded-[24px] bg-card"> | 118 | environments={localEnvironments} |
| 123 | {localEnvironments.map((environment, index) => ( | 119 | expandedId={expandedId} |
| 124 | <View | 120 | onToggle={handleToggle} |
| 125 | key={environment.environmentId} | 121 | onReconnect={onReconnectEnvironment} |
| 126 | collapsable={false} | 122 | onRemove={onRemoveEnvironmentPress} |
| 127 | className={cn(index !== 0 && "border-t border-border")} | 123 | onSetEnabled={onSetEnvironmentEnabled} |
| 128 | > | 124 | onUpdate={handleUpdateEnvironment} |
| 129 | <ConnectionEnvironmentRow | 125 | /> |
| 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 | )} | ||
| 157 | 126 | ||
| 158 | {/* Always mounted: already-connected relay environments must stay | 127 | {/* Always mounted: already-connected relay environments must stay |
| 159 | visible (and removable) even when cloud config is missing or the | 128 | visible (and removable) even when cloud config is missing or the |