Skip to content

Commit 898a2ad

Browse files
chrisbobbesm-sayedi
andcommitted
msglist: In combined/mentions/starred, exclude DMs if all recipients muted
In the future, this should apply to the ReactionsNarrow from the Web app too, once we have it. Co-authored-by: Sayed Mahmood Sayedi <[email protected]>
1 parent 8339c64 commit 898a2ad

File tree

6 files changed

+345
-51
lines changed

6 files changed

+345
-51
lines changed

lib/model/message.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,12 @@ class MessageStoreImpl extends PerAccountStoreBase with MessageStore, _OutboxMes
287287
}
288288
}
289289

290+
void handleMutedUsersEvent(MutedUsersEvent event) {
291+
for (final view in _messageListViews) {
292+
view.handleMutedUsersEvent(event);
293+
}
294+
}
295+
290296
void handleMessageEvent(MessageEvent event) {
291297
// If the message is one we already know about (from a fetch),
292298
// clobber it with the one from the event system.

lib/model/message_list.dart

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import 'content.dart';
1313
import 'message.dart';
1414
import 'narrow.dart';
1515
import 'store.dart';
16+
import 'user.dart';
1617

1718
export '../api/route/messages.dart' show Anchor, AnchorCode, NumericAnchor;
1819

@@ -504,7 +505,9 @@ class MessageListView with ChangeNotifier, _MessageSequence {
504505
return switch (message.conversation) {
505506
StreamConversation(:final streamId, :final topic) =>
506507
store.isTopicVisible(streamId, topic),
507-
DmConversation() => true,
508+
DmConversation(:final allRecipientIds) =>
509+
!store.shouldMuteDmConversation(DmNarrow(
510+
allRecipientIds: allRecipientIds, selfUserId: store.selfUserId)),
508511
};
509512

510513
case ChannelNarrow(:final streamId):
@@ -515,8 +518,14 @@ class MessageListView with ChangeNotifier, _MessageSequence {
515518

516519
case TopicNarrow():
517520
case DmNarrow():
521+
return true;
522+
518523
case MentionsNarrow():
519524
case StarredMessagesNarrow():
525+
if (message.conversation case DmConversation(:final allRecipientIds)) {
526+
return !store.shouldMuteDmConversation(DmNarrow(
527+
allRecipientIds: allRecipientIds, selfUserId: store.selfUserId));
528+
}
520529
return true;
521530
}
522531
}
@@ -528,12 +537,12 @@ class MessageListView with ChangeNotifier, _MessageSequence {
528537
switch (narrow) {
529538
case CombinedFeedNarrow():
530539
case ChannelNarrow():
540+
case MentionsNarrow():
541+
case StarredMessagesNarrow():
531542
return false;
532543

533544
case TopicNarrow():
534545
case DmNarrow():
535-
case MentionsNarrow():
536-
case StarredMessagesNarrow():
537546
return true;
538547
}
539548
}
@@ -557,6 +566,22 @@ class MessageListView with ChangeNotifier, _MessageSequence {
557566
}
558567
}
559568

569+
/// Whether this event could affect the result that [_messageVisible]
570+
/// would ever have returned for any possible message in this message list.
571+
MutedUsersVisibilityEffect _mutedUsersEventCanAffectVisibility(MutedUsersEvent event) {
572+
switch(narrow) {
573+
case CombinedFeedNarrow():
574+
case MentionsNarrow():
575+
case StarredMessagesNarrow():
576+
return store.mightChangeShouldMuteDmConversation(event);
577+
578+
case ChannelNarrow():
579+
case TopicNarrow():
580+
case DmNarrow():
581+
return MutedUsersVisibilityEffect.none;
582+
}
583+
}
584+
560585
void _setStatus(FetchingStatus value, {FetchingStatus? was}) {
561586
assert(was == null || _status == was);
562587
_status = value;
@@ -795,6 +820,34 @@ class MessageListView with ChangeNotifier, _MessageSequence {
795820
}
796821
}
797822

