From 90e493a665d40562c8b42fb2e840a8b075eee4cd Mon Sep 17 00:00:00 2001 From: Khader M Khudair Date: Wed, 14 Aug 2024 16:18:36 +0300 Subject: [PATCH] subscription_list: Show @-mention marker when that applies Fixes: #747 --- lib/model/unreads.dart | 22 ++++++++++ lib/widgets/subscription_list.dart | 18 ++++++-- test/model/unreads_test.dart | 42 ++++++++++++++++++ test/widgets/subscription_list_test.dart | 54 ++++++++++++++++++++++++ 4 files changed, 133 insertions(+), 3 deletions(-) diff --git a/lib/model/unreads.dart b/lib/model/unreads.dart index b6da57b58b..5f270c7480 100644 --- a/lib/model/unreads.dart +++ b/lib/model/unreads.dart @@ -223,6 +223,28 @@ class Unreads extends ChangeNotifier { } } + Set get channelsWithUnreadMentions { + final channels = {}; + for (var messageId in mentions) { + final streamId = _reverseStreamsLookup[messageId]?.streamId; + if (streamId != null) { + channels.add(streamId); + } + } + return channels; + } + + Set get channelsWithUnmutedMentions { + final channels = {}; + for (var messageId in mentions) { + final info = _reverseStreamsLookup[messageId]!; + if (channelStore.isTopicVisible(info.streamId, info.topic)) { + channels.add(info.streamId); + } + } + return channels; + } + void handleMessageEvent(MessageEvent event) { final message = event.message; if (message.flags.contains(MessageFlag.read)) { diff --git a/lib/widgets/subscription_list.dart b/lib/widgets/subscription_list.dart index 86a9caf4e4..b8ffaf6c86 100644 --- a/lib/widgets/subscription_list.dart +++ b/lib/widgets/subscription_list.dart @@ -185,6 +185,8 @@ class _SubscriptionList extends StatelessWidget { @override Widget build(BuildContext context) { + final channelsWithMentions = unreadsModel!.channelsWithUnreadMentions; + final channelsWithUnmutedMentions = unreadsModel!.channelsWithUnmutedMentions; return SliverList.builder( itemCount: subscriptions.length, itemBuilder: (BuildContext context, int index) { @@ -192,9 +194,15 @@ class _SubscriptionList extends StatelessWidget { final unreadCount = unreadsModel!.countInChannel(subscription.streamId); final showMutedUnreadBadge = unreadCount == 0 && unreadsModel!.countInChannelNarrow(subscription.streamId) > 0; + final hasMentions = channelsWithMentions.contains(subscription.streamId); + final hasOnlyMutedMentions = !subscription.isMuted + && channelsWithMentions.contains(subscription.streamId) + && !channelsWithUnmutedMentions.contains(subscription.streamId); return SubscriptionItem(subscription: subscription, unreadCount: unreadCount, - showMutedUnreadBadge: showMutedUnreadBadge); + showMutedUnreadBadge: showMutedUnreadBadge, + hasMentions: hasMentions, + hasOnlyMutedMentions: hasOnlyMutedMentions); }); } } @@ -206,11 +214,15 @@ class SubscriptionItem extends StatelessWidget { required this.subscription, required this.unreadCount, required this.showMutedUnreadBadge, + required this.hasMentions, + required this.hasOnlyMutedMentions, }); final Subscription subscription; final int unreadCount; final bool showMutedUnreadBadge; + final bool hasMentions; + final bool hasOnlyMutedMentions; @override Widget build(BuildContext context) { @@ -258,7 +270,7 @@ class SubscriptionItem extends StatelessWidget { subscription.name)))), if (hasUnreads) ...[ const SizedBox(width: 12), - // TODO(#747) show @-mention indicator when it applies + AtMentionMarker(muted: !subscription.isMuted && hasOnlyMutedMentions), Opacity( opacity: opacity, child: UnreadCountBadge( @@ -267,7 +279,7 @@ class SubscriptionItem extends StatelessWidget { bold: true)), ] else if (showMutedUnreadBadge) ...[ const SizedBox(width: 12), - // TODO(#747) show @-mention indicator when it applies + if (hasMentions) const AtMentionMarker(muted: true), const MutedUnreadBadge(), ], const SizedBox(width: 16), diff --git a/test/model/unreads_test.dart b/test/model/unreads_test.dart index d46aa8138b..69afc2588f 100644 --- a/test/model/unreads_test.dart +++ b/test/model/unreads_test.dart @@ -326,6 +326,48 @@ void main() { }); } }); + + test('channelsWithUnreadMentions', () { + final stream1 = eg.stream(); + final stream2 = eg.stream(); + + prepare(); + fillWithMessages([ + eg.streamMessage(stream: stream1, flags: [MessageFlag.mentioned]), + eg.streamMessage(stream: stream1, flags: []), + eg.streamMessage(stream: stream2, flags: []), + ]); + + check(model.channelsWithUnreadMentions.single).equals(stream1.streamId); + }); + + test('channelsWithUnmutedMentions', () async { + final stream1 = eg.stream(); + final stream2 = eg.stream(); + final stream3 = eg.stream(); + final streams = [stream1, stream2, stream3]; + + prepare(); + + await channelStore.addStreams(streams); + await channelStore.addSubscriptions([ + eg.subscription(stream1), + eg.subscription(stream2), + eg.subscription(stream3, isMuted: true)]); + + await channelStore.addUserTopic(stream1, 'a normal', UserTopicVisibilityPolicy.none); + await channelStore.addUserTopic(stream2, 'b muted', UserTopicVisibilityPolicy.muted); + await channelStore.addUserTopic(stream3, 'c normal', UserTopicVisibilityPolicy.none); + + + fillWithMessages([ + eg.streamMessage(stream: stream1, flags: [MessageFlag.mentioned], topic: 'a normal'), + eg.streamMessage(stream: stream2, flags: [MessageFlag.mentioned], topic: 'b muted'), + eg.streamMessage(stream: stream3, flags: [MessageFlag.mentioned], topic: 'c normal'), + ]); + + check(model.channelsWithUnmutedMentions.single).equals(stream1.streamId); + }); }); group('DM messages', () { diff --git a/test/widgets/subscription_list_test.dart b/test/widgets/subscription_list_test.dart index ac327a2894..6459753129 100644 --- a/test/widgets/subscription_list_test.dart +++ b/test/widgets/subscription_list_test.dart @@ -316,4 +316,58 @@ void main() { checkStreamNameWght(mutedStreamWithUnmutedUnreads.name, 400); checkStreamNameWght(mutedStreamWithNoUnmutedUnreads.name, 400); }); + + group('@-mention marker', () { + Iterable getAtMentionMarkers(WidgetTester tester) { + return tester.widgetList(find.byType(AtMentionMarker)); + } + + testWidgets('is shown when subscription has unread mentions', (tester) async { + final streamWithMentions = eg.stream(); + final streamWithNoMentions = eg.stream(); + + await setupStreamListPage(tester, + subscriptions: [ + eg.subscription(streamWithMentions), + eg.subscription(streamWithNoMentions), + ], + userTopics: [ + eg.userTopicItem(streamWithMentions, 'a', UserTopicVisibilityPolicy.none), + eg.userTopicItem(streamWithNoMentions, 'b', UserTopicVisibilityPolicy.muted), + ], + unreadMsgs: eg.unreadMsgs( + mentions: [1], + channels: [ + UnreadChannelSnapshot(streamId: streamWithMentions.streamId, topic: 'a', unreadMessageIds: [1]), + UnreadChannelSnapshot(streamId: streamWithNoMentions.streamId, topic: 'b', unreadMessageIds: [2]), + ]), + ); + + check(getAtMentionMarkers(tester)).single; + }); + + testWidgets('is muted when subscription has only muted mentions', (tester) async { + final streamWithMentions = eg.stream(); + final streamWithOnlyMutedMentions = eg.stream(); + + await setupStreamListPage(tester, + subscriptions: [ + eg.subscription(streamWithMentions), + eg.subscription(streamWithOnlyMutedMentions, isMuted: true), + ], + userTopics: [ + eg.userTopicItem(streamWithMentions, 'a', UserTopicVisibilityPolicy.none), + eg.userTopicItem(streamWithOnlyMutedMentions, 'b', UserTopicVisibilityPolicy.none), + ], + unreadMsgs: eg.unreadMsgs( + mentions: [1, 2], + channels: [ + UnreadChannelSnapshot(streamId: streamWithMentions.streamId, topic: 'a', unreadMessageIds: [1]), + UnreadChannelSnapshot(streamId: streamWithOnlyMutedMentions.streamId, topic: 'b', unreadMessageIds: [2]), + ]), + ); + + check(getAtMentionMarkers(tester).map((e) => e.muted)).deepEquals([false, true]); + }); + }); }