Skip to content

Commit b6724c7

Browse files
committed
action_sheet: Add "Mark as unread from here" button
Fixes: zulip#131
1 parent 9d25211 commit b6724c7

File tree

4 files changed

+85
-0
lines changed

4 files changed

+85
-0
lines changed

assets/l10n/app_en.arb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@
5151
"@actionSheetOptionCopyMessageLink": {
5252
"description": "Label for copy message link button on action sheet."
5353
},
54+
"actionSheetMarkAsUnread": "Mark as unread from here",
55+
"@actionSheetMarkAsUnread": {
56+
"description": "Label for mark as unread button on action sheet."
57+
},
5458
"actionSheetOptionShare": "Share",
5559
"@actionSheetOptionShare": {
5660
"description": "Label for share button on action sheet."
@@ -432,6 +436,21 @@
432436
"@errorMarkAsReadFailedTitle": {
433437
"description": "Error title when mark as read action failed."
434438
},
439+
"markAsUnreadComplete": "Marked {num, plural, =1{1 message} other{{num} messages}} as unread.",
440+
"@markAsUnreadComplete": {
441+
"description": "Message when marking messages as unread has completed.",
442+
"placeholders": {
443+
"num": {"type": "int", "example": "4"}
444+
}
445+
},
446+
"markAsUnreadInProgress": "Marking messages as unread...",
447+
"@markAsUnreadInProgress": {
448+
"description": "Progress message when marking messages as unread."
449+
},
450+
"errorMarkAsUnreadFailedTitle": "Mark as unread failed",
451+
"@errorMarkAsUnreadFailedTitle": {
452+
"description": "Error title when mark as unread action failed."
453+
},
435454
"today": "Today",
436455
"@today": {
437456
"description": "Term to use to reference the current day."

lib/widgets/action_sheet.dart

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import '../api/model/model.dart';
88
import '../api/route/messages.dart';
99
import '../model/internal_link.dart';
1010
import '../model/narrow.dart';
11+
import 'actions.dart';
1112
import 'clipboard.dart';
1213
import 'compose_box.dart';
1314
import 'dialog.dart';
@@ -28,6 +29,8 @@ void showMessageActionSheet({required BuildContext context, required Message mes
2829
// any message list, so that's fine.
2930
final messageListPage = MessageListPage.ancestorOf(context);
3031
final isComposeBoxOffered = messageListPage.composeBoxController != null;
32+
final narrow = messageListPage.narrow;
33+
final isMessageRead = message.flags.contains(MessageFlag.read);
3134

3235
final hasThumbsUpReactionVote = message.reactions
3336
?.aggregated.any((reactionWithVotes) =>
@@ -46,6 +49,11 @@ void showMessageActionSheet({required BuildContext context, required Message mes
4649
message: message,
4750
messageListContext: context,
4851
),
52+
if (isMessageRead) MarkAsUnreadButton(
53+
message: message,
54+
messageListContext: context,
55+
narrow: narrow,
56+
),
4957
CopyMessageTextButton(message: message, messageListContext: context),
5058
CopyMessageLinkButton(message: message, messageListContext: context),
5159
ShareButton(message: message, messageListContext: context),
@@ -402,3 +410,27 @@ class ShareButton extends MessageActionSheetMenuItemButton {
402410
}
403411
}
404412
}
413+
414+
class MarkAsUnreadButton extends MessageActionSheetMenuItemButton {
415+
MarkAsUnreadButton({
416+
super.key,
417+
required super.message,
418+
required super.messageListContext,
419+
required this.narrow,
420+
});
421+
422+
final Narrow narrow;
423+
424+
@override IconData get icon => Icons.mark_chat_unread_outlined;
425+
426+
@override
427+
String label(ZulipLocalizations zulipLocalizations) {
428+
return zulipLocalizations.actionSheetMarkAsUnread;
429+
}
430+
431+
@override void onPressed(BuildContext context) async {
432+
if (!context.mounted) return;
433+
Navigator.of(context).pop();
434+
await markNarrowAsUnreadFromMessage(messageListContext, message, narrow);
435+
}
436+
}

lib/widgets/actions.dart

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,24 @@ Future<void> markNarrowAsRead(BuildContext context, Narrow narrow) async {
6767
}
6868
}
6969

70+
Future<void> markNarrowAsUnreadFromMessage(
71+
BuildContext context,
72+
Message message,
73+
Narrow narrow,
74+
) async {
75+
final zulipLocalizations = ZulipLocalizations.of(context);
76+
await updateMessageFlagsStartingFromAnchor(
77+
context: context,
78+
startingAnchor: NumericAnchor(message.id),
79+
apiNarrow: narrow.apiEncode(),
80+
includeAnchor: true,
81+
op: UpdateMessageFlagsOp.remove,
82+
flag: MessageFlag.read,
83+
onCompletedMessage: zulipLocalizations.markAsUnreadComplete,
84+
onFailedTitle: zulipLocalizations.errorMarkAsUnreadFailedTitle,
85+
progressMessage: zulipLocalizations.markAsUnreadInProgress);
86+
}
87+
7088
/// Updates message flags by applying given operation `op` using given `flag`
7189
/// the update happens on given `apiNarrow` starting from given `startingAnchor`
7290
///

test/widgets/action_sheet_test.dart

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,4 +545,20 @@ void main() {
545545
check(mockSharePlus.sharedString).isNull();
546546
});
547547
});
548+
549+
group('MarkAsUnread', () {
550+
testWidgets('not visible if message is not read', (WidgetTester tester) async {
551+
final unreadMessage = eg.streamMessage(flags: []);
552+
await setupToMessageActionSheet(tester, message: unreadMessage, narrow: TopicNarrow.ofMessage(unreadMessage));
553+
554+
check(find.byIcon(Icons.mark_chat_unread_outlined).evaluate().length).equals(0);
555+
});
556+
557+
testWidgets('visible if message is read', (WidgetTester tester) async {
558+
final message = eg.streamMessage(flags: [MessageFlag.read]);
559+
await setupToMessageActionSheet(tester, message: message, narrow: TopicNarrow.ofMessage(message));
560+
561+
check(find.byIcon(Icons.mark_chat_unread_outlined).evaluate().length).equals(1);
562+
});
563+
});
548564
}

0 commit comments

Comments
 (0)