Skip to content

Commit 3e95dd2

Browse files
chrisbobbesm-sayedi
andcommitted
recent-dms: Exclude DM conversations where all other recipients are muted
Co-authored-by: Sayed Mahmood Sayedi <[email protected]>
1 parent c90fa9d commit 3e95dd2

File tree

4 files changed

+45
-12
lines changed

4 files changed

+45
-12
lines changed

lib/model/user.dart

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import '../api/model/events.dart';
22
import '../api/model/initial_snapshot.dart';
33
import '../api/model/model.dart';
44
import 'localizations.dart';
5+
import 'narrow.dart';
56
import 'store.dart';
67

78
/// The portion of [PerAccountStore] describing the users in the realm.
@@ -85,6 +86,17 @@ mixin UserStore on PerAccountStoreBase {
8586
/// Looks for [userId] in a private [Set],
8687
/// or in [mutedUsers] instead if that's non-null.
8788
bool isUserMuted(int userId, {Set<int>? mutedUsers});
89+
90+
/// Whether the self-user has muted everyone in [narrow].
91+
///
92+
/// Returns false for the self-DM.
93+
///
94+
/// Calls [isUserMuted] for each participant, passing along [mutedUsers].
95+
bool shouldMuteDmConversation(DmNarrow narrow, {Set<int>? mutedUsers}) {
96+
if (narrow.otherRecipientIds.isEmpty) return false;
97+
return narrow.otherRecipientIds.every(
98+
(userId) => isUserMuted(userId, mutedUsers: mutedUsers));
99+
}
88100
}
89101

90102
/// The implementation of [UserStore] that does the work.

lib/widgets/recent_dm_conversations.dart

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ class _RecentDmConversationsPageBodyState extends State<RecentDmConversationsPag
4848

