Skip to content

Commit 7ca77ca

Browse files
committed
action_sheet: Add "Mark Topic As Read" button
fixes: #1225
1 parent 18b2811 commit 7ca77ca

11 files changed

+115
-0
lines changed

Diff for: assets/l10n/app_en.arb

+4
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,10 @@
120120
"@actionSheetOptionUnstarMessage": {
121121
"description": "Label for unstar button on action sheet."
122122
},
123+
"actionSheetOptionMarkTopicAsRead": "Mark Topic As Read",
124+
"@actionSheetOptionMarkTopicAsRead": {
125+
"description": "Option to mark a specific topic as read in the action sheet."
126+
},
123127
"errorWebAuthOperationalErrorTitle": "Something went wrong",
124128
"@errorWebAuthOperationalErrorTitle": {
125129
"description": "Error title when third-party authentication has an operational error (not necessarily caused by invalid credentials)."

Diff for: lib/generated/l10n/zulip_localizations.dart

+6
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,12 @@ abstract class ZulipLocalizations {
285285
/// **'Unstar message'**
286286
String get actionSheetOptionUnstarMessage;
287287

288+
/// Option to mark a specific topic as read in the action sheet.
289+
///
290+
/// In en, this message translates to:
291+
/// **'Mark Topic As Read'**
292+
String get actionSheetOptionMarkTopicAsRead;
293+
288294
/// Error title when third-party authentication has an operational error (not necessarily caused by invalid credentials).
289295
///
290296
/// In en, this message translates to:

Diff for: lib/generated/l10n/zulip_localizations_ar.dart

+3
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@ class ZulipLocalizationsAr extends ZulipLocalizations {
100100
@override
101101
String get actionSheetOptionUnstarMessage => 'Unstar message';
102102

103+
@override
104+
String get actionSheetOptionMarkTopicAsRead => 'Mark Topic As Read';
105+
103106
@override
104107
String get errorWebAuthOperationalErrorTitle => 'Something went wrong';
105108

Diff for: lib/generated/l10n/zulip_localizations_en.dart

+3
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@ class ZulipLocalizationsEn extends ZulipLocalizations {
100100
@override
101101
String get actionSheetOptionUnstarMessage => 'Unstar message';
102102

103+
@override
104+
String get actionSheetOptionMarkTopicAsRead => 'Mark Topic As Read';
105+
103106
@override
104107
String get errorWebAuthOperationalErrorTitle => 'Something went wrong';
105108

Diff for: lib/generated/l10n/zulip_localizations_ja.dart

+3
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@ class ZulipLocalizationsJa extends ZulipLocalizations {
100100
@override
101101
String get actionSheetOptionUnstarMessage => 'Unstar message';
102102

103+
@override
104+
String get actionSheetOptionMarkTopicAsRead => 'Mark Topic As Read';
105+
103106
@override
104107
String get errorWebAuthOperationalErrorTitle => 'Something went wrong';
105108

Diff for: lib/generated/l10n/zulip_localizations_nb.dart

+3
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@ class ZulipLocalizationsNb extends ZulipLocalizations {
100100
@override
101101
String get actionSheetOptionUnstarMessage => 'Unstar message';
102102

103+
@override
104+
String get actionSheetOptionMarkTopicAsRead => 'Mark Topic As Read';
105+
103106
@override
104107
String get errorWebAuthOperationalErrorTitle => 'Something went wrong';
105108

Diff for: lib/generated/l10n/zulip_localizations_pl.dart

+3
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@ class ZulipLocalizationsPl extends ZulipLocalizations {
100100
@override
101101
String get actionSheetOptionUnstarMessage => 'Odbierz gwiazdkę';
102102

103+
@override
104+
String get actionSheetOptionMarkTopicAsRead => 'Mark Topic As Read';
105+
103106
@override
104107
String get errorWebAuthOperationalErrorTitle => 'Coś poszło nie tak';
105108

Diff for: lib/generated/l10n/zulip_localizations_ru.dart

+3
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@ class ZulipLocalizationsRu extends ZulipLocalizations {
100100
@override
101101
String get actionSheetOptionUnstarMessage => 'Снять отметку с сообщения';
102102

103+
@override
104+
String get actionSheetOptionMarkTopicAsRead => 'Mark Topic As Read';
105+
103106
@override
104107
String get errorWebAuthOperationalErrorTitle => 'Что-то пошло не так';
105108

Diff for: lib/generated/l10n/zulip_localizations_sk.dart

+3
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@ class ZulipLocalizationsSk extends ZulipLocalizations {
100100
@override
101101
String get actionSheetOptionUnstarMessage => 'Odhviezdičkovať správu';
102102

103+
@override
104+
String get actionSheetOptionMarkTopicAsRead => 'Mark Topic As Read';
105+
103106
@override
104107
String get errorWebAuthOperationalErrorTitle => 'Niečo sa pokazilo';
105108

Diff for: lib/widgets/action_sheet.dart

+32
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,14 @@ void showTopicActionSheet(BuildContext context, {
240240
pageContext: context);
241241
}));
242242

243+
final unreadCount = store.unreads.countInTopicNarrow(channelId, topic);
244+
if (unreadCount > 0) {
245+
optionButtons.add(MarkTopicAsReadButton(
246+
channelId: channelId,
247+
topic: topic,
248+
pageContext: context));
249+
}
250+
243251
if (optionButtons.isEmpty) {
244252
// TODO(a11y): This case makes a no-op gesture handler; as a consequence,
245253
// we're presenting some UI (to people who use screen-reader software) as
@@ -372,6 +380,30 @@ class UserTopicUpdateButton extends ActionSheetMenuItemButton {
372380
}
373381
}
374382

383+
class MarkTopicAsReadButton extends ActionSheetMenuItemButton {
384+
const MarkTopicAsReadButton({
385+
super.key,
386+
required this.channelId,
387+
required this.topic,
388+
required super.pageContext,
389+
});
390+
391+
final int channelId;
392+
final TopicName topic;
393+
394+
@override IconData get icon => ZulipIcons.message_checked;
395+
396+
@override
397+
String label(ZulipLocalizations zulipLocalizations) {
398+
return zulipLocalizations.actionSheetOptionMarkTopicAsRead;
399+
}
400+
401+
@override void onPressed() async {
402+
if (!pageContext.mounted) return;
403+
await markNarrowAsRead(pageContext, TopicNarrow(channelId, topic));
404+
}
405+
}
406+
375407
/// Show a sheet of actions you can take on a message in the message list.
376408
///
377409
/// Must have a [MessageListPage] ancestor.

Diff for: test/widgets/action_sheet_test.dart

+52
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,58 @@ void main() {
450450
}
451451
});
452452
});
453+
454+
group('MarkTopicAsReadButton', () {
455+
testWidgets('visible if topic has unread messages', (tester) async {
456+
await prepare();
457+
final message = eg.streamMessage(stream: someChannel, topic: someTopic);
458+
await store.handleEvent(MessageEvent(id: 0, message: message));
459+
await showFromAppBar(tester, messages: [message]);
460+
check(find.text('Mark Topic As Read')).findsOne();
461+
});
462+
463+
testWidgets('not visible if topic has no unread messages', (tester) async {
464+
await prepare();
465+
final message = eg.streamMessage(stream: someChannel, topic: someTopic);
466+
await showFromAppBar(tester, messages: [message]);
467+
check(find.text('Mark Topic As Read')).findsNothing();
468+
});
469+
470+
testWidgets('marks topic as read using legacy API when FL < 155', (tester) async {
471+
await prepare(zulipFeatureLevel: 154);
472+
final message = eg.streamMessage(stream: someChannel, topic: someTopic);
473+
await store.handleEvent(MessageEvent(id: 0, message: message));
474+
await showFromAppBar(tester, messages: [message]);
475+
476+
connection.prepare(json: {'result': 'success', 'msg': ''});
477+
await tester.tap(find.text('Mark Topic As Read'));
478+
await tester.pumpAndSettle();
479+
480+
check(connection.lastRequest).isA<http.Request>()
481+
.url.path.equals('/api/v1/mark_topic_as_read');
482+
});
483+
484+
testWidgets('marks topic as read using new API when FL ≥ 155', (tester) async {
485+
await prepare(zulipFeatureLevel: 155);
486+
final message = eg.streamMessage(stream: someChannel, topic: someTopic);
487+
await store.handleEvent(MessageEvent(id: 0, message: message));
488+
await showFromAppBar(tester, messages: [message]);
489+
490+
connection.prepare(json: UpdateMessageFlagsForNarrowResult(
491+
processedCount: 1, updatedCount: 1,
492+
firstProcessedId: message.id, lastProcessedId: message.id,
493+
foundOldest: true, foundNewest: true).toJson());
494+
await tester.tap(find.text('Mark Topic As Read'));
495+
await tester.pumpAndSettle();
496+
497+
check(connection.lastRequest).isA<http.Request>()
498+
..url.path.equals('/api/v1/messages/flags/narrow')
499+
..bodyFields['narrow'].equals(jsonEncode([
500+
...TopicNarrow(someChannel.streamId, TopicName(someTopic)).apiEncode(),
501+
{'operator': 'is', 'operand': 'unread'}
502+
]));
503+
});
504+
});
453505
});
454506

455507
group('message action sheet', () {

0 commit comments

Comments
 (0)