Skip to content

Commit 573f4f2

Browse files
Brazolesarbanis
andauthored
feat(ui): mark unread feature - UI (#1881)
* mark message as unread feature * jump to last read message * changelog * error handling * tweak * mark unread feature - ui * doc * localization tests * tweaks * tweaks * localizations tests * tweak * Add test coverage --------- Co-authored-by: Efthymis Sarmpanis <[email protected]>
1 parent 305986c commit 573f4f2

27 files changed

+759
-50
lines changed

docusaurus/docs/Flutter/02-stream_chat_flutter/03-custom_widgets/03-customize_message_actions.mdx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,18 @@ By default we render the following message actions:
2929

3030
* pin message
3131

32+
* mark unread
33+
3234
:::note
3335
Edit and delete message are only available on messages sent by the user.
3436
Additionally, pinning a message requires you to add the roles which are allowed to pin messages.
3537
:::
3638

39+
:::note
40+
Mark unread message is only available on messages sent by other users and only when read events are enabled for the channel.
41+
Additionally, it's not possible to mark messages inside threads as unread.
42+
:::
43+
3744
### Partially remove some message actions
3845

3946
For example, if you only want to keep "copy message" and "delete message":
@@ -49,6 +56,7 @@ StreamMessageListView(
4956
showDeleteMessage: details.isMyMessage,
5057
showReplyMessage: false,
5158
showThreadReplyMessage: false,
59+
showMarkUnreadMessage: false,
5260
);
5361
},
5462
)

packages/stream_chat_flutter/CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,20 @@
1+
## Unreleased
2+
3+
✅ Added
4+
`StreamMessageListView` will now by default show unread indicator floating on top of the message list that will scroll to last read message when tapped and mark channel as unread when dismissed.
5+
6+
- Added `showUnreadIndicator` parameter to `StreamMessageListView` that controls visibility of new channel unread indicator
7+
- Added `unreadIndicatorBuilder` parameter to `StreamMessageListView` that allows to provide custom unread indicator builder
8+
- Added `markReadWhenAtTheBottom` parameter to `StreamMessageListView` that will toggle, previously default, behaviour of marking channel as read when message list is scrolled to the bottom (now default is `false`)
9+
- Added `showUnreadCountOnScrollToBottom` parameter to `StreamMessageListView` that will toggle, previously shown by default, unread messages counter on the scroll to bottom button (no default is `false`)
10+
11+
Added Mark as Unread option to `StreamMessageWidget` context menu that will show for non-thread messages of other users and mark channel as unread from selected message onwards.
12+
13+
- Added `showMarkUnreadMessage` to `StreamMessageWidget` that controls visibility of Mark as Unread option.
14+
15+
🔄 Changed
16+
17+
118
## 7.1.0
219

320
🐞 Fixed

packages/stream_chat_flutter/lib/src/localization/translations.dart

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,15 @@ abstract class Translations {
208208
/// based on [pinned]
209209
String togglePinUnpinText({required bool pinned});
210210

211+
/// The text for marking message as unread functionality in [MessageWidget]
212+
String get markAsUnreadLabel;
213+
214+
/// The text for unread count indicator
215+
String unreadCountIndicatorLabel({required int unreadCount});
216+
217+
/// The text of an error shown when marking a message as unread fails
218+
String get markUnreadError;
219+
211220
/// The text for showing delete/retry-delete based on [isDeleteFailed]
212221
String toggleDeleteRetryDeleteMessageText({required bool isDeleteFailed});
213222

@@ -575,6 +584,14 @@ class DefaultTranslations implements Translations {
575584
return 'Pin to Conversation';
576585
}
577586

587+
@override
588+
String get markAsUnreadLabel => 'Mark as Unread';
589+
590+
@override
591+
String unreadCountIndicatorLabel({required int unreadCount}) {
592+
return '$unreadCount unread';
593+
}
594+
578595
@override
579596
String toggleDeleteRetryDeleteMessageText({required bool isDeleteFailed}) {
580597
if (isDeleteFailed) return 'Retry Deleting Message';
@@ -807,4 +824,9 @@ Attachment limit exceeded: it's not possible to add more than $limit attachments
807824

808825
@override
809826
String get allowFileAccessMessage => 'Allow access to files';
827+
828+
@override
829+
String get markUnreadError =>
830+
'Error marking message unread. Cannot mark unread messages older than the'
831+
' newest 100 channel messages.';
810832
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
3+
4+
/// {@template markUnreadMessageButton}
5+
/// Allows a user to mark message (and all messages onwards) as unread.
6+
///
7+
/// Used by [MessageActionsModal]. Should not be used by itself.
8+
/// {@endtemplate}
9+
class MarkUnreadMessageButton extends StatelessWidget {
10+
/// {@macro markUnreadMessageButton}
11+
const MarkUnreadMessageButton({
12+
super.key,
13+
required this.onTap,
14+
});
15+
16+
/// The callback to perform when the button is tapped.
17+
final VoidCallback onTap;
18+
19+
@override
20+
Widget build(BuildContext context) {
21+
final streamChatThemeData = StreamChatTheme.of(context);
22+
return InkWell(
23+
onTap: onTap,
24+
child: Padding(
25+
padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 16),
26+
child: Row(
27+
children: [
28+
StreamSvgIcon.messageUnread(
29+
color: streamChatThemeData.primaryIconTheme.color,
30+
size: 24,
31+
),
32+
const SizedBox(width: 16),
33+
Text(
34+
context.translations.markAsUnreadLabel,
35+
style: streamChatThemeData.textTheme.body,
36+
),
37+
],
38+
),
39+
),
40+
);
41+
}
42+
}

packages/stream_chat_flutter/lib/src/message_actions_modal/message_actions_modal.dart

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import 'dart:ui';
22

33
import 'package:flutter/material.dart' hide ButtonStyle;
44
import 'package:stream_chat_flutter/src/message_actions_modal/mam_widgets.dart';
5+
import 'package:stream_chat_flutter/src/message_actions_modal/mark_unread_message_button.dart';
56
import 'package:stream_chat_flutter/src/message_widget/reactions/reactions_align.dart';
67
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
78

@@ -25,6 +26,7 @@ class MessageActionsModal extends StatefulWidget {
2526
this.showReplyMessage = true,
2627
this.showResendMessage = true,
2728
this.showThreadReplyMessage = true,
29+
this.showMarkUnreadMessage = true,
2830
this.showFlagButton = true,
2931
this.showPinButton = true,
3032
this.editMessageInputBuilder,
@@ -72,6 +74,9 @@ class MessageActionsModal extends StatefulWidget {
7274
/// Flag for showing resend action
7375
final bool showResendMessage;
7476

77+
/// Flag for showing mark unread action
78+
final bool showMarkUnreadMessage;
79+
7580
/// Flag for showing reply action
7681
final bool showReplyMessage;
7782

@@ -178,6 +183,22 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
178183
message: widget.message,
179184
onThreadReplyTap: widget.onThreadReplyTap,
180185
),
186+
if (widget.showMarkUnreadMessage)
187+
MarkUnreadMessageButton(onTap: () async {
188+
try {
189+
await channel.markUnread(widget.message.id);
190+
} catch (ex) {
191+
ScaffoldMessenger.of(context).showSnackBar(
192+
SnackBar(
193+
content: Text(
194+
context.translations.markUnreadError,
195+
),
196+
),
197+
);
198+
}
199+
200+
Navigator.of(context).pop();
201+
}),
181202
if (widget.showResendMessage)
182203
ResendMessageButton(
183204
message: widget.message,

0 commit comments

Comments
 (0)