823+
void handleMutedUsersEvent(MutedUsersEvent event) {
824+
switch (_mutedUsersEventCanAffectVisibility(event)) {
825+
case MutedUsersVisibilityEffect.none:
826+
return;
827+
828+
case MutedUsersVisibilityEffect.muted:
829+
final anyRemoved = _removeMessagesWhere((message) {
830+
if (message is! DmMessage) return false;
831+
final narrow = DmNarrow.ofMessage(message, selfUserId: store.selfUserId);
832+
return store.shouldMuteDmConversation(narrow, event: event);
833+
});
834+
if (anyRemoved) {
835+
notifyListeners();
836+
}
837+
838+
case MutedUsersVisibilityEffect.mixed:
839+
case MutedUsersVisibilityEffect.unmuted:
840+
// TODO get the newly-unmuted messages from the message store
841+
// For now, we simplify the task by just refetching this message list
842+
// from scratch.
843+
if (fetched) {
844+
_reset();
845+
notifyListeners();
846+
fetchInitial();
847+
}
848+
}
849+
}
850+
798851
void handleDeleteMessageEvent(DeleteMessageEvent event) {
799852
if (_removeMessagesById(event.messageIds)) {
800853
notifyListeners();

lib/model/store.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,10 @@ class PerAccountStore extends PerAccountStoreBase with ChangeNotifier, EmojiStor
649649
bool isUserMuted(int userId, {MutedUsersEvent? event}) =>
650650
_users.isUserMuted(userId, event: event);
651651

652+
@override
653+
MutedUsersVisibilityEffect mightChangeShouldMuteDmConversation(MutedUsersEvent event) =>
654+
_users.mightChangeShouldMuteDmConversation(event);
655+
652656
final UserStoreImpl _users;
653657

654658
final TypingStatus typingStatus;
@@ -958,6 +962,8 @@ class PerAccountStore extends PerAccountStoreBase with ChangeNotifier, EmojiStor
958962

959963
case MutedUsersEvent():
960964
assert(debugLog("server event: muted_users"));
965+
_messages.handleMutedUsersEvent(event);
966+
// Update _users last, so other handlers can compare to the old value.
961967
_users.handleMutedUsersEvent(event);
962968
notifyListeners();
963969

lib/model/user.dart

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import '../api/model/events.dart';
22
import '../api/model/initial_snapshot.dart';
33
import '../api/model/model.dart';
4+
import 'algorithms.dart';
45
import 'localizations.dart';
56
import 'narrow.dart';
67
import 'store.dart';
@@ -97,6 +98,27 @@ mixin UserStore on PerAccountStoreBase {
9798
return narrow.otherRecipientIds.every(
9899
(userId) => isUserMuted(userId, event: event));
99100
}
101+
102+
/// Whether the given event might change the result of [shouldMuteDmConversation]
103+
/// for its list of muted users, compared to the current state.
104+
MutedUsersVisibilityEffect mightChangeShouldMuteDmConversation(MutedUsersEvent event);
105+
}
106+
107+
/// Whether and how a given [MutedUsersEvent] may affect the results
108+
/// that [UserStore.shouldMuteDmConversation] would give for some messages.
109+
enum MutedUsersVisibilityEffect {
110+
/// The event will have no effect on the visibility results.
111+
none,
112+
113+
/// The event may change some visibility results from true to false.
114+
muted,
115+
116+
/// The event may change some visibility results from false to true.
117+
unmuted,
118+
119+
/// The event may change some visibility results from false to true,
120+
/// and some from true to false.
121+
mixed;
100122
}
101123

102124
/// The implementation of [UserStore] that does the work.
@@ -130,6 +152,29 @@ class UserStoreImpl extends PerAccountStoreBase with UserStore {
130152
return (event?.mutedUsers.map((item) => item.id) ?? _mutedUsers).contains(userId);
131153
}
132154

155+
@override
156+
MutedUsersVisibilityEffect mightChangeShouldMuteDmConversation(MutedUsersEvent event) {
157+
final sortedOld = _mutedUsers.toList()..sort();
158+
final sortedNew = event.mutedUsers.map((u) => u.id).toList()..sort();
159+
assert(isSortedWithoutDuplicates(sortedOld));
160+
assert(isSortedWithoutDuplicates(sortedNew));
161+
final union = setUnion(sortedOld, sortedNew);
162+
163+
final willMuteSome = sortedOld.length < union.length;
164+
final willUnmuteSome = sortedNew.length < union.length;
165+
166+
switch ((willUnmuteSome, willMuteSome)) {
167+
case (true, false):
168+
return MutedUsersVisibilityEffect.unmuted;
169+
case (false, true):
170+
return MutedUsersVisibilityEffect.muted;
171+
case (true, true):
172+
return MutedUsersVisibilityEffect.mixed;
173+
case (false, false): // TODO(log)?
174+
return MutedUsersVisibilityEffect.none;
175+
}
176+
}
177+
133178
void handleRealmUserEvent(RealmUserEvent event) {
134179
switch (event) {
135180
case RealmUserAddEvent():

0 commit comments

Comments
 (0)