From eaca7e0f7de6e24231c4e64b5de778dd10952a0c Mon Sep 17 00:00:00 2001 From: Haitao Pan Date: Wed, 27 May 2026 10:37:16 +0800 Subject: [PATCH] Keep sidebar task order stable --- ...pp_controller_desktop_thread_sessions.dart | 7 +-- ...app_controller_desktop_thread_storage.dart | 3 -- .../sidebar_navigation_task_status_test.dart | 53 +++++++++++++++++++ .../assistant_execution_target_test.dart | 36 ++++++++----- 4 files changed, 76 insertions(+), 23 deletions(-) diff --git a/lib/app/app_controller_desktop_thread_sessions.dart b/lib/app/app_controller_desktop_thread_sessions.dart index db2feaf4..dd4c51b2 100644 --- a/lib/app/app_controller_desktop_thread_sessions.dart +++ b/lib/app/app_controller_desktop_thread_sessions.dart @@ -642,12 +642,7 @@ extension AppControllerDesktopThreadSessions on AppController { byKey[currentKey] = assistantSessionSummaryForInternal(currentKey); } - final items = byKey.values.toList(growable: true) - ..sort( - (left, right) => - (right.updatedAtMs ?? 0).compareTo(left.updatedAtMs ?? 0), - ); - return items; + return byKey.values.toList(growable: false); } List archivedAssistantSessionsInternal() { diff --git a/lib/app/app_controller_desktop_thread_storage.dart b/lib/app/app_controller_desktop_thread_storage.dart index a4417761..09e6198b 100644 --- a/lib/app/app_controller_desktop_thread_storage.dart +++ b/lib/app/app_controller_desktop_thread_storage.dart @@ -386,9 +386,6 @@ extension AppControllerDesktopThreadStorage on AppController { items.add(assistantSessionSummaryForInternal(currentSessionKey)); } - items.sort((left, right) { - return (right.updatedAtMs ?? 0).compareTo(left.updatedAtMs ?? 0); - }); return items; } diff --git a/test/features/app/sidebar_navigation_task_status_test.dart b/test/features/app/sidebar_navigation_task_status_test.dart index 6d4fcc0c..95868e2b 100644 --- a/test/features/app/sidebar_navigation_task_status_test.dart +++ b/test/features/app/sidebar_navigation_task_status_test.dart @@ -117,8 +117,61 @@ void main() { findsNothing, ); }); + + testWidgets('sidebar keeps the supplied task order when selection changes', ( + tester, + ) async { + const firstTitle = '先显示任务'; + const selectedTitle = '当前选择任务'; + const lastTitle = '后显示任务'; + + await _pumpSidebar( + tester, + items: const [ + SidebarTaskItem( + sessionKey: 'first-task', + title: firstTitle, + preview: '第一项', + updatedAtMs: 1000, + executionTarget: AssistantExecutionTarget.gateway, + isCurrent: false, + pending: false, + ), + SidebarTaskItem( + sessionKey: 'selected-task', + title: selectedTitle, + preview: '被选中的第二项', + updatedAtMs: 3000, + executionTarget: AssistantExecutionTarget.gateway, + isCurrent: true, + pending: false, + ), + SidebarTaskItem( + sessionKey: 'last-task', + title: lastTitle, + preview: '第三项', + updatedAtMs: 2000, + executionTarget: AssistantExecutionTarget.gateway, + isCurrent: false, + pending: false, + ), + ], + ); + + expect( + _textTop(tester, firstTitle), + lessThan(_textTop(tester, selectedTitle)), + ); + expect( + _textTop(tester, selectedTitle), + lessThan(_textTop(tester, lastTitle)), + ); + }); } +double _textTop(WidgetTester tester, String text) => + tester.getTopLeft(find.text(text)).dy; + Future _pumpSidebar( WidgetTester tester, { required List items, diff --git a/test/runtime/assistant_execution_target_test.dart b/test/runtime/assistant_execution_target_test.dart index 79bafb71..56cbf94b 100644 --- a/test/runtime/assistant_execution_target_test.dart +++ b/test/runtime/assistant_execution_target_test.dart @@ -349,7 +349,7 @@ void main() { }); test( - 'switching sessions does not refresh task ordering timestamp', + 'assistant task list keeps repository order when tasks update', () async { final localHome = await Directory.systemTemp.createTemp( 'xworkmate-stable-task-selection-home-', @@ -365,33 +365,41 @@ void main() { addTearDown(controller.dispose); controller.resolvedUserHomeDirectoryInternal = localHome.path; - const newerTask = 'draft:newer-task'; - const olderTask = 'draft:older-task'; - const newerUpdatedAtMs = 2000.0; - const olderUpdatedAtMs = 1000.0; + const firstTask = 'draft:first-task'; + const secondTask = 'draft:second-task'; + const firstUpdatedAtMs = 1000.0; + const secondUpdatedAtMs = 2000.0; controller.upsertTaskThreadInternal( - newerTask, + firstTask, executionTarget: AssistantExecutionTarget.gateway, messageViewMode: AssistantMessageViewMode.rendered, - updatedAtMs: newerUpdatedAtMs, + updatedAtMs: firstUpdatedAtMs, ); controller.upsertTaskThreadInternal( - olderTask, + secondTask, executionTarget: AssistantExecutionTarget.gateway, messageViewMode: AssistantMessageViewMode.rendered, - updatedAtMs: olderUpdatedAtMs, + updatedAtMs: secondUpdatedAtMs, ); - await controller.switchSession(olderTask); + await controller.switchSession(secondTask); + controller.upsertTaskThreadInternal( + secondTask, + executionTarget: AssistantExecutionTarget.gateway, + messageViewMode: AssistantMessageViewMode.rendered, + updatedAtMs: 3000, + ); - expect(controller.currentSessionKey, olderTask); + expect(controller.currentSessionKey, secondTask); expect( - controller.requireTaskThreadForSessionInternal(olderTask).updatedAtMs, - olderUpdatedAtMs, + controller + .requireTaskThreadForSessionInternal(secondTask) + .updatedAtMs, + 3000, ); expect( controller.assistantSessions.map((item) => item.key).take(2), - [newerTask, olderTask], + [firstTask, secondTask], ); }, );