Skip to content

Commit f791827

Browse files
committed
subscription_list: Show @-mention marker when that applies
Fixes: zulip#747
1 parent de53b7c commit f791827

File tree

4 files changed

+133
-3
lines changed

4 files changed

+133
-3
lines changed

lib/model/unreads.dart

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,28 @@ class Unreads extends ChangeNotifier {
223223
}
224224
}
225225

226+
Set<int> get channelsWithUnreadMentions {
227+
final channels = <int>{};
228+
for (var messageId in mentions) {
229+
final streamId = _reverseStreamsLookup[messageId]?.streamId;
230+
if (streamId != null) {
231+
channels.add(streamId);
232+
}
233+
}
234+
return channels;
235+
}
236+
237+
Set<int> get channelsWithUnmutedMentions {
238+
final channels = <int>{};
239+
for (var messageId in mentions) {
240+
final info = _reverseStreamsLookup[messageId]!;
241+
if (channelStore.isTopicVisible(info.streamId, info.topic)) {
242+
channels.add(info.streamId);
243+
}
244+
}
245+
return channels;
246+
}
247+
226248
void handleMessageEvent(MessageEvent event) {
227249
final message = event.message;
228250
if (message.flags.contains(MessageFlag.read)) {

lib/widgets/subscription_list.dart

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -185,16 +185,24 @@ class _SubscriptionList extends StatelessWidget {
185185

186186
@override
187187
Widget build(BuildContext context) {
188+
final channelsWithMentions = unreadsModel!.channelsWithUnreadMentions;
189+
final channelsWithUnmutedMentions = unreadsModel!.channelsWithUnmutedMentions;
188190
return SliverList.builder(
189191
itemCount: subscriptions.length,
190192
itemBuilder: (BuildContext context, int index) {
191193
final subscription = subscriptions[index];
192194
final unreadCount = unreadsModel!.countInChannel(subscription.streamId);
193195
final showMutedUnreadBadge = unreadCount == 0
194196
&& unreadsModel!.countInChannelNarrow(subscription.streamId) > 0;
197+
final hasMentions = channelsWithMentions.contains(subscription.streamId);
198+
final hasOnlyMutedMentions = !subscription.isMuted
199+
&& channelsWithMentions.contains(subscription.streamId)
200+
&& !channelsWithUnmutedMentions.contains(subscription.streamId);
195201
return SubscriptionItem(subscription: subscription,
196202
unreadCount: unreadCount,
197-
showMutedUnreadBadge: showMutedUnreadBadge);
203+
showMutedUnreadBadge: showMutedUnreadBadge,
204+
hasMentions: hasMentions,
205+
hasOnlyMutedMentions: hasOnlyMutedMentions);
198206
});
199207
}
200208
}
@@ -206,11 +214,15 @@ class SubscriptionItem extends StatelessWidget {
206214
required this.subscription,
207215
required this.unreadCount,
208216
required this.showMutedUnreadBadge,
217+
required this.hasMentions,
218+
required this.hasOnlyMutedMentions,
209219
});
210220

211221
final Subscription subscription;
212222
final int unreadCount;
213223
final bool showMutedUnreadBadge;
224+
final bool hasMentions;
225+
final bool hasOnlyMutedMentions;
214226

