Skip to content

Commit

Permalink
feat(ui): mark unread feature - UI (#1881)
Browse files Browse the repository at this point in the history
* 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 <e.sarbanis@gmail.com>
  • Loading branch information
Brazol and esarbanis committed Apr 2, 2024
1 parent 305986c commit 573f4f2
Show file tree
Hide file tree
Showing 27 changed files with 759 additions and 50 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,18 @@ By default we render the following message actions:

* pin message

* mark unread

:::note
Edit and delete message are only available on messages sent by the user.
Additionally, pinning a message requires you to add the roles which are allowed to pin messages.
:::

:::note
Mark unread message is only available on messages sent by other users and only when read events are enabled for the channel.
Additionally, it's not possible to mark messages inside threads as unread.
:::

### Partially remove some message actions

For example, if you only want to keep "copy message" and "delete message":
Expand All @@ -49,6 +56,7 @@ StreamMessageListView(
showDeleteMessage: details.isMyMessage,
showReplyMessage: false,
showThreadReplyMessage: false,
showMarkUnreadMessage: false,
);
},
)
Expand Down
17 changes: 17 additions & 0 deletions packages/stream_chat_flutter/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
## Unreleased

✅ Added
`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.

- Added `showUnreadIndicator` parameter to `StreamMessageListView` that controls visibility of new channel unread indicator
- Added `unreadIndicatorBuilder` parameter to `StreamMessageListView` that allows to provide custom unread indicator builder
- 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`)
- 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`)

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.

- Added `showMarkUnreadMessage` to `StreamMessageWidget` that controls visibility of Mark as Unread option.

🔄 Changed


## 7.1.0

🐞 Fixed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,15 @@ abstract class Translations {
/// based on [pinned]
String togglePinUnpinText({required bool pinned});

/// The text for marking message as unread functionality in [MessageWidget]
String get markAsUnreadLabel;

/// The text for unread count indicator
String unreadCountIndicatorLabel({required int unreadCount});

/// The text of an error shown when marking a message as unread fails
String get markUnreadError;

/// The text for showing delete/retry-delete based on [isDeleteFailed]
String toggleDeleteRetryDeleteMessageText({required bool isDeleteFailed});

Expand Down Expand Up @@ -575,6 +584,14 @@ class DefaultTranslations implements Translations {
return 'Pin to Conversation';
}

@override
String get markAsUnreadLabel => 'Mark as Unread';

@override
String unreadCountIndicatorLabel({required int unreadCount}) {
return '$unreadCount unread';
}

@override
String toggleDeleteRetryDeleteMessageText({required bool isDeleteFailed}) {
if (isDeleteFailed) return 'Retry Deleting Message';
Expand Down Expand Up @@ -807,4 +824,9 @@ Attachment limit exceeded: it's not possible to add more than $limit attachments

@override
String get allowFileAccessMessage => 'Allow access to files';

@override
String get markUnreadError =>
'Error marking message unread. Cannot mark unread messages older than the'
' newest 100 channel messages.';
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import 'package:flutter/material.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';

/// {@template markUnreadMessageButton}
/// Allows a user to mark message (and all messages onwards) as unread.
///
/// Used by [MessageActionsModal]. Should not be used by itself.
/// {@endtemplate}
class MarkUnreadMessageButton extends StatelessWidget {
/// {@macro markUnreadMessageButton}
const MarkUnreadMessageButton({
super.key,
required this.onTap,
});

/// The callback to perform when the button is tapped.
final VoidCallback onTap;

@override
Widget build(BuildContext context) {
final streamChatThemeData = StreamChatTheme.of(context);
return InkWell(
onTap: onTap,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 11, horizontal: 16),
child: Row(
children: [
StreamSvgIcon.messageUnread(
color: streamChatThemeData.primaryIconTheme.color,
size: 24,
),
const SizedBox(width: 16),
Text(
context.translations.markAsUnreadLabel,
style: streamChatThemeData.textTheme.body,
),
],
),
),
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'dart:ui';

import 'package:flutter/material.dart' hide ButtonStyle;
import 'package:stream_chat_flutter/src/message_actions_modal/mam_widgets.dart';
import 'package:stream_chat_flutter/src/message_actions_modal/mark_unread_message_button.dart';
import 'package:stream_chat_flutter/src/message_widget/reactions/reactions_align.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';

Expand All @@ -25,6 +26,7 @@ class MessageActionsModal extends StatefulWidget {
this.showReplyMessage = true,
this.showResendMessage = true,
this.showThreadReplyMessage = true,
this.showMarkUnreadMessage = true,
this.showFlagButton = true,
this.showPinButton = true,
this.editMessageInputBuilder,
Expand Down Expand Up @@ -72,6 +74,9 @@ class MessageActionsModal extends StatefulWidget {
/// Flag for showing resend action
final bool showResendMessage;

/// Flag for showing mark unread action
final bool showMarkUnreadMessage;

/// Flag for showing reply action
final bool showReplyMessage;

Expand Down Expand Up @@ -178,6 +183,22 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
message: widget.message,
onThreadReplyTap: widget.onThreadReplyTap,
),
if (widget.showMarkUnreadMessage)
MarkUnreadMessageButton(onTap: () async {
try {
await channel.markUnread(widget.message.id);
} catch (ex) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
context.translations.markUnreadError,
),
),
);
}

Navigator.of(context).pop();
}),
if (widget.showResendMessage)
ResendMessageButton(
message: widget.message,
Expand Down

0 comments on commit 573f4f2

Please sign in to comment.