authorgravatar for me@t3.ggTheo Browne <me@t3.gg> 2026-09-04 07:03:49-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-04 07:03:49-07:00
log7cf5b284e6e37895f535433c77f728b3e4292c9b
tree2c897d7fb3972422edd86bd1c1ea121072ee2e9a
parent887ece307131bdc853cc10f3b82067dee77c4ecf
signature Signed by PGP key B5690EEEBB952194

perf(mobile): skip unused legacy list work (#9679)


3 files changed, 101 insertions(+), 77 deletions(-)

apps/mobile/src/features/home/HomeScreen.tsx+51-41
...@@ -61,6 +61,7 @@ import type { HomeListFilterMenuEnvironment } from "./home-list-filter-menu";...@@ -61,6 +61,7 @@ import type { HomeListFilterMenuEnvironment } from "./home-list-filter-menu";
61import {61import {
62 buildHomeListLayout,62 buildHomeListLayout,
63 DEFAULT_GROUP_DISPLAY_STATE,63 DEFAULT_GROUP_DISPLAY_STATE,
64 EMPTY_HOME_LIST_LAYOUT,
64 homeListItemsAreEqual,65 homeListItemsAreEqual,
65 nextGroupDisplayState,66 nextGroupDisplayState,
66 type HomeGroupDisplayAction,67 type HomeGroupDisplayAction,
...@@ -339,48 +340,57 @@ export function HomeScreen(props: HomeScreenProps) {...@@ -339,48 +340,57 @@ export function HomeScreen(props: HomeScreenProps) {
339 );340 );
340 const scopedProjects = useMemo(341 const scopedProjects = useMemo(
341 () =>342 () =>
342 selectedProjectRefKeys === null343 threadListV2Enabled
343 ? props.projects344 ? []
344 : props.projects.filter((project) =>345 : selectedProjectRefKeys === null
345 selectedProjectRefKeys.has(scopedProjectKey(project.environmentId, project.id)),346 ? props.projects
346 ),347 : props.projects.filter((project) =>
347 [props.projects, selectedProjectRefKeys],348 selectedProjectRefKeys.has(scopedProjectKey(project.environmentId, project.id)),
349 ),
350 [threadListV2Enabled, props.projects, selectedProjectRefKeys],
348 );351 );
349 const scopedThreads = useMemo(352 const scopedThreads = useMemo(
350 () =>353 () =>
351 selectedProjectRefKeys === null354 threadListV2Enabled
352 ? props.threads355 ? []
353 : props.threads.filter((thread) =>356 : selectedProjectRefKeys === null
354 selectedProjectRefKeys.has(scopedProjectKey(thread.environmentId, thread.projectId)),357 ? props.threads
355 ),358 : props.threads.filter((thread) =>
356 [props.threads, selectedProjectRefKeys],359 selectedProjectRefKeys.has(scopedProjectKey(thread.environmentId, thread.projectId)),
360 ),
361 [threadListV2Enabled, props.threads, selectedProjectRefKeys],
357 );362 );
358 const scopedPendingTasks = useMemo(363 const scopedPendingTasks = useMemo(
359 () =>364 () =>
360 selectedProjectRefKeys === null365 threadListV2Enabled
361 ? props.pendingTasks366 ? []
362 : props.pendingTasks.filter((pendingTask) =>367 : selectedProjectRefKeys === null
363 selectedProjectRefKeys.has(368 ? props.pendingTasks
364 scopedProjectKey(pendingTask.message.environmentId, pendingTask.creation.projectId),369 : props.pendingTasks.filter((pendingTask) =>
370 selectedProjectRefKeys.has(
371 scopedProjectKey(pendingTask.message.environmentId, pendingTask.creation.projectId),
372 ),
365 ),373 ),
366 ),374 [threadListV2Enabled, props.pendingTasks, selectedProjectRefKeys],
367 [props.pendingTasks, selectedProjectRefKeys],
368 );375 );
369376
370 const projectGroups = useMemo(377 const projectGroups = useMemo(
371 () =>378 () =>
372 buildHomeThreadGroups({379 threadListV2Enabled
373 projects: scopedProjects,380 ? []
374 threads: scopedThreads,381 : buildHomeThreadGroups({
375 pendingTasks: scopedPendingTasks,382 projects: scopedProjects,
376 environmentId: props.selectedEnvironmentId,383 threads: scopedThreads,
377 searchQuery: props.searchQuery,384 pendingTasks: scopedPendingTasks,
378 matchedThreadKeys,385 environmentId: props.selectedEnvironmentId,
379 projectSortOrder: props.projectSortOrder,386 searchQuery: props.searchQuery,
380 threadSortOrder: props.threadSortOrder,387 matchedThreadKeys,
381 projectGroupingMode: props.projectGroupingMode,388 projectSortOrder: props.projectSortOrder,
382 }),389 threadSortOrder: props.threadSortOrder,
390 projectGroupingMode: props.projectGroupingMode,
391 }),
383 [392 [
393 threadListV2Enabled,
384 props.projectGroupingMode,394 props.projectGroupingMode,
385 props.projectSortOrder,395 props.projectSortOrder,
386 props.searchQuery,396 props.searchQuery,
...@@ -396,12 +406,14 @@ export function HomeScreen(props: HomeScreenProps) {...@@ -396,12 +406,14 @@ export function HomeScreen(props: HomeScreenProps) {
396 const hasSearchQuery = props.searchQuery.trim().length > 0;406 const hasSearchQuery = props.searchQuery.trim().length > 0;
397 const listLayout = useMemo(407 const listLayout = useMemo(
398 () =>408 () =>
399 buildHomeListLayout({409 threadListV2Enabled
400 groups: projectGroups,410 ? EMPTY_HOME_LIST_LAYOUT
401 displayStates: effectiveGroupDisplayStates,411 : buildHomeListLayout({
402 showAllThreads: hasSearchQuery,412 groups: projectGroups,
403 }),413 displayStates: effectiveGroupDisplayStates,
404 [projectGroups, effectiveGroupDisplayStates, hasSearchQuery],414 showAllThreads: hasSearchQuery,
415 }),
416 [threadListV2Enabled, projectGroups, effectiveGroupDisplayStates, hasSearchQuery],
405 );417 );
406418
407 const projectCwdByKey = useMemo(() => {419 const projectCwdByKey = useMemo(() => {
...@@ -1033,7 +1045,7 @@ export function HomeScreen(props: HomeScreenProps) {...@@ -1033,7 +1045,7 @@ export function HomeScreen(props: HomeScreenProps) {
1033 // so the v1 check already covers v2.1045 // so the v1 check already covers v2.
1034 const hasAnyThreads =1046 const hasAnyThreads =
1035 props.threads.some((thread) => thread.archivedAt === null) || props.pendingTasks.length > 0;1047 props.threads.some((thread) => thread.archivedAt === null) || props.pendingTasks.length > 0;
1036 const hasResults = projectGroups.length > 0;1048 const hasResults = threadListV2Enabled ? threadListV2Items.length > 0 : projectGroups.length > 0;
1037 const selectedEnvironmentLabel =1049 const selectedEnvironmentLabel =
1038 props.selectedEnvironmentId === null1050 props.selectedEnvironmentId === null
1039 ? null1051 ? null
...@@ -1097,10 +1109,8 @@ export function HomeScreen(props: HomeScreenProps) {...@@ -1097,10 +1109,8 @@ export function HomeScreen(props: HomeScreenProps) {
1097 <EmptyState title="No threads yet" detail="Create a task to start a new coding session." />1109 <EmptyState title="No threads yet" detail="Create a task to start a new coding session." />
1098 )1110 )
1099 ) : null;1111 ) : null;
1100 // Self-contained: v1's listEmpty keys off projectGroups, which ignores the1112 // Use the v2 project scope for its empty state. Snoozed threads need no
1101 // v2 project scope, so it can be null (results elsewhere) while this list1113 // special empty state: their shelf header is a list row even while collapsed.
1102 // is empty. Snoozed threads need no special empty state: their shelf header
1103 // is a list row even while collapsed.
1104 const v2ListEmpty =1114 const v2ListEmpty =
1105 hasSearchQuery && threadSearch.isPending ? null : hasSearchQuery ? (1115 hasSearchQuery && threadSearch.isPending ? null : hasSearchQuery ? (
1106 <EmptyState title="No results" detail={`No threads matching "${props.searchQuery}".`} />1116 <EmptyState title="No results" detail={`No threads matching "${props.searchQuery}".`} />
apps/mobile/src/features/home/homeListItems.ts+2
...@@ -62,6 +62,8 @@ export interface HomeListLayout {...@@ -62,6 +62,8 @@ export interface HomeListLayout {
62 readonly stickyHeaderIndices: ReadonlyArray<number>;62 readonly stickyHeaderIndices: ReadonlyArray<number>;
63}63}
6464
65export const EMPTY_HOME_LIST_LAYOUT: HomeListLayout = { items: [], stickyHeaderIndices: [] };
66
65export type HomeGroupDisplayAction = "toggle-collapsed" | "show-more" | "show-less";67export type HomeGroupDisplayAction = "toggle-collapsed" | "show-more" | "show-less";
6668
67export function nextGroupDisplayState(69export function nextGroupDisplayState(
apps/mobile/src/features/threads/ThreadNavigationSidebar.tsx+48-36
...@@ -45,6 +45,7 @@ import { buildHomeListFilterMenu } from "../home/home-list-filter-menu";...@@ -45,6 +45,7 @@ import { buildHomeListFilterMenu } from "../home/home-list-filter-menu";
45import {45import {
46 buildHomeListLayout,46 buildHomeListLayout,
47 DEFAULT_GROUP_DISPLAY_STATE,47 DEFAULT_GROUP_DISPLAY_STATE,
48 EMPTY_HOME_LIST_LAYOUT,
48 homeListItemsAreEqual,49 homeListItemsAreEqual,
49 nextGroupDisplayState,50 nextGroupDisplayState,
50 type HomeGroupDisplayAction,51 type HomeGroupDisplayAction,
...@@ -269,47 +270,56 @@ function ThreadNavigationSidebarPane(...@@ -269,47 +270,56 @@ function ThreadNavigationSidebarPane(
269 );270 );
270 const scopedProjects = useMemo(271 const scopedProjects = useMemo(
271 () =>272 () =>
272 selectedProjectRefs === null273 threadListV2Enabled
273 ? projects274 ? []
274 : projects.filter((project) =>275 : selectedProjectRefs === null
275 selectedProjectRefs.has(scopedProjectKey(project.environmentId, project.id)),276 ? projects
276 ),277 : projects.filter((project) =>
277 [projects, selectedProjectRefs],278 selectedProjectRefs.has(scopedProjectKey(project.environmentId, project.id)),
279 ),
280 [threadListV2Enabled, projects, selectedProjectRefs],
278 );281 );
279 const scopedThreads = useMemo(282 const scopedThreads = useMemo(
280 () =>283 () =>
281 selectedProjectRefs === null284 threadListV2Enabled
282 ? threads285 ? []
283 : threads.filter((thread) =>286 : selectedProjectRefs === null
284 selectedProjectRefs.has(scopedProjectKey(thread.environmentId, thread.projectId)),287 ? threads
285 ),288 : threads.filter((thread) =>
286 [selectedProjectRefs, threads],289 selectedProjectRefs.has(scopedProjectKey(thread.environmentId, thread.projectId)),
290 ),
291 [threadListV2Enabled, selectedProjectRefs, threads],
287 );292 );
288 const scopedPendingTasks = useMemo(293 const scopedPendingTasks = useMemo(
289 () =>294 () =>
290 selectedProjectRefs === null295 threadListV2Enabled
291 ? pendingTasks296 ? []
292 : pendingTasks.filter((pendingTask) =>297 : selectedProjectRefs === null
293 selectedProjectRefs.has(298 ? pendingTasks
294 scopedProjectKey(pendingTask.message.environmentId, pendingTask.creation.projectId),299 : pendingTasks.filter((pendingTask) =>
300 selectedProjectRefs.has(
301 scopedProjectKey(pendingTask.message.environmentId, pendingTask.creation.projectId),
302 ),
295 ),303 ),
296 ),304 [threadListV2Enabled, pendingTasks, selectedProjectRefs],
297 [pendingTasks, selectedProjectRefs],
298 );305 );
299 const groups = useMemo(306 const groups = useMemo(
300 () =>307 () =>
301 buildHomeThreadGroups({308 threadListV2Enabled
302 projects: scopedProjects,309 ? []
303 threads: scopedThreads,310 : buildHomeThreadGroups({
304 pendingTasks: scopedPendingTasks,311 projects: scopedProjects,
305 environmentId: options.selectedEnvironmentId,312 threads: scopedThreads,
306 searchQuery: props.searchQuery,313 pendingTasks: scopedPendingTasks,
307 matchedThreadKeys,314 environmentId: options.selectedEnvironmentId,
308 projectSortOrder: options.projectSortOrder,315 searchQuery: props.searchQuery,
309 threadSortOrder: options.threadSortOrder,316 matchedThreadKeys,
310 projectGroupingMode: options.projectGroupingMode,317 projectSortOrder: options.projectSortOrder,
311 }),318 threadSortOrder: options.threadSortOrder,
319 projectGroupingMode: options.projectGroupingMode,
320 }),
312 [321 [
322 threadListV2Enabled,
313 matchedThreadKeys,323 matchedThreadKeys,
314 options,324 options,
315 props.searchQuery,325 props.searchQuery,
...@@ -334,12 +344,14 @@ function ThreadNavigationSidebarPane(...@@ -334,12 +344,14 @@ function ThreadNavigationSidebarPane(
334 const hasSearchQuery = props.searchQuery.trim().length > 0;344 const hasSearchQuery = props.searchQuery.trim().length > 0;
335 const listLayout = useMemo(345 const listLayout = useMemo(
336 () =>346 () =>
337 buildHomeListLayout({347 threadListV2Enabled
338 groups,348 ? EMPTY_HOME_LIST_LAYOUT
339 displayStates: groupDisplayStates,349 : buildHomeListLayout({
340 showAllThreads: hasSearchQuery,350 groups,
341 }),351 displayStates: groupDisplayStates,
342 [groups, groupDisplayStates, hasSearchQuery],352 showAllThreads: hasSearchQuery,
353 }),
354 [threadListV2Enabled, groups, groupDisplayStates, hasSearchQuery],
343 );355 );
344 const projectCwdByKey = useMemo(() => {356 const projectCwdByKey = useMemo(() => {
345 const map = new Map<string, string>();357 const map = new Map<string, string>();