From 62908440e1f0264e0419c2e261a25be350943169 Mon Sep 17 00:00:00 2001 From: sladro Date: Mon, 6 Apr 2026 09:10:27 +0800 Subject: [PATCH] Retry project loading when reusing agent URL --- .../features/projects/project_list_page.dart | 5 +- apps/mobile_app/test/widget_test.dart | 59 ++++++++++++++++++- 2 files changed, 59 insertions(+), 5 deletions(-) diff --git a/apps/mobile_app/lib/features/projects/project_list_page.dart b/apps/mobile_app/lib/features/projects/project_list_page.dart index 156f50a..0f2fa9f 100644 --- a/apps/mobile_app/lib/features/projects/project_list_page.dart +++ b/apps/mobile_app/lib/features/projects/project_list_page.dart @@ -68,11 +68,10 @@ class _ProjectListPageState extends ConsumerState { } final current = ref.read(agentBaseUriProvider); - if (current == parsedUri) { - return; + if (current != parsedUri) { + ref.read(agentBaseUriProvider.notifier).state = parsedUri; } - ref.read(agentBaseUriProvider.notifier).state = parsedUri; ref.invalidate(sessionsProvider); await _reloadProjects(); } diff --git a/apps/mobile_app/test/widget_test.dart b/apps/mobile_app/test/widget_test.dart index 6f83d4c..8125bb8 100644 --- a/apps/mobile_app/test/widget_test.dart +++ b/apps/mobile_app/test/widget_test.dart @@ -62,6 +62,30 @@ void main() { expect(find.widgetWithText(FilledButton, 'Open terminal'), findsOneWidget); }); + testWidgets( + 'reuses the same agent URL and retries project loading when Use is tapped', + (tester) async { + final projectRepository = _RecoveringProjectRepository(); + + await _pumpApp( + tester, + projectRepository: projectRepository, + sessionRepository: _FakeSessionRepository(), + ); + + expect(find.text('Could not load projects'), findsOneWidget); + expect(projectRepository.listProjectsCallCount, 1); + + await tester.tap(find.widgetWithText(FilledButton, 'Use')); + await tester.pump(); + await tester.pumpAndSettle(); + + expect(projectRepository.listProjectsCallCount, 2); + expect(find.text('Could not load projects'), findsNothing); + expect(find.text('codex-main'), findsOneWidget); + }, + ); + testWidgets('project card opens a terminal without an extra prompt', ( tester, ) async { @@ -275,7 +299,10 @@ void main() { expect(find.byType(TextField), findsNothing); expect(find.byKey(const Key('terminal_actions_sheet')), findsNothing); expect(find.byKey(const Key('terminal_actions_button')), findsOneWidget); - expect(find.byKey(const Key('terminal_toggle_actions_button')), findsNothing); + expect( + find.byKey(const Key('terminal_toggle_actions_button')), + findsNothing, + ); await tester.tap(find.byKey(const Key('terminal_actions_button'))); await tester.pumpAndSettle(); @@ -283,7 +310,10 @@ void main() { expect(find.byKey(const Key('terminal_actions_sheet')), findsOneWidget); expect(find.text('Reconnect'), findsOneWidget); expect(find.text('Latest'), findsOneWidget); - expect(find.byKey(const Key('terminal_quick_key_ctrl_c')), findsOneWidget); + expect( + find.byKey(const Key('terminal_quick_key_ctrl_c')), + findsOneWidget, + ); expect(find.text('ssh prod'), findsNothing); }, ); @@ -1183,6 +1213,31 @@ class _FailingProjectRepository extends ProjectRepository { } } +class _RecoveringProjectRepository extends _FakeProjectRepository { + int listProjectsCallCount = 0; + + @override + Future> listProjects() async { + listProjectsCallCount += 1; + if (listProjectsCallCount == 1) { + throw DioException.connectionError( + requestOptions: RequestOptions( + path: '/api/projects', + baseUrl: 'http://100.81.30.82:5067', + ), + reason: 'No route to host', + error: const SocketException( + 'Connection failed', + osError: OSError('No route to host', 65), + port: 5067, + ), + ); + } + + return super.listProjects(); + } +} + class _FakeAgentApiClient extends AgentApiClient { _FakeAgentApiClient() : super(Uri.parse('http://100.81.30.82:5067'));