authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-10-07 22:48:55-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-10-07 22:48:55-07:00
log6eed953a060213630af0d7e3cd9a664bd85f7ced
tree90231578e8d75f9846f6bb59a4ed5e3fed2abff8
parent6651aaab38c6814ed7bba3e805a1ec7a01870b55
signature Signed by PGP key B5690EEEBB952194

perf(mobile): remove unused Home project sorting (#16177)


3 files changed, 61 insertions(+), 34 deletions(-)

apps/mobile/src/features/home/HomeScreen.tsx+5-33
...@@ -67,7 +67,7 @@ import { useThreadListV2ShelfPreferences } from "../threads/use-thread-list-v2-s...@@ -67,7 +67,7 @@ import { useThreadListV2ShelfPreferences } from "../threads/use-thread-list-v2-s
67import type { HomeListFilterMenuEnvironment } from "./home-list-filter-menu";67import type { HomeListFilterMenuEnvironment } from "./home-list-filter-menu";
68import {68import {
69 buildHomeProjectScopes,69 buildHomeProjectScopes,
70 sortHomeProjectScopes,70 findHomeProjectScope,
71 type HomeProjectSortOrder,71 type HomeProjectSortOrder,
72} from "./homeThreadList";72} from "./homeThreadList";
73import { createSwipeRowActivation } from "./swipe-row-activation";73import { createSwipeRowActivation } from "./swipe-row-activation";
...@@ -386,42 +386,14 @@ export function HomeScreen(props: HomeScreenProps) {...@@ -386,42 +386,14 @@ export function HomeScreen(props: HomeScreenProps) {
386 }, [props.projects]);386 }, [props.projects]);
387387
388 const v2ProjectScopeKey = props.selectedProjectKey;388 const v2ProjectScopeKey = props.selectedProjectKey;
389 const v2ScopeProjects = useMemo(
390 () =>
391 sortHomeProjectScopes({
392 scopes: projectScopes,
393 threads: props.threads,
394 pendingTasks: props.pendingTasks,
395 projectSortOrder: props.projectSortOrder,
396 }),
397 [
398 props.pendingTasks,
399 props.projects,
400 props.projectSortOrder,
401 props.selectedEnvironmentId,
402 props.threads,
403 projectScopes,
404 ],
405 );
406 const v2ScopedProjectGroup = useMemo(389 const v2ScopedProjectGroup = useMemo(
407 () =>390 () => findHomeProjectScope(projectScopes, v2ProjectScopeKey),
408 v2ProjectScopeKey === null391 [v2ProjectScopeKey, projectScopes],
409 ? null
410 : (v2ScopeProjects.find(
411 (scope) =>
412 scope.key === v2ProjectScopeKey ||
413 scope.projectRefs.some(
414 (projectRef) =>
415 scopedProjectKey(projectRef.environmentId, projectRef.projectId) ===
416 v2ProjectScopeKey,
417 ),
418 ) ?? null),
419 [v2ProjectScopeKey, v2ScopeProjects],
420 );392 );
421 const v2ProjectTitleByProjectKey = useMemo(393 const v2ProjectTitleByProjectKey = useMemo(
422 () =>394 () =>
423 new Map(395 new Map(
424 v2ScopeProjects.flatMap((scope) =>396 projectScopes.flatMap((scope) =>
425 scope.projectRefs.map(397 scope.projectRefs.map(
426 (projectRef) =>398 (projectRef) =>
427 [399 [
...@@ -431,7 +403,7 @@ export function HomeScreen(props: HomeScreenProps) {...@@ -431,7 +403,7 @@ export function HomeScreen(props: HomeScreenProps) {
431 ),403 ),
432 ),404 ),
433 ),405 ),
434 [v2ScopeProjects],406 [projectScopes],
435 );407 );
436 const v2ScopedProjectKeys = useMemo(408 const v2ScopedProjectKeys = useMemo(
437 () =>409 () =>
apps/mobile/src/features/home/homeThreadList.test.ts+36-1
...@@ -5,7 +5,11 @@ import type {...@@ -5,7 +5,11 @@ import type {
5import { EnvironmentId, ProjectId, ProviderInstanceId, ThreadId } from "@t3tools/contracts";5import { EnvironmentId, ProjectId, ProviderInstanceId, ThreadId } from "@t3tools/contracts";
6import { describe, expect, it } from "vite-plus/test";6import { describe, expect, it } from "vite-plus/test";
77
8import { buildHomeProjectScopes, sortHomeProjectScopes } from "./homeThreadList";8import {
9 buildHomeProjectScopes,
10 findHomeProjectScope,
11 sortHomeProjectScopes,
12} from "./homeThreadList";
9import { makeThreadShellFixture } from "../../test-fixtures";13import { makeThreadShellFixture } from "../../test-fixtures";
1014
11function makeProject(15function makeProject(
...@@ -282,4 +286,35 @@ describe("home project scopes", () => {...@@ -282,4 +286,35 @@ describe("home project scopes", () => {
282 }),286 }),
283 ).toHaveLength(2);287 ).toHaveLength(2);
284 });288 });
289
290 it("prefers an exact scope key over a project ID that collides with it", () => {
291 const environmentId = EnvironmentId.make("environment-local");
292 // project.create accepts caller-supplied IDs, so one project's scoped ref
293 // key can equal another project's normalized workspace-root scope key.
294 const collidingProject = makeProject({
295 environmentId,
296 id: ProjectId.make("/workspaces/target"),
297 title: "Colliding",
298 workspaceRoot: "/workspaces/colliding",
299 });
300 const targetProject = makeProject({
301 environmentId,
302 id: ProjectId.make("project-target"),
303 title: "Target",
304 workspaceRoot: "/workspaces/target",
305 });
306 const scopes = buildHomeProjectScopes({
307 projects: [collidingProject, targetProject],
308 environmentId: null,
309 projectGroupingMode: "separate",
310 });
311 const targetScope = scopes.find((scope) => scope.title === "Target");
312 expect(scopes.map((scope) => scope.title)).toEqual(["Colliding", "Target"]);
313 expect(targetScope?.key).toBe("environment-local:/workspaces/target");
314
315 expect(findHomeProjectScope(scopes, "environment-local:/workspaces/target")).toBe(targetScope);
316 expect(findHomeProjectScope(scopes, "environment-local:project-target")).toBe(targetScope);
317 expect(findHomeProjectScope(scopes, "environment-local:missing")).toBeNull();
318 expect(findHomeProjectScope(scopes, null)).toBeNull();
319 });
285});320});
apps/mobile/src/features/home/homeThreadList.ts+20
...@@ -65,6 +65,26 @@ export function buildHomeProjectScopes(input: {...@@ -65,6 +65,26 @@ export function buildHomeProjectScopes(input: {
65 });65 });
66}66}
6767
68// Prefers an exact scope key so a caller-supplied project ID that equals another
69// project's normalized workspace root cannot select the wrong group.
70export function findHomeProjectScope(
71 scopes: ReadonlyArray<HomeProjectScope>,
72 key: string | null,
73): HomeProjectScope | null {
74 if (key === null) {
75 return null;
76 }
77 return (
78 scopes.find((scope) => scope.key === key) ??
79 scopes.find((scope) =>
80 scope.projectRefs.some(
81 (projectRef) => scopedProjectKey(projectRef.environmentId, projectRef.projectId) === key,
82 ),
83 ) ??
84 null
85 );
86}
87
68export function sortHomeProjectScopes(input: {88export function sortHomeProjectScopes(input: {
69 readonly scopes: ReadonlyArray<HomeProjectScope>;89 readonly scopes: ReadonlyArray<HomeProjectScope>;
70 readonly threads: ReadonlyArray<EnvironmentThreadShell>;90 readonly threads: ReadonlyArray<EnvironmentThreadShell>;