From 122358c9a209bccfa414041ed1b4dce8407e7c34 Mon Sep 17 00:00:00 2001 From: Thomas SharedInbox Date: Fri, 15 May 2026 15:36:05 +0200 Subject: [PATCH] fix(ux): navigate back after deleting all search results (#85) When the user searches in a mailbox, selects all results, and deletes them, re-evaluate the search. If no results remain and there is a previous screen in the navigation stack, pop back to it instead of clearing the search and showing the regular inbox. Co-Authored-By: Claude Sonnet 4.6 --- lib/ui/screens/email_list_screen.dart | 6 +- test/widget/email_list_screen_test.dart | 81 +++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 1 deletion(-) diff --git a/lib/ui/screens/email_list_screen.dart b/lib/ui/screens/email_list_screen.dart index a18c9b9..e945d3a 100644 --- a/lib/ui/screens/email_list_screen.dart +++ b/lib/ui/screens/email_list_screen.dart @@ -503,7 +503,11 @@ class _EmailListScreenState extends ConsumerState { .searchEmails(widget.accountId, widget.mailboxPath, searchQuery); if (!mounted) return; if (remaining.isEmpty) { - _searchController.clear(); + if (context.canPop()) { + context.pop(); + } else { + _searchController.clear(); + } } else { setState(() => _searchResults = remaining); } diff --git a/test/widget/email_list_screen_test.dart b/test/widget/email_list_screen_test.dart index 14fdb6a..ae63577 100644 --- a/test/widget/email_list_screen_test.dart +++ b/test/widget/email_list_screen_test.dart @@ -3,9 +3,30 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:sharedinbox/core/models/email.dart'; import 'package:sharedinbox/di.dart'; +import 'package:sharedinbox/ui/screens/email_list_screen.dart'; +import 'package:sharedinbox/ui/screens/mailbox_list_screen.dart'; import 'helpers.dart'; +// A fake email repository whose search results can be changed mid-test. +class _MutableFakeEmailRepository extends FakeEmailRepository { + List _results; + + _MutableFakeEmailRepository(List initial) + : _results = List.of(initial), + super(); + + void setSearchResults(List results) => _results = results; + + @override + Future> searchEmails( + String accountId, + String mailboxPath, + String query, + ) async => + _results; +} + final _kDate = DateTime(2024, 6); void main() { @@ -405,6 +426,66 @@ void main() { expect(find.text('Result email'), findsWidgets); }); + testWidgets( + 'deleting all search results pops back to previous screen', + (tester) async { + final email = testEmail(subject: 'Needle'); + final repo = _MutableFakeEmailRepository([email]); + + // Start at the mailbox list so the email list is pushed on top of it, + // making context.canPop() == true inside EmailListScreen. + await tester.pumpWidget( + buildApp( + initialLocation: '/accounts/acc-1/mailboxes', + overrides: [ + accountRepositoryProvider.overrideWithValue( + FakeAccountRepository([kTestAccount]), + ), + mailboxRepositoryProvider.overrideWithValue( + FakeMailboxRepository([kTestMailbox]), + ), + emailRepositoryProvider.overrideWithValue(repo), + ], + ), + ); + await tester.pumpAndSettle(); + + expect(find.byType(MailboxListScreen), findsOneWidget); + + // Navigate into INBOX (pushes EmailListScreen onto the stack). + await tester.tap(find.text('INBOX')); + await tester.pumpAndSettle(); + + expect(find.byType(EmailListScreen), findsOneWidget); + + // Search for the email. + await tester.enterText(find.byType(TextField), 'Needle'); + await tester.testTextInput.receiveAction(TextInputAction.search); + await tester.pumpAndSettle(); + + // 'Needle' also appears in the SearchBar input, so match at least one. + expect(find.text('Needle'), findsAtLeastNWidgets(1)); + + // Long-press the sender name (unique to the email tile) to enter + // selection mode. + await tester.longPress(find.text('Bob')); + await tester.pumpAndSettle(); + + await tester.tap(find.byIcon(Icons.select_all)); + await tester.pumpAndSettle(); + + // After deletion the search re-runs and finds nothing. + repo.setSearchResults([]); + + await tester.tap(find.byIcon(Icons.delete)); + await tester.pumpAndSettle(); + + // Should have popped back to the mailbox list. + expect(find.byType(EmailListScreen), findsNothing); + expect(find.byType(MailboxListScreen), findsOneWidget); + }, + ); + testWidgets('shows preview snippet when email has preview', (tester) async { final email = Email( id: 'acc-1:99',