Skip to content

Commit 22cdbf0

Browse files
committed
action_sheet: Add "Mark as unread from here" button
Fixes: zulip#131
1 parent c0644a3 commit 22cdbf0

File tree

4 files changed

+80
-0
lines changed

4 files changed

+80
-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: 26 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';
@@ -36,6 +37,8 @@ void showMessageActionSheet({required BuildContext context, required Message mes
3637
&& reactionWithVotes.userIds.contains(store.selfUserId))
3738
?? false;
3839

40+
final isMessageRead = message.flags.contains(MessageFlag.read);
41+
3942
showDraggableScrollableModalBottomSheet<void>(
4043
context: context,
4144
builder: (BuildContext _) {
@@ -46,6 +49,7 @@ void showMessageActionSheet({required BuildContext context, required Message mes
4649
message: message,
4750
messageListContext: context,
4851
),
52+
if (isMessageRead) MarkAsUnreadButton(message: message, messageListContext: context),
4953
CopyMessageTextButton(message: message, messageListContext: context),
5054
CopyMessageLinkButton(message: message, messageListContext: context),
5155
ShareButton(message: message, messageListContext: context),
@@ -402,3 +406,25 @@ class ShareButton extends MessageActionSheetMenuItemButton {
402406
}
403407
}
404408
}
409+
410+
class MarkAsUnreadButton extends MessageActionSheetMenuItemButton {
411+
MarkAsUnreadButton({
412+
super.key,
413+
required super.message,
414+
required super.messageListContext,
415+
}) : narrow = MessageListPage.ancestorOf(messageListContext).narrow;
416+
417+
final Narrow narrow;
418+
419+
@override IconData get icon => Icons.mark_chat_unread_outlined;
420+
421+
@override
422+
String label(ZulipLocalizations zulipLocalizations) {
423+
return zulipLocalizations.actionSheetMarkAsUnread;
424+
}
425+
426+
@override void onPressed(BuildContext context) async {
427+
Navigator.of(context).pop();
428+
await markNarrowAsUnreadFromMessage(messageListContext, message, narrow);
429+
}
430+
}

lib/widgets/actions.dart

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,25 @@ Future<bool> markNarrowAsRead(
5353
onProgressTitle: zulipLocalizations.markAsReadInProgress);
5454
}
5555

56+
Future<bool> markNarrowAsUnreadFromMessage(
57+
BuildContext context,
58+
Message message,
59+
Narrow narrow,
60+
) async {
61+
final zulipLocalizations = ZulipLocalizations.of(context);
62+
63+
return updateMessageFlagsStartingFromAnchor(
64+
context: context,
65+
startAnchor: NumericAnchor(message.id),
66+
apiNarrow: narrow.apiEncode(),
67+
includeAnchor: true,
68+
op: UpdateMessageFlagsOp.remove,
69+
flag: MessageFlag.read,
70+
onCompletedMessage: zulipLocalizations.markAsUnreadComplete,
71+
onFailedTitle: zulipLocalizations.errorMarkAsUnreadFailedTitle,
72+
onProgressTitle: zulipLocalizations.markAsUnreadInProgress);
73+
}
74+
5675
Future<bool> updateMessageFlagsStartingFromAnchor({
5776
required BuildContext context,
5877
required Anchor startAnchor,

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)