From d2f11486838cb576c4e0d9128210647cec98e9a3 Mon Sep 17 00:00:00 2001 From: chungwwei Date: Wed, 19 Nov 2025 11:39:06 -0500 Subject: [PATCH 1/5] actions: Add markNarrowAsRead dialog to CombinedFeedNarrow View A confirm ActionDialog is added to prevent accidentally marking many messages as read in CombinedFeedNarrow view. Also, adjust existing MarkAsReadWidget tests to account for the added confirmation dialog. Fixes: part of #1858 --- assets/l10n/app_en.arb | 12 ++++++++ lib/generated/l10n/zulip_localizations.dart | 18 ++++++++++++ .../l10n/zulip_localizations_ar.dart | 10 +++++++ .../l10n/zulip_localizations_de.dart | 10 +++++++ .../l10n/zulip_localizations_el.dart | 10 +++++++ .../l10n/zulip_localizations_en.dart | 10 +++++++ .../l10n/zulip_localizations_es.dart | 10 +++++++ .../l10n/zulip_localizations_fr.dart | 10 +++++++ .../l10n/zulip_localizations_he.dart | 10 +++++++ .../l10n/zulip_localizations_hu.dart | 10 +++++++ .../l10n/zulip_localizations_it.dart | 10 +++++++ .../l10n/zulip_localizations_ja.dart | 10 +++++++ .../l10n/zulip_localizations_nb.dart | 10 +++++++ .../l10n/zulip_localizations_pl.dart | 10 +++++++ .../l10n/zulip_localizations_ru.dart | 10 +++++++ .../l10n/zulip_localizations_sk.dart | 10 +++++++ .../l10n/zulip_localizations_sl.dart | 10 +++++++ .../l10n/zulip_localizations_uk.dart | 10 +++++++ .../l10n/zulip_localizations_zh.dart | 10 +++++++ lib/widgets/actions.dart | 11 ++++++++ test/widgets/actions_test.dart | 28 +++++++++++++++---- test/widgets/message_list_test.dart | 16 +++++++++++ 22 files changed, 249 insertions(+), 6 deletions(-) diff --git a/assets/l10n/app_en.arb b/assets/l10n/app_en.arb index 38b49c2edd..80b37efa8b 100644 --- a/assets/l10n/app_en.arb +++ b/assets/l10n/app_en.arb @@ -932,6 +932,18 @@ "@errorMarkAsUnreadFailedTitle": { "description": "Error title when mark as unread action failed." }, + "markAllAsReadConfirmationDialogTitle": "Mark messages as read?", + "@markAllAsReadConfirmationDialogTitle": { + "description": "Title of the confirmation dialog for marking all messages as read" + }, + "markAllAsReadConfirmationDialogMessage": "To preserve your reading state, this view does not mark messages as read.", + "@markAllAsReadConfirmationDialogMessage": { + "description": "Message that warns the user they may unintentionally mark messages as read" + }, + "markAllAsReadConfirmationDialogAction": "Mark as read", + "@markAllAsReadConfirmationDialogAction": { + "description": "Action message to confirm marking all messages as read." + }, "today": "Today", "@today": { "description": "Term to use to reference the current day." diff --git a/lib/generated/l10n/zulip_localizations.dart b/lib/generated/l10n/zulip_localizations.dart index fe79921c07..34640c15f7 100644 --- a/lib/generated/l10n/zulip_localizations.dart +++ b/lib/generated/l10n/zulip_localizations.dart @@ -1387,6 +1387,24 @@ abstract class ZulipLocalizations { /// **'Mark as unread failed'** String get errorMarkAsUnreadFailedTitle; + /// Title of the confirmation dialog for marking all messages as read + /// + /// In en, this message translates to: + /// **'Mark messages as read?'** + String get markAllAsReadConfirmationDialogTitle; + + /// Message that warns the user they may unintentionally mark messages as read + /// + /// In en, this message translates to: + /// **'To preserve your reading state, this view does not mark messages as read.'** + String get markAllAsReadConfirmationDialogMessage; + + /// Action message to confirm marking all messages as read. + /// + /// In en, this message translates to: + /// **'Mark as read'** + String get markAllAsReadConfirmationDialogAction; + /// Term to use to reference the current day. /// /// In en, this message translates to: diff --git a/lib/generated/l10n/zulip_localizations_ar.dart b/lib/generated/l10n/zulip_localizations_ar.dart index 5e69a181da..1200c4c8fd 100644 --- a/lib/generated/l10n/zulip_localizations_ar.dart +++ b/lib/generated/l10n/zulip_localizations_ar.dart @@ -774,6 +774,16 @@ class ZulipLocalizationsAr extends ZulipLocalizations { @override String get errorMarkAsUnreadFailedTitle => 'Mark as unread failed'; + @override + String get markAllAsReadConfirmationDialogTitle => 'Mark messages as read?'; + + @override + String get markAllAsReadConfirmationDialogMessage => + 'To preserve your reading state, this view does not mark messages as read.'; + + @override + String get markAllAsReadConfirmationDialogAction => 'Mark as read'; + @override String get today => 'Today'; diff --git a/lib/generated/l10n/zulip_localizations_de.dart b/lib/generated/l10n/zulip_localizations_de.dart index 241e886e55..8fe9236974 100644 --- a/lib/generated/l10n/zulip_localizations_de.dart +++ b/lib/generated/l10n/zulip_localizations_de.dart @@ -793,6 +793,16 @@ class ZulipLocalizationsDe extends ZulipLocalizations { String get errorMarkAsUnreadFailedTitle => 'Als ungelesen markieren fehlgeschlagen'; + @override + String get markAllAsReadConfirmationDialogTitle => 'Mark messages as read?'; + + @override + String get markAllAsReadConfirmationDialogMessage => + 'To preserve your reading state, this view does not mark messages as read.'; + + @override + String get markAllAsReadConfirmationDialogAction => 'Mark as read'; + @override String get today => 'Heute'; diff --git a/lib/generated/l10n/zulip_localizations_el.dart b/lib/generated/l10n/zulip_localizations_el.dart index ce72dc77bf..5a53732071 100644 --- a/lib/generated/l10n/zulip_localizations_el.dart +++ b/lib/generated/l10n/zulip_localizations_el.dart @@ -774,6 +774,16 @@ class ZulipLocalizationsEl extends ZulipLocalizations { @override String get errorMarkAsUnreadFailedTitle => 'Mark as unread failed'; + @override + String get markAllAsReadConfirmationDialogTitle => 'Mark messages as read?'; + + @override + String get markAllAsReadConfirmationDialogMessage => + 'To preserve your reading state, this view does not mark messages as read.'; + + @override + String get markAllAsReadConfirmationDialogAction => 'Mark as read'; + @override String get today => 'Today'; diff --git a/lib/generated/l10n/zulip_localizations_en.dart b/lib/generated/l10n/zulip_localizations_en.dart index 6d10362555..46ebc9bdf8 100644 --- a/lib/generated/l10n/zulip_localizations_en.dart +++ b/lib/generated/l10n/zulip_localizations_en.dart @@ -774,6 +774,16 @@ class ZulipLocalizationsEn extends ZulipLocalizations { @override String get errorMarkAsUnreadFailedTitle => 'Mark as unread failed'; + @override + String get markAllAsReadConfirmationDialogTitle => 'Mark messages as read?'; + + @override + String get markAllAsReadConfirmationDialogMessage => + 'To preserve your reading state, this view does not mark messages as read.'; + + @override + String get markAllAsReadConfirmationDialogAction => 'Mark as read'; + @override String get today => 'Today'; diff --git a/lib/generated/l10n/zulip_localizations_es.dart b/lib/generated/l10n/zulip_localizations_es.dart index 80098d557f..ef1ecbe462 100644 --- a/lib/generated/l10n/zulip_localizations_es.dart +++ b/lib/generated/l10n/zulip_localizations_es.dart @@ -774,6 +774,16 @@ class ZulipLocalizationsEs extends ZulipLocalizations { @override String get errorMarkAsUnreadFailedTitle => 'Mark as unread failed'; + @override + String get markAllAsReadConfirmationDialogTitle => 'Mark messages as read?'; + + @override + String get markAllAsReadConfirmationDialogMessage => + 'To preserve your reading state, this view does not mark messages as read.'; + + @override + String get markAllAsReadConfirmationDialogAction => 'Mark as read'; + @override String get today => 'Today'; diff --git a/lib/generated/l10n/zulip_localizations_fr.dart b/lib/generated/l10n/zulip_localizations_fr.dart index adb41177b2..25a802da52 100644 --- a/lib/generated/l10n/zulip_localizations_fr.dart +++ b/lib/generated/l10n/zulip_localizations_fr.dart @@ -790,6 +790,16 @@ class ZulipLocalizationsFr extends ZulipLocalizations { @override String get errorMarkAsUnreadFailedTitle => 'Mark as unread failed'; + @override + String get markAllAsReadConfirmationDialogTitle => 'Mark messages as read?'; + + @override + String get markAllAsReadConfirmationDialogMessage => + 'To preserve your reading state, this view does not mark messages as read.'; + + @override + String get markAllAsReadConfirmationDialogAction => 'Mark as read'; + @override String get today => 'Today'; diff --git a/lib/generated/l10n/zulip_localizations_he.dart b/lib/generated/l10n/zulip_localizations_he.dart index e59309be3e..1b53cbd8d6 100644 --- a/lib/generated/l10n/zulip_localizations_he.dart +++ b/lib/generated/l10n/zulip_localizations_he.dart @@ -774,6 +774,16 @@ class ZulipLocalizationsHe extends ZulipLocalizations { @override String get errorMarkAsUnreadFailedTitle => 'Mark as unread failed'; + @override + String get markAllAsReadConfirmationDialogTitle => 'Mark messages as read?'; + + @override + String get markAllAsReadConfirmationDialogMessage => + 'To preserve your reading state, this view does not mark messages as read.'; + + @override + String get markAllAsReadConfirmationDialogAction => 'Mark as read'; + @override String get today => 'Today'; diff --git a/lib/generated/l10n/zulip_localizations_hu.dart b/lib/generated/l10n/zulip_localizations_hu.dart index 6a3c11465c..7baf15a8dd 100644 --- a/lib/generated/l10n/zulip_localizations_hu.dart +++ b/lib/generated/l10n/zulip_localizations_hu.dart @@ -774,6 +774,16 @@ class ZulipLocalizationsHu extends ZulipLocalizations { @override String get errorMarkAsUnreadFailedTitle => 'Mark as unread failed'; + @override + String get markAllAsReadConfirmationDialogTitle => 'Mark messages as read?'; + + @override + String get markAllAsReadConfirmationDialogMessage => + 'To preserve your reading state, this view does not mark messages as read.'; + + @override + String get markAllAsReadConfirmationDialogAction => 'Mark as read'; + @override String get today => 'Today'; diff --git a/lib/generated/l10n/zulip_localizations_it.dart b/lib/generated/l10n/zulip_localizations_it.dart index cd27a1daa7..d04554dc8b 100644 --- a/lib/generated/l10n/zulip_localizations_it.dart +++ b/lib/generated/l10n/zulip_localizations_it.dart @@ -785,6 +785,16 @@ class ZulipLocalizationsIt extends ZulipLocalizations { String get errorMarkAsUnreadFailedTitle => 'Contrassegno come non letti non riuscito'; + @override + String get markAllAsReadConfirmationDialogTitle => 'Mark messages as read?'; + + @override + String get markAllAsReadConfirmationDialogMessage => + 'To preserve your reading state, this view does not mark messages as read.'; + + @override + String get markAllAsReadConfirmationDialogAction => 'Mark as read'; + @override String get today => 'Oggi'; diff --git a/lib/generated/l10n/zulip_localizations_ja.dart b/lib/generated/l10n/zulip_localizations_ja.dart index d11aedf19f..b5b264bc1d 100644 --- a/lib/generated/l10n/zulip_localizations_ja.dart +++ b/lib/generated/l10n/zulip_localizations_ja.dart @@ -752,6 +752,16 @@ class ZulipLocalizationsJa extends ZulipLocalizations { @override String get errorMarkAsUnreadFailedTitle => '未読にできませんでした'; + @override + String get markAllAsReadConfirmationDialogTitle => 'Mark messages as read?'; + + @override + String get markAllAsReadConfirmationDialogMessage => + 'To preserve your reading state, this view does not mark messages as read.'; + + @override + String get markAllAsReadConfirmationDialogAction => 'Mark as read'; + @override String get today => '今日'; diff --git a/lib/generated/l10n/zulip_localizations_nb.dart b/lib/generated/l10n/zulip_localizations_nb.dart index 162ca9e677..1b287e1e5d 100644 --- a/lib/generated/l10n/zulip_localizations_nb.dart +++ b/lib/generated/l10n/zulip_localizations_nb.dart @@ -774,6 +774,16 @@ class ZulipLocalizationsNb extends ZulipLocalizations { @override String get errorMarkAsUnreadFailedTitle => 'Mark as unread failed'; + @override + String get markAllAsReadConfirmationDialogTitle => 'Mark messages as read?'; + + @override + String get markAllAsReadConfirmationDialogMessage => + 'To preserve your reading state, this view does not mark messages as read.'; + + @override + String get markAllAsReadConfirmationDialogAction => 'Mark as read'; + @override String get today => 'Today'; diff --git a/lib/generated/l10n/zulip_localizations_pl.dart b/lib/generated/l10n/zulip_localizations_pl.dart index 40682171b2..8c45b615af 100644 --- a/lib/generated/l10n/zulip_localizations_pl.dart +++ b/lib/generated/l10n/zulip_localizations_pl.dart @@ -790,6 +790,16 @@ class ZulipLocalizationsPl extends ZulipLocalizations { String get errorMarkAsUnreadFailedTitle => 'Oznaczanie jako nieprzeczytane bez powodzenia'; + @override + String get markAllAsReadConfirmationDialogTitle => 'Mark messages as read?'; + + @override + String get markAllAsReadConfirmationDialogMessage => + 'To preserve your reading state, this view does not mark messages as read.'; + + @override + String get markAllAsReadConfirmationDialogAction => 'Mark as read'; + @override String get today => 'Dzisiaj'; diff --git a/lib/generated/l10n/zulip_localizations_ru.dart b/lib/generated/l10n/zulip_localizations_ru.dart index 04f8903b91..cd3c172702 100644 --- a/lib/generated/l10n/zulip_localizations_ru.dart +++ b/lib/generated/l10n/zulip_localizations_ru.dart @@ -795,6 +795,16 @@ class ZulipLocalizationsRu extends ZulipLocalizations { String get errorMarkAsUnreadFailedTitle => 'Не удалось снять отметку прочтения'; + @override + String get markAllAsReadConfirmationDialogTitle => 'Mark messages as read?'; + + @override + String get markAllAsReadConfirmationDialogMessage => + 'To preserve your reading state, this view does not mark messages as read.'; + + @override + String get markAllAsReadConfirmationDialogAction => 'Mark as read'; + @override String get today => 'Сегодня'; diff --git a/lib/generated/l10n/zulip_localizations_sk.dart b/lib/generated/l10n/zulip_localizations_sk.dart index e2dc542fc5..00686f38b5 100644 --- a/lib/generated/l10n/zulip_localizations_sk.dart +++ b/lib/generated/l10n/zulip_localizations_sk.dart @@ -776,6 +776,16 @@ class ZulipLocalizationsSk extends ZulipLocalizations { String get errorMarkAsUnreadFailedTitle => 'Zlyhalo označenie správ za prečítané'; + @override + String get markAllAsReadConfirmationDialogTitle => 'Mark messages as read?'; + + @override + String get markAllAsReadConfirmationDialogMessage => + 'To preserve your reading state, this view does not mark messages as read.'; + + @override + String get markAllAsReadConfirmationDialogAction => 'Mark as read'; + @override String get today => 'Dnes'; diff --git a/lib/generated/l10n/zulip_localizations_sl.dart b/lib/generated/l10n/zulip_localizations_sl.dart index 6f9d8343ca..6eb45640a6 100644 --- a/lib/generated/l10n/zulip_localizations_sl.dart +++ b/lib/generated/l10n/zulip_localizations_sl.dart @@ -804,6 +804,16 @@ class ZulipLocalizationsSl extends ZulipLocalizations { String get errorMarkAsUnreadFailedTitle => 'Označevanje kot neprebrano ni uspelo'; + @override + String get markAllAsReadConfirmationDialogTitle => 'Mark messages as read?'; + + @override + String get markAllAsReadConfirmationDialogMessage => + 'To preserve your reading state, this view does not mark messages as read.'; + + @override + String get markAllAsReadConfirmationDialogAction => 'Mark as read'; + @override String get today => 'Danes'; diff --git a/lib/generated/l10n/zulip_localizations_uk.dart b/lib/generated/l10n/zulip_localizations_uk.dart index 84df26a42e..3872f12348 100644 --- a/lib/generated/l10n/zulip_localizations_uk.dart +++ b/lib/generated/l10n/zulip_localizations_uk.dart @@ -791,6 +791,16 @@ class ZulipLocalizationsUk extends ZulipLocalizations { String get errorMarkAsUnreadFailedTitle => 'Не вдалося позначити як непрочитане'; + @override + String get markAllAsReadConfirmationDialogTitle => 'Mark messages as read?'; + + @override + String get markAllAsReadConfirmationDialogMessage => + 'To preserve your reading state, this view does not mark messages as read.'; + + @override + String get markAllAsReadConfirmationDialogAction => 'Mark as read'; + @override String get today => 'Сьогодні'; diff --git a/lib/generated/l10n/zulip_localizations_zh.dart b/lib/generated/l10n/zulip_localizations_zh.dart index b08c25004e..13ce0ffd23 100644 --- a/lib/generated/l10n/zulip_localizations_zh.dart +++ b/lib/generated/l10n/zulip_localizations_zh.dart @@ -774,6 +774,16 @@ class ZulipLocalizationsZh extends ZulipLocalizations { @override String get errorMarkAsUnreadFailedTitle => 'Mark as unread failed'; + @override + String get markAllAsReadConfirmationDialogTitle => 'Mark messages as read?'; + + @override + String get markAllAsReadConfirmationDialogMessage => + 'To preserve your reading state, this view does not mark messages as read.'; + + @override + String get markAllAsReadConfirmationDialogAction => 'Mark as read'; + @override String get today => 'Today'; diff --git a/lib/widgets/actions.dart b/lib/widgets/actions.dart index bd8274613f..327bbbcb7d 100644 --- a/lib/widgets/actions.dart +++ b/lib/widgets/actions.dart @@ -32,6 +32,17 @@ abstract final class ZulipAction { static Future markNarrowAsRead(BuildContext context, Narrow narrow) async { final zulipLocalizations = ZulipLocalizations.of(context); + if (narrow is CombinedFeedNarrow) { + final didConfirm = showSuggestedActionDialog(context: context, + title: zulipLocalizations.markAllAsReadConfirmationDialogTitle, + message: zulipLocalizations.markAllAsReadConfirmationDialogMessage, + actionButtonText: zulipLocalizations.markAllAsReadConfirmationDialogAction, + ); + + if (await didConfirm.result != true) return; + if (!context.mounted) return; + } + final didPass = await updateMessageFlagsStartingFromAnchor( context: context, // Include `is:unread` in the narrow. That has a database index, so diff --git a/test/widgets/actions_test.dart b/test/widgets/actions_test.dart index 7c292aa62e..db1f7a4fd2 100644 --- a/test/widgets/actions_test.dart +++ b/test/widgets/actions_test.dart @@ -56,6 +56,26 @@ void main() { } group('markNarrowAsRead', () { + Future confirmToMarkNarrowAsRead({required WidgetTester tester, + required BuildContext context, required Narrow narrow + }) async { + final zulipLocalizations = GlobalLocalizations.zulipLocalizations; + + final future = tester.runAsync(() => ZulipAction.markNarrowAsRead(context, narrow)); + + await tester.pump(); + final (confirmButton, _) = checkSuggestedActionDialog(tester, + expectedTitle: zulipLocalizations.markAllAsReadConfirmationDialogTitle, + expectedMessage: zulipLocalizations.markAllAsReadConfirmationDialogMessage, + expectedActionButtonText: zulipLocalizations.markAllAsReadConfirmationDialogAction, + ); + + await tester.tap(find.byWidget(confirmButton)); + await tester.pump(); + + await future; + } + testWidgets('smoke test on modern server', (tester) async { final narrow = TopicNarrow.ofMessage(eg.streamMessage()); await prepare(tester); @@ -88,9 +108,7 @@ void main() { processedCount: 11, updatedCount: 3, firstProcessedId: null, lastProcessedId: null, foundOldest: true, foundNewest: true).toJson()); - final future = ZulipAction.markNarrowAsRead(context, narrow); - await tester.pump(Duration.zero); - await future; + await confirmToMarkNarrowAsRead(tester: tester, context: context, narrow: narrow); check(connection.lastRequest).isA() ..method.equals('POST') ..url.path.equals('/api/v1/messages/flags/narrow') @@ -114,9 +132,7 @@ void main() { processedCount: 11, updatedCount: 3, firstProcessedId: null, lastProcessedId: null, foundOldest: true, foundNewest: true).toJson()); - final future = ZulipAction.markNarrowAsRead(context, narrow); - await tester.pump(Duration.zero); - await future; + await confirmToMarkNarrowAsRead(tester: tester, context: context, narrow: narrow); check(store.unreads.oldUnreadsMissing).isFalse(); }); }); diff --git a/test/widgets/message_list_test.dart b/test/widgets/message_list_test.dart index 89b2a31bd8..905b16b14f 100644 --- a/test/widgets/message_list_test.dart +++ b/test/widgets/message_list_test.dart @@ -972,6 +972,16 @@ void main() { return finder.evaluate().isNotEmpty; } + (Widget, Widget) checkConfirmDialog(WidgetTester tester) { + final zulipLocalizations = GlobalLocalizations.zulipLocalizations; + return checkSuggestedActionDialog(tester, + expectedTitle: zulipLocalizations.markAllAsReadConfirmationDialogTitle, + expectedMessage: zulipLocalizations.markAllAsReadConfirmationDialogMessage, + expectDestructiveActionButton: true, + expectedActionButtonText: zulipLocalizations.markAllAsReadConfirmationDialogAction, + ); + } + testWidgets('from read to unread', (tester) async { final message = eg.streamMessage(flags: [MessageFlag.read]); await setupMessageListPage(tester, messages: [message]); @@ -1161,6 +1171,9 @@ void main() { firstProcessedId: null, lastProcessedId: null, foundOldest: true, foundNewest: true).toJson()); await tester.tap(find.byType(MarkAsReadWidget)); + await tester.pump(); + final (confirmButton, _) = checkConfirmDialog(tester); + await tester.tap(find.byWidget(confirmButton)); await tester.pumpAndSettle(); check(store.unreads.oldUnreadsMissing).isFalse(); }); @@ -1174,6 +1187,9 @@ void main() { connection.prepare(httpException: http.ClientException('Oops')); await tester.tap(find.byType(MarkAsReadWidget)); + await tester.pump(); + final (confirmButton, _) = checkConfirmDialog(tester); + await tester.tap(find.byWidget(confirmButton)); await tester.pumpAndSettle(); checkErrorDialog(tester, expectedTitle: zulipLocalizations.errorMarkAsReadFailedTitle, From 4e1b373a17b6a4b1230d76d0110cec7777ebf379 Mon Sep 17 00:00:00 2001 From: chungwwei Date: Tue, 2 Dec 2025 22:26:42 -0500 Subject: [PATCH 2/5] actions: Add markNarrowAsRead dialog to ChannelNarrow View Added dialog to prevent accidentally marking many messages as read in ChannelNarrow View. Also, adjust existing MarkChannelAsReadButton tests to account for the added confirmation dialog. Fixes: part of #1858 --- lib/widgets/actions.dart | 2 +- test/widgets/action_sheet_test.dart | 22 ++++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/lib/widgets/actions.dart b/lib/widgets/actions.dart index 327bbbcb7d..0802381448 100644 --- a/lib/widgets/actions.dart +++ b/lib/widgets/actions.dart @@ -32,7 +32,7 @@ abstract final class ZulipAction { static Future markNarrowAsRead(BuildContext context, Narrow narrow) async { final zulipLocalizations = ZulipLocalizations.of(context); - if (narrow is CombinedFeedNarrow) { + if (narrow is CombinedFeedNarrow || narrow is ChannelNarrow) { final didConfirm = showSuggestedActionDialog(context: context, title: zulipLocalizations.markAllAsReadConfirmationDialogTitle, message: zulipLocalizations.markAllAsReadConfirmationDialogMessage, diff --git a/test/widgets/action_sheet_test.dart b/test/widgets/action_sheet_test.dart index c43c6daab7..8c65743a44 100644 --- a/test/widgets/action_sheet_test.dart +++ b/test/widgets/action_sheet_test.dart @@ -471,6 +471,8 @@ void main() { } testWidgets('happy path from inbox', (tester) async { + final zulipLocalizations = GlobalLocalizations.zulipLocalizations; + await prepare(); final message = eg.streamMessage(stream: someChannel, topic: someTopic); await store.addMessage(message); @@ -480,17 +482,37 @@ void main() { firstProcessedId: message.id, lastProcessedId: message.id, foundOldest: true, foundNewest: true).toJson()); await tester.tap(findButtonForLabel('Mark channel as read')); + await tester.pump(); + await tester.pump(); + final (confirmButton, _) = checkSuggestedActionDialog(tester, + expectedTitle: zulipLocalizations.markAllAsReadConfirmationDialogTitle, + expectedMessage: zulipLocalizations.markAllAsReadConfirmationDialogMessage, + expectedActionButtonText: zulipLocalizations.markAllAsReadConfirmationDialogAction, + ); + await tester.tap(find.byWidget(confirmButton)); await tester.pumpAndSettle(); + checkRequest(someChannel.streamId); checkNoDialog(tester); }); testWidgets('request fails', (tester) async { + final zulipLocalizations = GlobalLocalizations.zulipLocalizations; + await prepare(); await showFromInbox(tester); connection.prepare(httpException: http.ClientException('Oops')); await tester.tap(findButtonForLabel('Mark channel as read')); + await tester.pump(); + await tester.pump(); + final (confirmButton, _) = checkSuggestedActionDialog(tester, + expectedTitle: zulipLocalizations.markAllAsReadConfirmationDialogTitle, + expectedMessage: zulipLocalizations.markAllAsReadConfirmationDialogMessage, + expectedActionButtonText: zulipLocalizations.markAllAsReadConfirmationDialogAction, + ); + await tester.tap(find.byWidget(confirmButton)); await tester.pumpAndSettle(); + checkRequest(someChannel.streamId); checkErrorDialog(tester, expectedTitle: "Mark as read failed"); From 804de2758862812052f91c2f77152aca695c4538 Mon Sep 17 00:00:00 2001 From: chungwwei Date: Tue, 2 Dec 2025 22:40:16 -0500 Subject: [PATCH 3/5] actions: Add markNarrowAsRead dialog to MentionsNarrow view --- lib/widgets/actions.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/widgets/actions.dart b/lib/widgets/actions.dart index 0802381448..4a8efd65c1 100644 --- a/lib/widgets/actions.dart +++ b/lib/widgets/actions.dart @@ -32,7 +32,7 @@ abstract final class ZulipAction { static Future markNarrowAsRead(BuildContext context, Narrow narrow) async { final zulipLocalizations = ZulipLocalizations.of(context); - if (narrow is CombinedFeedNarrow || narrow is ChannelNarrow) { + if (narrow is CombinedFeedNarrow || narrow is ChannelNarrow || narrow is MentionsNarrow) { final didConfirm = showSuggestedActionDialog(context: context, title: zulipLocalizations.markAllAsReadConfirmationDialogTitle, message: zulipLocalizations.markAllAsReadConfirmationDialogMessage, From 06e2ae066fff78b4a11addd1a60c258b674ca772 Mon Sep 17 00:00:00 2001 From: chungwwei Date: Tue, 2 Dec 2025 22:43:14 -0500 Subject: [PATCH 4/5] actions: Add markNarrowAsRead dialog to KeywordSearchNarrow view --- lib/widgets/actions.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/widgets/actions.dart b/lib/widgets/actions.dart index 4a8efd65c1..7b1d65c8c9 100644 --- a/lib/widgets/actions.dart +++ b/lib/widgets/actions.dart @@ -32,7 +32,8 @@ abstract final class ZulipAction { static Future markNarrowAsRead(BuildContext context, Narrow narrow) async { final zulipLocalizations = ZulipLocalizations.of(context); - if (narrow is CombinedFeedNarrow || narrow is ChannelNarrow || narrow is MentionsNarrow) { + if (narrow is CombinedFeedNarrow || narrow is ChannelNarrow || + narrow is MentionsNarrow || narrow is KeywordSearchNarrow) { final didConfirm = showSuggestedActionDialog(context: context, title: zulipLocalizations.markAllAsReadConfirmationDialogTitle, message: zulipLocalizations.markAllAsReadConfirmationDialogMessage, From 712aa85ccaaffc76e7ec5d64959e4ddd327223f3 Mon Sep 17 00:00:00 2001 From: chungwwei Date: Tue, 2 Dec 2025 22:53:52 -0500 Subject: [PATCH 5/5] actions: Add markNarrowAsRead dialog to StarredMessagesNarrow view --- lib/widgets/actions.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/widgets/actions.dart b/lib/widgets/actions.dart index 7b1d65c8c9..a769ff7f13 100644 --- a/lib/widgets/actions.dart +++ b/lib/widgets/actions.dart @@ -33,7 +33,8 @@ abstract final class ZulipAction { final zulipLocalizations = ZulipLocalizations.of(context); if (narrow is CombinedFeedNarrow || narrow is ChannelNarrow || - narrow is MentionsNarrow || narrow is KeywordSearchNarrow) { + narrow is MentionsNarrow || narrow is KeywordSearchNarrow || + narrow is StarredMessagesNarrow) { final didConfirm = showSuggestedActionDialog(context: context, title: zulipLocalizations.markAllAsReadConfirmationDialogTitle, message: zulipLocalizations.markAllAsReadConfirmationDialogMessage,