Skip to content

Commit 00c636e

Browse files
t3dotggclaude
andauthored
fix(mobile): picking a project on two machines starts on the connected one (#25839)
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 63bc022 commit 00c636e

4 files changed

Lines changed: 39 additions & 2 deletions

File tree

‎apps/mobile/src/features/threads/NewTaskRouteScreen.tsx‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,11 @@ export function NewTaskRouteScreen({ route }: StaticScreenProps<NewTaskRoutePara
188188
) ?? null)
189189
: null;
190190
const { connectedEnvironments } = useRemoteConnectionStatus();
191+
const connectedEnvironmentIds = new Set(
192+
connectedEnvironments
193+
.filter((environment) => environment.connectionState === "connected")
194+
.map((environment) => environment.environmentId),
195+
);
191196
const openScratch = useAtomCommand(projectEnvironment.openScratch, {
192197
reportFailure: false,
193198
});
@@ -461,6 +466,7 @@ export function NewTaskRouteScreen({ route }: StaticScreenProps<NewTaskRoutePara
461466
const selectionTarget = getProjectScopeSelectionTarget(
462467
scope,
463468
selectedEnvironmentId,
469+
connectedEnvironmentIds,
464470
);
465471
if (Platform.OS === "android") {
466472
return (

‎apps/mobile/src/features/threads/new-task-project-selection.test.ts‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { describe, expect, it } from "vite-plus/test";
44
import type { EnvironmentProject } from "@t3tools/client-runtime/state/shell";
55
import type { HomeProjectScope } from "../home/homeThreadList";
66
import {
7+
getProjectScopeSelectionTarget,
78
newTaskDestination,
89
resolveDraftProjectSelection,
910
resolveEnvironmentProjectMatch,
@@ -53,6 +54,26 @@ function makeScope(projects: ReadonlyArray<EnvironmentProject>): HomeProjectScop
5354
};
5455
}
5556

57+
describe("getProjectScopeSelectionTarget", () => {
58+
it("prefers a connected copy of a project that spans environments", () => {
59+
const mac = makeProject("t3code", "mac");
60+
const server = makeProject("t3code", "server");
61+
const target = (connected: ReadonlyArray<string>, preferred: string | null = null) =>
62+
getProjectScopeSelectionTarget(
63+
makeScope([mac, server]),
64+
preferred === null ? null : EnvironmentId.make(preferred),
65+
new Set(connected.map((id) => EnvironmentId.make(id))),
66+
);
67+
expect(target(["server"])).toBe(server);
68+
expect(target(["server"], "mac")).toBe(server);
69+
expect(target(["mac", "server"], "server")).toBe(server);
70+
expect(target(["mac", "server"])).toBe(mac);
71+
// With nothing connected, the usual order still applies.
72+
expect(target([], "server")).toBe(server);
73+
expect(target([])).toBe(mac);
74+
});
75+
});
76+
5677
describe("resolveEnvironmentProjectMatch", () => {
5778
it("follows the same repository onto the target machine", () => {
5879
const selected = makeProject("t3code", "mac", { repositoryKey: "github.com/t3tools/t3code" });

‎apps/mobile/src/features/threads/new-task-project-selection.ts‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,21 @@ type DraftProjectSelectionResolution =
1010
| { readonly kind: "pick" }
1111
| { readonly kind: "dismiss" };
1212

13+
/**
14+
* The copy of a project a new thread starts in: the preferred environment's, then the
15+
* representative. A connected copy wins over a disconnected one.
16+
*/
1317
export function getProjectScopeSelectionTarget(
1418
scope: HomeProjectScope,
1519
preferredEnvironmentId: EnvironmentId | null,
20+
connectedEnvironmentIds: ReadonlySet<EnvironmentId>,
1621
): EnvironmentProject {
22+
const rank = (project: EnvironmentProject) =>
23+
project.environmentId === preferredEnvironmentId ? 0 : project === scope.representative ? 1 : 2;
24+
const ordered = [...scope.projects].sort((left, right) => rank(left) - rank(right));
1725
return (
18-
scope.projects.find((project) => project.environmentId === preferredEnvironmentId) ??
26+
ordered.find((project) => connectedEnvironmentIds.has(project.environmentId)) ??
27+
ordered[0] ??
1928
scope.representative
2029
);
2130
}

‎docs/user/thread-sidebar.md‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ an existing worktree, use **New thread in this worktree** from the branch toolba
1313
On mobile, **New thread** starts in the current thread's project, or else in the
1414
last project you opened or chose on that device. If neither is available, the
1515
project picker opens. Turn off **Settings → General → Use last project** to
16-
choose a project every time.
16+
choose a project every time. When you pick a project that is on more than one
17+
machine, the thread starts on a connected one if any is connected.
1718

1819
When you change a new thread's project, T3 Code stays in the current environment
1920
if that project exists there. Otherwise it selects an environment that has it.

0 commit comments

Comments
 (0)