Skip to content

Commit 18bec31

Browse files
committed
subscription_list: Show @-mention marker when that applies
Fixes: zulip#747
1 parent 854b59e commit 18bec31

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
@@ -217,6 +217,28 @@ class Unreads extends ChangeNotifier {
217217
}
218218
}
219219

220+
Set<int> get channelsWithUnreadMentions {
221+
final channels = <int>{};
222+
for (var messageId in mentions) {
223+
final streamId = _reverseStreamsLookup[messageId]?.streamId;
224+
if (streamId != null) {
225+
channels.add(streamId);
226+
}
227+
}
228+
return channels;
229+
}
230+
231+
Set<int> get channelsWithUnmutedMentions {
232+
final channels = <int>{};
233+
for (var messageId in mentions) {
234+
final info = _reverseStreamsLookup[messageId]!;
235+
if (channelStore.isTopicVisible(info.streamId, info.topic)) {
236+
channels.add(info.streamId);
237+
}
238+
}
239+
return channels;
240+
}
241+
220242
void handleMessageEvent(MessageEvent event) {
221243
final message = event.message;
222244
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
@@ -298,6 +298,48 @@ void main() {
298298
});
299299
}
300300
});
301+
302+
test('channelsWithUnreadMentions', () {
303+
final stream1 = eg.stream();
304+
final stream2 = eg.stream();
305+
306+
prepare();
307+
fillWithMessages([
308+
eg.streamMessage(stream: stream1, flags: [MessageFlag.mentioned]),
309+
eg.streamMessage(stream: stream1, flags: []),
310+
eg.streamMessage(stream: stream2, flags: []),
311+
]);
312+
313+
check(model.channelsWithUnreadMentions.single).equals(stream1.streamId);
314+
});
315+
316+
test('channelsWithUnmutedMentions', () async {
317+
final stream1 = eg.stream();
318+
final stream2 = eg.stream();
319+
final stream3 = eg.stream();
320+
final streams = [stream1, stream2, stream3];
321+
322+
prepare();
323+
324+
await channelStore.addStreams(streams);
325+
await channelStore.addSubscriptions([
326+
eg.subscription(stream1),
327+
eg.subscription(stream2),
328+
eg.subscription(stream3, isMuted: true)]);
329+
330+
await channelStore.addUserTopic(stream1, 'a normal', UserTopicVisibilityPolicy.none);
331+
await channelStore.addUserTopic(stream2, 'b muted', UserTopicVisibilityPolicy.muted);
332+
await channelStore.addUserTopic(stream3, 'c normal', UserTopicVisibilityPolicy.none);
333+
334+
335+
fillWithMessages([
336+
eg.streamMessage(stream: stream1, flags: [MessageFlag.mentioned], topic: 'a normal'),
337+
eg.streamMessage(stream: stream2, flags: [MessageFlag.mentioned], topic: 'b muted'),
338+
eg.streamMessage(stream: stream3, flags: [MessageFlag.mentioned], topic: 'c normal'),
339+
]);
340+
341+
check(model.channelsWithUnmutedMentions.single).equals(stream1.streamId);
342+
});
301343
});
302344

303345
group('DM messages', () {

test/widgets/subscription_list_test.dart

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

0 commit comments

Comments
 (0)