Skip to content

Commit b98de3f

Browse files
feat(clients): reorder routes by drag, add them from the machine's row
The Routes submenu needed two levels of menus to move one route, and adding a route meant knowing that pairing the same machine again would merge. - Web and desktop: the machine's row shows "N routes" next to its status; clicking it expands an inline list. Drag the grip (or use the keyboard) to reorder, remove a route with its x, and "Add route" opens the pairing dialog scoped to that machine, with T3 Connect offered when the account can reach it. - Mobile: the environment detail screen's Routes section has Edit, which shows drag handles (VoiceOver gets Move up / Move down) and remove buttons, and "Add route" opens the pairing screen for that machine. - Pairing for a route refuses an address that reaches a different machine, before spending the one-time code. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
1 parent 55c7b44 commit b98de3f

13 files changed

Lines changed: 726 additions & 238 deletions

File tree

‎apps/mobile/src/connection/onboarding.ts‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,17 @@ const onboardingScheduler = createAtomCommandScheduler();
1313
export const connectPairingUrl = createRuntimeCommand(connectionAtomRuntime, {
1414
label: "mobile:connection:connect-pairing-url",
1515
scheduler: onboardingScheduler,
16-
concurrency: { mode: "singleFlight", key: (pairingUrl: string) => pairingUrl },
17-
execute: (pairingUrl: string) =>
16+
concurrency: {
17+
mode: "singleFlight",
18+
key: (input: { readonly pairingUrl: string }) => input.pairingUrl,
19+
},
20+
execute: (input: {
21+
readonly pairingUrl: string;
22+
/** Set when adding a route to this saved machine. */
23+
readonly expectedEnvironmentId?: EnvironmentId;
24+
}) =>
1825
ConnectionOnboarding.ConnectionOnboarding.pipe(
19-
Effect.flatMap((onboarding) => onboarding.registerPairing({ pairingUrl })),
26+
Effect.flatMap((onboarding) => onboarding.registerPairing(input)),
2027
),
2128
});
2229

‎apps/mobile/src/features/connection/ConnectionsNewRouteScreen.tsx‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
useRoute,
77
type StaticScreenProps,
88
} from "@react-navigation/native";
9+
import type { EnvironmentId } from "@t3tools/contracts";
910
import { AsyncResult } from "effect/unstable/reactivity";
1011
import { useCallback, useEffect, useRef, useState } from "react";
1112
import { Alert, Linking, Platform, View } from "react-native";
@@ -23,6 +24,8 @@ type ConnectionsNewRouteParams = {
2324
readonly mode?: string;
2425
readonly pairingUrl?: string;
2526
readonly autoConnect?: string;
27+
/** Adds a route to this saved machine instead of a new environment. */
28+
readonly routeFor?: EnvironmentId;
2629
};
2730

2831
export function ConnectionsNewRouteScreen({
@@ -158,7 +161,7 @@ export function ConnectionsNewRouteScreen({
158161
setIsSubmitting(true);
159162
onChangeConnectionPairingUrl(pairingUrl);
160163
try {
161-
const result = await onConnectPress(pairingUrl);
164+
const result = await onConnectPress(pairingUrl, params.routeFor);
162165
if (AsyncResult.isSuccess(result)) {
163166
if (replaceWithHome || !navigation.canGoBack()) {
164167
navigation.dispatch(StackActions.replace("Home"));
@@ -170,7 +173,7 @@ export function ConnectionsNewRouteScreen({
170173
setIsSubmitting(false);
171174
}
172175
},
173-
[navigation, onChangeConnectionPairingUrl, onConnectPress],
176+
[navigation, onChangeConnectionPairingUrl, onConnectPress, params.routeFor],
174177
);
175178

176179
const handleSubmit = useCallback(async () => {

‎apps/mobile/src/features/connection/useConnectionController.ts‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,11 @@ export function useConnectionController() {
6969
);
7070

7171
const connectPairingUrl = useCallback(
72-
(pairingUrl: string) => connectPairingUrlMutation(pairingUrl),
72+
(pairingUrl: string, expectedEnvironmentId?: EnvironmentId) =>
73+
connectPairingUrlMutation({
74+
pairingUrl,
75+
...(expectedEnvironmentId === undefined ? {} : { expectedEnvironmentId }),
76+
}),
7377
[connectPairingUrlMutation],
7478
);
7579
const connectRelayEnvironment = useCallback(

0 commit comments

Comments
 (0)