Skip to content

Commit 0a02709

Browse files
committed
subscription_list: Show a dot for unreads if channel is muted
Fixes: #712
1 parent 53853ab commit 0a02709

File tree

5 files changed

+111
-2
lines changed

5 files changed

+111
-2
lines changed

lib/model/unreads.dart

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,19 @@ class Unreads extends ChangeNotifier {
206206
}
207207
}
208208

209+
/// Checks if stream contains strictly muted unreads,
210+
/// using [StreamStore.isTopicVisible].
211+
bool hasMutedInStream(int streamId) {
212+
final topics = streams[streamId];
213+
if (topics == null) return false;
214+
for (final entry in topics.entries) {
215+
if (!streamStore.isTopicVisible(streamId, entry.key)) {
216+
if (entry.value.isNotEmpty) return true;
217+
}
218+
}
219+
return false;
220+
}
221+
209222
void handleMessageEvent(MessageEvent event) {
210223
final message = event.message;
211224
if (message.flags.contains(MessageFlag.read)) {

lib/widgets/subscription_list.dart

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,10 @@ class _SubscriptionList extends StatelessWidget {
180180
itemBuilder: (BuildContext context, int index) {
181181
final subscription = subscriptions[index];
182182
final unreadCount = unreadsModel!.countInStream(subscription.streamId);
183-
// TODO(#346): if stream muted, show a dot for unreads
184-
return SubscriptionItem(subscription: subscription, unreadCount: unreadCount);
183+
final hasMutedUnreads = unreadCount == 0 && unreadsModel!.hasMutedInStream(subscription.streamId);
184+
return SubscriptionItem(subscription: subscription,
185+
unreadCount: unreadCount,
186+
hasMutedUnreads: hasMutedUnreads);
185187
});
186188
}
187189
}
@@ -192,10 +194,12 @@ class SubscriptionItem extends StatelessWidget {
192194
super.key,
193195
required this.subscription,
194196
required this.unreadCount,
197+
required this.hasMutedUnreads,
195198
});
196199

197200
final Subscription subscription;
198201
final int unreadCount;
202+
final bool hasMutedUnreads;
199203

200204
@override
201205
Widget build(BuildContext context) {
@@ -239,6 +243,10 @@ class SubscriptionItem extends StatelessWidget {
239243
// TODO(#384) show @-mention indicator when it applies
240244
UnreadCountBadge(count: unreadCount, backgroundColor: swatch, bold: true),
241245
],
246+
if (hasMutedUnreads) ...[
247+
const SizedBox(width: 12),
248+
const MutedUnreadBadge(),
249+
],
242250
const SizedBox(width: 16),
243251
])));
244252
}

lib/widgets/unread_count_badge.dart

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11

22
import 'package:flutter/material.dart';
3+
import 'package:flutter_color_models/flutter_color_models.dart';
34

45
import '../api/model/model.dart';
56
import 'text.dart';
@@ -65,3 +66,22 @@ class UnreadCountBadge extends StatelessWidget {
6566
count.toString())));
6667
}
6768
}
69+
70+
class MutedUnreadBadge extends StatelessWidget {
71+
const MutedUnreadBadge({
72+
super.key,
73+
});
74+
75+
@override
76+
Widget build(BuildContext context) {
77+
return Opacity(
78+
opacity: 0.5,
79+
child: Container(
80+
width: 8,
81+
height: 8,
82+
margin: const EdgeInsetsDirectional.only(end: 3),
83+
decoration: const BoxDecoration(
84+
color: HslColor(0, 0, 0.8),
85+
shape: BoxShape.circle,),),);
86+
}
87+
}

