| author | |
| committer | |
| log | 18dcc43ace0a7b0d93f2fd61907b2a4e27522936 |
| tree | 6e509c34ea3e4ba4163f621ad2531b289152d6e4 |
| parent | 767a5dbd00d0a2050745a2ae2e29050c5282b8c0 |
| signature | Signed by PGP key B5690EEEBB952194 |
3 files changed, 63 insertions(+), 56 deletions(-)
apps/mobile/src/features/connection/ConnectionEnvironmentRow.tsx+19-28| ... | ... | @@ -10,7 +10,7 @@ import { useCallback, useState } from "react"; |
| 10 | 10 | import { Platform, Alert, Pressable, View } from "react-native"; |
| 11 | 11 | import Animated, { FadeIn, FadeOut, LinearTransition } from "react-native-reanimated"; |
| 12 | 12 | |
| 13 | import { AppText as Text, AppTextInput as TextInput } from "../../components/AppText"; | |
| 13 | import { AppText as Text } from "../../components/AppText"; | |
| 14 | 14 | import { EnvironmentMachineSymbol } from "../../components/EnvironmentMachineSymbol"; |
| 15 | 15 | import { MaterialButton } from "../../components/MaterialButton"; |
| 16 | 16 | import { MaterialIconButton } from "../../components/MaterialIconButton"; |
| ... | ... | @@ -18,6 +18,7 @@ import { ThemedSwitch } from "../../components/ThemedSwitch"; |
| 18 | 18 | import { cn } from "../../lib/cn"; |
| 19 | 19 | import type { ConnectedEnvironmentSummary } from "../../state/remote-runtime-types"; |
| 20 | 20 | import { serverEnvironment } from "../../state/server"; |
| 21 | import { ConnectionFormField } from "./ConnectionFormField"; | |
| 21 | 22 | import { ConnectionStatusDot } from "./ConnectionStatusDot"; |
| 22 | 23 | |
| 23 | 24 | function connectionStatusLabel(environment: ConnectedEnvironmentSummary): string | null { |
| ... | ... | @@ -152,34 +153,24 @@ export function ConnectionEnvironmentRow(props: { |
| 152 | 153 | </Text> |
| 153 | 154 | ) : ( |
| 154 | 155 | <> |
| 155 | <View className="gap-1.5"> | |
| 156 | <Text className="text-2xs font-t3-bold tracking-[0.8px] uppercase text-foreground-muted"> | |
| 157 | Label | |
| 158 | </Text> | |
| 159 | <TextInput | |
| 160 | autoCapitalize="words" | |
| 161 | autoCorrect={false} | |
| 162 | placeholder="My MacBook" | |
| 163 | value={label} | |
| 164 | onChangeText={setLabel} | |
| 165 | className="rounded-[14px] border border-input-border bg-input px-4 py-3 text-base text-foreground" | |
| 166 | /> | |
| 167 | </View> | |
| 156 | <ConnectionFormField | |
| 157 | label="Label" | |
| 158 | autoCapitalize="words" | |
| 159 | autoCorrect={false} | |
| 160 | placeholder="My MacBook" | |
| 161 | value={label} | |
| 162 | onChangeText={setLabel} | |
| 163 | /> | |
| 168 | 164 | |
| 169 | <View className="gap-1.5"> | |
| 170 | <Text className="text-2xs font-t3-bold tracking-[0.8px] uppercase text-foreground-muted"> | |
| 171 | URL | |
| 172 | </Text> | |
| 173 | <TextInput | |
| 174 | autoCapitalize="none" | |
| 175 | autoCorrect={false} | |
| 176 | keyboardType="url" | |
| 177 | placeholder="192.168.1.100:8080" | |
| 178 | value={url} | |
| 179 | onChangeText={setUrl} | |
| 180 | className="rounded-[14px] border border-input-border bg-input px-4 py-3 text-base text-foreground" | |
| 181 | /> | |
| 182 | </View> | |
| 165 | <ConnectionFormField | |
| 166 | label="URL" | |
| 167 | autoCapitalize="none" | |
| 168 | autoCorrect={false} | |
| 169 | keyboardType="url" | |
| 170 | placeholder="192.168.1.100:8080" | |
| 171 | value={url} | |
| 172 | onChangeText={setUrl} | |
| 173 | /> | |
| 183 | 174 | </> |
| 184 | 175 | )} |
| 185 | 176 |
apps/mobile/src/features/connection/ConnectionFormField.tsx created+25| ... | ... | @@ -0,0 +1,25 @@ |
| 1 | import { View } from "react-native"; | |
| 2 | ||
| 3 | import { AppText, AppTextInput, type AppTextInputProps } from "../../components/AppText"; | |
| 4 | import { cn } from "../../lib/cn"; | |
| 5 | ||
| 6 | type ConnectionFormFieldProps = Omit<AppTextInputProps, "className"> & { | |
| 7 | readonly label: string; | |
| 8 | readonly className?: string; | |
| 9 | }; | |
| 10 | ||
| 11 | /** Labeled connection input with a native wrapper retained inside form sheets. */ | |
| 12 | export function ConnectionFormField({ label, className, ...inputProps }: ConnectionFormFieldProps) { | |
| 13 | return ( | |
| 14 | <View collapsable={false} className={cn("gap-1.5", className)}> | |
| 15 | <AppText className="text-2xs font-t3-bold tracking-[0.8px] uppercase text-foreground-muted"> | |
| 16 | {label} | |
| 17 | </AppText> | |
| 18 | <AppTextInput | |
| 19 | accessibilityLabel={label} | |
| 20 | {...inputProps} | |
| 21 | className="rounded-[14px] px-4 py-3.5" | |
| 22 | /> | |
| 23 | </View> | |
| 24 | ); | |
| 25 | } |
apps/mobile/src/features/connection/ConnectionsNewRouteScreen.tsx+19-28| ... | ... | @@ -13,8 +13,9 @@ import { Alert, Linking, Platform, View } from "react-native"; |
| 13 | 13 | import { useSafeAreaInsets } from "react-native-safe-area-context"; |
| 14 | 14 | import { useUniwindTheme } from "../../lib/useUniwindTheme"; |
| 15 | 15 | import { SettingsScreen } from "../settings/components/SettingsScreen"; |
| 16 | import { AppText as Text, AppTextInput as TextInput } from "../../components/AppText"; | |
| 16 | import { AppText as Text } from "../../components/AppText"; | |
| 17 | 17 | import { ErrorBanner } from "../../components/ErrorBanner"; |
| 18 | import { ConnectionFormField } from "./ConnectionFormField"; | |
| 18 | 19 | import { ConnectionSheetButton } from "./ConnectionSheetButton"; |
| 19 | 20 | import { buildPairingUrl, extractPairingUrlFromQrPayload, parsePairingUrl } from "./pairing"; |
| 20 | 21 | import { useRemoteConnections } from "../../state/use-remote-environment-registry"; |
| ... | ... | @@ -262,34 +263,24 @@ export function ConnectionsNewRouteScreen({ |
| 262 | 263 | ) |
| 263 | 264 | ) : ( |
| 264 | 265 | <View collapsable={false} className="gap-4 rounded-[24px] bg-card p-4"> |
| 265 | <View collapsable={false} className="gap-1.5"> | |
| 266 | <Text className="text-2xs font-t3-bold tracking-[0.8px] uppercase text-foreground-muted"> | |
| 267 | Host | |
| 268 | </Text> | |
| 269 | <TextInput | |
| 270 | autoCapitalize="none" | |
| 271 | autoCorrect={false} | |
| 272 | keyboardType="url" | |
| 273 | placeholder="192.168.1.100:8080" | |
| 274 | value={hostInput} | |
| 275 | onChangeText={handleHostChange} | |
| 276 | className="rounded-[14px] border border-input-border bg-input px-4 py-3.5 text-base text-foreground" | |
| 277 | /> | |
| 278 | </View> | |
| 266 | <ConnectionFormField | |
| 267 | label="Host" | |
| 268 | autoCapitalize="none" | |
| 269 | autoCorrect={false} | |
| 270 | keyboardType="url" | |
| 271 | placeholder="192.168.1.100:8080" | |
| 272 | value={hostInput} | |
| 273 | onChangeText={handleHostChange} | |
| 274 | /> | |
| 279 | 275 | |
| 280 | <View collapsable={false} className="gap-1.5"> | |
| 281 | <Text className="text-2xs font-t3-bold tracking-[0.8px] uppercase text-foreground-muted"> | |
| 282 | Pairing code | |
| 283 | </Text> | |
| 284 | <TextInput | |
| 285 | autoCapitalize="none" | |
| 286 | autoCorrect={false} | |
| 287 | placeholder="abc-123-xyz" | |
| 288 | value={codeInput} | |
| 289 | onChangeText={handleCodeChange} | |
| 290 | className="rounded-[14px] border border-input-border bg-input px-4 py-3.5 text-base text-foreground" | |
| 291 | /> | |
| 292 | </View> | |
| 276 | <ConnectionFormField | |
| 277 | label="Pairing code" | |
| 278 | autoCapitalize="none" | |
| 279 | autoCorrect={false} | |
| 280 | placeholder="abc-123-xyz" | |
| 281 | value={codeInput} | |
| 282 | onChangeText={handleCodeChange} | |
| 283 | /> | |
| 293 | 284 | |
| 294 | 285 | {pairingConnectionError ? <ErrorBanner message={pairingConnectionError} /> : null} |
| 295 | 286 |