215227
@override
216228
Widget build(BuildContext context) {
@@ -258,7 +270,7 @@ class SubscriptionItem extends StatelessWidget {
258270
subscription.name)))),
259271
if (hasUnreads) ...[
260272
const SizedBox(width: 12),
261-
// TODO(#747) show @-mention indicator when it applies
273+
AtMentionMarker(muted: !subscription.isMuted && hasOnlyMutedMentions),
262274
Opacity(
263275
opacity: opacity,
264276
child: UnreadCountBadge(
@@ -267,7 +279,7 @@ class SubscriptionItem extends StatelessWidget {
267279
bold: true)),
268280
] else if (showMutedUnreadBadge) ...[
269281
const SizedBox(width: 12),
270-
// TODO(#747) show @-mention indicator when it applies
282+
if (hasMentions) const AtMentionMarker(muted: true),
271283
const MutedUnreadBadge(),
272284
],
273285
const SizedBox(width: 16),

test/model/unreads_test.dart

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,48 @@ void main() {
326326
});
327327
}
328328
});
329+
330+
test('channelsWithUnreadMentions', () {
331+
final stream1 = eg.stream();
332+
final stream2 = eg.stream();
333+
334+
prepare();
335+
fillWithMessages([
336+
eg.streamMessage(stream: stream1, flags: [MessageFlag.mentioned]),
337+
eg.streamMessage(stream: stream1, flags: []),
338+
eg.streamMessage(stream: stream2, flags: []),
339+
]);
340+
341+
check(model.channelsWithUnreadMentions.single).equals(stream1.streamId);
342+
});
343+
344+
test('channelsWithUnmutedMentions', () async {
345+
final stream1 = eg.stream();
346+
final stream2 = eg.stream();
347+
final stream3 = eg.stream();
348+
final streams = [stream1, stream2, stream3];
349+
350+
prepare();
351+
352+
await channelStore.addStreams(streams);
353+
await channelStore.addSubscriptions([
354+
eg.subscription(stream1),
355+
eg.subscription(stream2),
356+
eg.subscription(stream3, isMuted: true)]);
357+
358+
await channelStore.addUserTopic(stream1, 'a normal', UserTopicVisibilityPolicy.none);
359+
await channelStore.addUserTopic(stream2, 'b muted', UserTopicVisibilityPolicy.muted);
360+
await channelStore.addUserTopic(stream3, 'c normal', UserTopicVisibilityPolicy.none);
361+
362+
363+
fillWithMessages([
364+
eg.streamMessage(stream: stream1, flags: [MessageFlag.mentioned], topic: 'a normal'),
365+
eg.streamMessage(stream: stream2, flags: [MessageFlag.mentioned], topic: 'b muted'),
366+
eg.streamMessage(stream: stream3, flags: [MessageFlag.mentioned], topic: 'c normal'),
367+
]);
368+
369+
check(model.channelsWithUnmutedMentions.single).equals(stream1.streamId);
370+
});
329371
});
330372

331373
group('DM messages', () {

test/widgets/subscription_list_test.dart

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,4 +316,58 @@ void main() {
316316
checkStreamNameWght(mutedStreamWithUnmutedUnreads.name, 400);
317317
checkStreamNameWght(mutedStreamWithNoUnmutedUnreads.name, 400);
318318
});
319+
320+
group('@-mention marker', () {
321+
Iterable<AtMentionMarker> getAtMentionMarkers(WidgetTester tester) {
322+
return tester.widgetList<AtMentionMarker>(find.byType(AtMentionMarker));
323+
}
324+
325+
testWidgets('is shown when subscription has unread mentions', (tester) async {
326+
final streamWithMentions = eg.stream();
327+
final streamWithNoMentions = eg.stream();
328+
329+
await setupStreamListPage(tester,
330+
subscriptions: [
331+
eg.subscription(streamWithMentions),
332+
eg.subscription(streamWithNoMentions),
333+
],
334+
userTopics: [
335+
eg.userTopicItem(streamWithMentions, 'a', UserTopicVisibilityPolicy.none),
336+
eg.userTopicItem(streamWithNoMentions, 'b', UserTopicVisibilityPolicy.muted),
337+
],
338+
unreadMsgs: eg.unreadMsgs(
339+
mentions: [1],
340+
channels: [
341+
UnreadChannelSnapshot(streamId: streamWithMentions.streamId, topic: 'a', unreadMessageIds: [1]),
342+
UnreadChannelSnapshot(streamId: streamWithNoMentions.streamId, topic: 'b', unreadMessageIds: [2]),
343+
]),
344+
);
345+
346+
check(getAtMentionMarkers(tester)).single;
347+
});
348+
349+
testWidgets('is muted when subscription has only muted mentions', (tester) async {
350+
final streamWithMentions = eg.stream();
351+
final streamWithOnlyMutedMentions = eg.stream();
352+
353+
await setupStreamListPage(tester,
354+
subscriptions: [
355+
eg.subscription(streamWithMentions),
356+
eg.subscription(streamWithOnlyMutedMentions, isMuted: true),
357+
],
358+
userTopics: [
359+
eg.userTopicItem(streamWithMentions, 'a', UserTopicVisibilityPolicy.none),
360+
eg.userTopicItem(streamWithOnlyMutedMentions, 'b', UserTopicVisibilityPolicy.none),
361+
],
362+
unreadMsgs: eg.unreadMsgs(
363+
mentions: [1, 2],
364+
channels: [
365+
UnreadChannelSnapshot(streamId: streamWithMentions.streamId, topic: 'a', unreadMessageIds: [1]),
366+
UnreadChannelSnapshot(streamId: streamWithOnlyMutedMentions.streamId, topic: 'b', unreadMessageIds: [2]),
367+
]),
368+
);
369+
370+
check(getAtMentionMarkers(tester).map((e) => e.muted)).deepEquals([false, true]);
371+
});
372+
});
319373
}

0 commit comments

Comments
 (0)