test/model/unreads_test.dart

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,30 @@ void main() {
216216
check(model.countInDmNarrow(narrow)).equals(5);
217217
});
218218
});
219+
group('muted helpers', () {
220+
test('hasMutedInStream', () async {
221+
final stream = eg.stream();
222+
prepare();
223+
await streamStore.addStream(stream);
224+
await streamStore.addSubscription(eg.subscription(stream));
225+
await streamStore.addUserTopic(stream, 'a', UserTopicVisibilityPolicy.unmuted);
226+
await streamStore.addUserTopic(stream, 'c', UserTopicVisibilityPolicy.unmuted);
227+
fillWithMessages([
228+
eg.streamMessage(stream: stream, topic: 'a', flags: []),
229+
eg.streamMessage(stream: stream, topic: 'a', flags: []),
230+
eg.streamMessage(stream: stream, topic: 'b', flags: []),
231+
eg.streamMessage(stream: stream, topic: 'b', flags: []),
232+
eg.streamMessage(stream: stream, topic: 'b', flags: []),
233+
eg.streamMessage(stream: stream, topic: 'c', flags: []),
234+
]);
235+
check(model.hasMutedInStream(stream.streamId)).equals(false);
236+
237+
await streamStore.handleEvent(SubscriptionUpdateEvent(id: 1,
238+
streamId: stream.streamId,
239+
property: SubscriptionProperty.isMuted, value: true));
240+
check(model.hasMutedInStream(stream.streamId)).equals(true);
241+
});
242+
});
219243

220244
group('handleMessageEvent', () {
221245
for (final (isUnread, isStream, isDirectMentioned, isWildcardMentioned) in [

test/widgets/subscription_list_test.dart

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,50 @@ void main() {
174174
check(find.byType(UnreadCountBadge).evaluate()).length.equals(0);
175175
});
176176

177+
testWidgets('muted unread badge shows with muted unreads', (tester) async {
178+
final stream = eg.stream();
179+
final unreadMsgs = eg.unreadMsgs(streams: [
180+
UnreadStreamSnapshot(streamId: stream.streamId, topic: 'a', unreadMessageIds: [1, 2]),
181+
UnreadStreamSnapshot(streamId: stream.streamId, topic: 'b', unreadMessageIds: [3]),
182+
]);
183+
await setupStreamListPage(tester,
184+
subscriptions: [eg.subscription(stream, isMuted: true)],
185+
userTopics: [UserTopicItem(
186+
streamId: stream.streamId,
187+
topicName: 'b',
188+
lastUpdated: 1234567890,
189+
visibilityPolicy: UserTopicVisibilityPolicy.muted,
190+
)],
191+
unreadMsgs: unreadMsgs);
192+
final finder = find.byWidgetPredicate((widget) => widget is MutedUnreadBadge);
193+
check(finder.evaluate().length).equals(1);
194+
});
195+
196+
testWidgets('muted unread badge does not show with any unmuted unreads', (tester) async {
197+
final stream = eg.stream();
198+
final unreadMsgs = eg.unreadMsgs(streams: [
199+
UnreadStreamSnapshot(streamId: stream.streamId, topic: 'a', unreadMessageIds: [1, 2]),
200+
UnreadStreamSnapshot(streamId: stream.streamId, topic: 'b', unreadMessageIds: [3]),
201+
]);
202+
await setupStreamListPage(tester,
203+
subscriptions: [eg.subscription(stream, isMuted: true)],
204+
userTopics: [UserTopicItem(
205+
streamId: stream.streamId,
206+
topicName: 'b',
207+
lastUpdated: 1234567890,
208+
visibilityPolicy: UserTopicVisibilityPolicy.muted,
209+
),
210+
UserTopicItem(
211+
streamId: stream.streamId,
212+
topicName: 'a',
213+
lastUpdated: 9876543210,
214+
visibilityPolicy: UserTopicVisibilityPolicy.unmuted,
215+
)],
216+
unreadMsgs: unreadMsgs);
217+
final finder = find.byWidgetPredicate((widget) => widget is MutedUnreadBadge);
218+
check(finder.evaluate().length).equals(0);
219+
});
220+
177221
testWidgets('color propagates to icon and badge', (tester) async {
178222
final stream = eg.stream();
179223
final unreadMsgs = eg.unreadMsgs(streams: [

0 commit comments

Comments
 (0)