4949
@override
5050
Widget build(BuildContext context) {
51+
final store = PerAccountStoreWidget.of(context);
5152
final sorted = model!.sorted;
5253
return SafeArea(
5354
// Don't pad the bottom here; we want the list content to do that.
@@ -56,6 +57,12 @@ class _RecentDmConversationsPageBodyState extends State<RecentDmConversationsPag
5657
itemCount: sorted.length,
5758
itemBuilder: (context, index) {
5859
final narrow = sorted[index];
60+
if (store.shouldMuteDmConversation(narrow)) {
61+
// Filter out conversations where everyone is muted.
62+
// TODO should we offer a "spam folder"-style summary screen
63+
// for these conversations we're filtering out?
64+
return SizedBox.shrink();
65+
}
5966
return RecentDmConversationsItem(
6067
narrow: narrow,
6168
unreadCount: unreadsModel!.countInDmNarrow(narrow),
@@ -88,9 +95,6 @@ class RecentDmConversationsItem extends StatelessWidget {
8895
title = store.selfUser.fullName;
8996
avatar = AvatarImage(userId: store.selfUserId, size: _avatarSize);
9097
case [var otherUserId]:
91-
// TODO(#296) actually don't show this row if the user is muted?
92-
// (should we offer a "spam folder" style summary screen of recent
93-
// 1:1 DM conversations from muted users?)
9498
title = store.userDisplayName(otherUserId);
9599
avatar = AvatarImage(userId: otherUserId, size: _avatarSize);
96100
default:

test/model/user_test.dart

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import 'package:checks/checks.dart';
22
import 'package:flutter_test/flutter_test.dart';
33
import 'package:zulip/api/model/events.dart';
44
import 'package:zulip/api/model/model.dart';
5+
import 'package:zulip/model/narrow.dart';
6+
import 'package:zulip/model/store.dart';
57

68
import '../api/model/model_checks.dart';
79
import '../example_data.dart' as eg;
@@ -81,25 +83,39 @@ void main() {
8183
});
8284

8385
testWidgets('MutedUsersEvent', (tester) async {
86+
late PerAccountStore store;
87+
88+
void checkDmConversationMuted(List<int> otherUserIds, bool expected) {
89+
final narrow = DmNarrow.withOtherUsers(otherUserIds, selfUserId: store.selfUserId);
90+
check(store.shouldMuteDmConversation(narrow)).equals(expected);
91+
}
92+
8493
final user1 = eg.user(userId: 1);
8594
final user2 = eg.user(userId: 2);
8695
final user3 = eg.user(userId: 3);
8796

88-
final store = eg.store(initialSnapshot: eg.initialSnapshot(
97+
store = eg.store(initialSnapshot: eg.initialSnapshot(
8998
realmUsers: [user1, user2, user3],
9099
mutedUsers: [MutedUserItem(id: 2), MutedUserItem(id: 1)]));
91100
check(store.isUserMuted(1)).isTrue();
92101
check(store.isUserMuted(2)).isTrue();
93102
check(store.isUserMuted(3)).isFalse();
103+
checkDmConversationMuted([1], true);
104+
checkDmConversationMuted([1, 2], true);
105+
checkDmConversationMuted([2, 3], false);
106+
checkDmConversationMuted([1, 2, 3], false);
94107

95108
await store.handleEvent(eg.mutedUsersEvent([2, 1, 3]));
96109
check(store.isUserMuted(1)).isTrue();
97110
check(store.isUserMuted(2)).isTrue();
98111
check(store.isUserMuted(3)).isTrue();
112+
checkDmConversationMuted([1, 2, 3], true);
99113

100114
await store.handleEvent(eg.mutedUsersEvent([2, 3]));
101115
check(store.isUserMuted(1)).isFalse();
102116
check(store.isUserMuted(2)).isTrue();
103117
check(store.isUserMuted(3)).isTrue();
118+
checkDmConversationMuted([1], false);
119+
checkDmConversationMuted([], false);
104120
});
105121
}

test/widgets/recent_dm_conversations_test.dart

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'package:checks/checks.dart';
22
import 'package:flutter/material.dart';
33
import 'package:flutter/rendering.dart';
4+
import 'package:flutter_checks/flutter_checks.dart';
45
import 'package:flutter_test/flutter_test.dart';
56
import 'package:zulip/api/model/events.dart';
67
import 'package:zulip/api/model/model.dart';
@@ -67,11 +68,11 @@ Future<void> setupPage(WidgetTester tester, {
6768
void main() {
6869
TestZulipBinding.ensureInitialized();
6970

70-
group('RecentDmConversationsPage', () {
71-
Finder findConversationItem(Narrow narrow) => find.byWidgetPredicate(
72-
(widget) => widget is RecentDmConversationsItem && widget.narrow == narrow,
73-
);
71+
Finder findConversationItem(Narrow narrow) => find.byWidgetPredicate(
72+
(widget) => widget is RecentDmConversationsItem && widget.narrow == narrow,
73+
);
7474

75+
group('RecentDmConversationsPage', () {
7576
testWidgets('page builds; conversations appear in order', (tester) async {
7677
final user1 = eg.user(userId: 1);
7778
final user2 = eg.user(userId: 2);
@@ -226,8 +227,8 @@ void main() {
226227
mutedUserIds: [user.userId],
227228
dmMessages: [message]);
228229

229-
checkAvatar(tester, DmNarrow.ofMessage(message, selfUserId: eg.selfUser.userId));
230-
checkTitle(tester, 'Muted user');
230+
final narrow = DmNarrow.ofMessage(message, selfUserId: eg.selfUser.userId);
231+
check(findConversationItem(narrow)).findsNothing();
231232
});
232233
});
233234

@@ -312,8 +313,8 @@ void main() {
312313
mutedUserIds: [user0.userId, user1.userId],
313314
dmMessages: [message]);
314315

315-
checkAvatar(tester, DmNarrow.ofMessage(message, selfUserId: eg.selfUser.userId));
316-
checkTitle(tester, 'Muted user, Muted user');
316+
final narrow = DmNarrow.ofMessage(message, selfUserId: eg.selfUser.userId);
317+
check(findConversationItem(narrow)).findsNothing();
317318
});
318319
});
319320

0 commit comments

Comments
 (0)