Skip to content

Mute muted users (Chris's revision, 2 of 2) #1561

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 20 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
c47024e
users: Have userDisplayEmail handle unknown users
chrisbobbe Jun 9, 2025
9e99dea
lightbox: Use senderDisplayName for sender's name
chrisbobbe Jun 9, 2025
9e8c892
compose: Fix error on quote-and-replying message from unknown sender
chrisbobbe Jun 9, 2025
8878485
users [nfc]: Use userDisplayName at last non-self-user sites in widgets/
chrisbobbe Jun 9, 2025
5d34d08
muted-users: Say "Muted user" to replace a user's name, where applicable
chrisbobbe Jun 9, 2025
ccb5e13
theme [nfc]: Rename some variables that aren't named variables in Figma
chrisbobbe Jun 9, 2025
f191080
muted-users: Use placeholder for avatars of muted users, where applic…
chrisbobbe Jun 9, 2025
becfbce
msglist [nfc]: Remove a no-op MainAxisAlignment.spaceBetween in _Send…
chrisbobbe Jun 9, 2025
004c05d
button [nfc]: Have ZulipWebUiKitButton support a smaller, ad hoc size
chrisbobbe Jun 10, 2025
acc3c39
button [nfc]: Have ZulipWebUiKitButton support an icon
chrisbobbe Jun 10, 2025
6489bd5
button [nfc]: Have ZulipWebUiKitButton support ad hoc minimal-neutral…
chrisbobbe Jun 10, 2025
9cd2972
msglist: Hide content of muted messages, with a "Reveal message" button
chrisbobbe Jun 9, 2025
7b5250d
recent-dms: Exclude DM conversations where all other recipients are m…
chrisbobbe Jun 10, 2025
c03987a
msglist [nfc]: Place `_allMessagesVisible` right after `_messageVisible`
sm-sayedi May 22, 2025
ff6bd53
msglist [nfc]: s/VisibilityEffect/UserTopicVisibilityEffect/
chrisbobbe Jun 10, 2025
f23302b
msglist: In combined/mentions/starred, exclude DMs if all recipients …
chrisbobbe Jun 10, 2025
d1895b7
autocomplete: Exclude muted users from user-mention autocomplete
chrisbobbe Jun 10, 2025
c447d4f
msglist [nfc]: Represent nobody-is-typing as null, not empty list
chrisbobbe Jun 10, 2025
411df89
msglist: Exclude muted users from typing-status text
chrisbobbe Jun 10, 2025
5438a65
new-dm: Exclude muted users
chrisbobbe Jun 12, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions assets/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -1007,17 +1007,13 @@
"@noEarlierMessages": {
"description": "Text to show at the start of a message list if there are no earlier messages."
},
"mutedSender": "Muted sender",
"@mutedSender": {
"description": "Name for a muted user to display in message list."
},
"revealButtonLabel": "Reveal message for muted sender",
"revealButtonLabel": "Reveal message",
"@revealButtonLabel": {
"description": "Label for the button revealing hidden message from a muted sender in message list."
},
"mutedUser": "Muted user",
"@mutedUser": {
"description": "Name for a muted user to display all over the app."
"description": "Text to display in place of a muted user's name."
},
"scrollToBottomTooltip": "Scroll to bottom",
"@scrollToBottomTooltip": {
Expand Down
4 changes: 0 additions & 4 deletions assets/l10n/app_pl.arb
Original file line number Diff line number Diff line change
Expand Up @@ -1089,10 +1089,6 @@
"@mutedSender": {
"description": "Name for a muted user to display in message list."
},
"revealButtonLabel": "Odsłoń wiadomość od wyciszonego użytkownika",
"@revealButtonLabel": {
"description": "Label for the button revealing hidden message from a muted sender in message list."
},
"mutedUser": "Wyciszony użytkownik",
"@mutedUser": {
"description": "Name for a muted user to display all over the app."
Expand Down
4 changes: 0 additions & 4 deletions assets/l10n/app_ru.arb
Original file line number Diff line number Diff line change
Expand Up @@ -1081,10 +1081,6 @@
"@mutedSender": {
"description": "Name for a muted user to display in message list."
},
"revealButtonLabel": "Показать сообщение отключенного отправителя",
"@revealButtonLabel": {
"description": "Label for the button revealing hidden message from a muted sender in message list."
},
"mutedUser": "Отключенный пользователь",
"@mutedUser": {
"description": "Name for a muted user to display all over the app."
Expand Down
10 changes: 2 additions & 8 deletions lib/generated/l10n/zulip_localizations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1499,19 +1499,13 @@ abstract class ZulipLocalizations {
/// **'No earlier messages'**
String get noEarlierMessages;

/// Name for a muted user to display in message list.
///
/// In en, this message translates to:
/// **'Muted sender'**
String get mutedSender;

/// Label for the button revealing hidden message from a muted sender in message list.
///
/// In en, this message translates to:
/// **'Reveal message for muted sender'**
/// **'Reveal message'**
String get revealButtonLabel;

/// Name for a muted user to display all over the app.
/// Text to display in place of a muted user's name.
///
/// In en, this message translates to:
/// **'Muted user'**
Expand Down
5 changes: 1 addition & 4 deletions lib/generated/l10n/zulip_localizations_ar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -820,10 +820,7 @@ class ZulipLocalizationsAr extends ZulipLocalizations {
String get noEarlierMessages => 'No earlier messages';

@override
String get mutedSender => 'Muted sender';

@override
String get revealButtonLabel => 'Reveal message for muted sender';
String get revealButtonLabel => 'Reveal message';

@override
String get mutedUser => 'Muted user';
Expand Down
5 changes: 1 addition & 4 deletions lib/generated/l10n/zulip_localizations_de.dart
Original file line number Diff line number Diff line change
Expand Up @@ -820,10 +820,7 @@ class ZulipLocalizationsDe extends ZulipLocalizations {
String get noEarlierMessages => 'No earlier messages';

@override
String get mutedSender => 'Muted sender';

@override
String get revealButtonLabel => 'Reveal message for muted sender';
String get revealButtonLabel => 'Reveal message';

@override
String get mutedUser => 'Muted user';
Expand Down
5 changes: 1 addition & 4 deletions lib/generated/l10n/zulip_localizations_en.dart
Original file line number Diff line number Diff line change
Expand Up @@ -820,10 +820,7 @@ class ZulipLocalizationsEn extends ZulipLocalizations {
String get noEarlierMessages => 'No earlier messages';

@override
String get mutedSender => 'Muted sender';

@override
String get revealButtonLabel => 'Reveal message for muted sender';
String get revealButtonLabel => 'Reveal message';

@override
String get mutedUser => 'Muted user';
Expand Down
5 changes: 1 addition & 4 deletions lib/generated/l10n/zulip_localizations_ja.dart
Original file line number Diff line number Diff line change
Expand Up @@ -820,10 +820,7 @@ class ZulipLocalizationsJa extends ZulipLocalizations {
String get noEarlierMessages => 'No earlier messages';

@override
String get mutedSender => 'Muted sender';

@override
String get revealButtonLabel => 'Reveal message for muted sender';
String get revealButtonLabel => 'Reveal message';

@override
String get mutedUser => 'Muted user';
Expand Down
5 changes: 1 addition & 4 deletions lib/generated/l10n/zulip_localizations_nb.dart
Original file line number Diff line number Diff line change
Expand Up @@ -820,10 +820,7 @@ class ZulipLocalizationsNb extends ZulipLocalizations {
String get noEarlierMessages => 'No earlier messages';

@override
String get mutedSender => 'Muted sender';

@override
String get revealButtonLabel => 'Reveal message for muted sender';
String get revealButtonLabel => 'Reveal message';

@override
String get mutedUser => 'Muted user';
Expand Down
5 changes: 1 addition & 4 deletions lib/generated/l10n/zulip_localizations_pl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -833,10 +833,7 @@ class ZulipLocalizationsPl extends ZulipLocalizations {
String get noEarlierMessages => 'Brak historii';

@override
String get mutedSender => 'Wyciszony nadawca';

@override
String get revealButtonLabel => 'Odsłoń wiadomość od wyciszonego użytkownika';
String get revealButtonLabel => 'Reveal message';

@override
String get mutedUser => 'Wyciszony użytkownik';
Expand Down
5 changes: 1 addition & 4 deletions lib/generated/l10n/zulip_localizations_ru.dart
Original file line number Diff line number Diff line change
Expand Up @@ -835,10 +835,7 @@ class ZulipLocalizationsRu extends ZulipLocalizations {
String get noEarlierMessages => 'Предшествующих сообщений нет';

@override
String get mutedSender => 'Отключенный отправитель';

@override
String get revealButtonLabel => 'Показать сообщение отключенного отправителя';
String get revealButtonLabel => 'Reveal message';

@override
String get mutedUser => 'Отключенный пользователь';
Expand Down
5 changes: 1 addition & 4 deletions lib/generated/l10n/zulip_localizations_sk.dart
Original file line number Diff line number Diff line change
Expand Up @@ -822,10 +822,7 @@ class ZulipLocalizationsSk extends ZulipLocalizations {
String get noEarlierMessages => 'No earlier messages';

@override
String get mutedSender => 'Muted sender';

@override
String get revealButtonLabel => 'Reveal message for muted sender';
String get revealButtonLabel => 'Reveal message';

@override
String get mutedUser => 'Muted user';
Expand Down
5 changes: 1 addition & 4 deletions lib/generated/l10n/zulip_localizations_uk.dart
Original file line number Diff line number Diff line change
Expand Up @@ -834,10 +834,7 @@ class ZulipLocalizationsUk extends ZulipLocalizations {
String get noEarlierMessages => 'Немає попередніх повідомлень';

@override
String get mutedSender => 'Muted sender';

@override
String get revealButtonLabel => 'Reveal message for muted sender';
String get revealButtonLabel => 'Reveal message';

@override
String get mutedUser => 'Muted user';
Expand Down
5 changes: 1 addition & 4 deletions lib/generated/l10n/zulip_localizations_zh.dart
Original file line number Diff line number Diff line change
Expand Up @@ -820,10 +820,7 @@ class ZulipLocalizationsZh extends ZulipLocalizations {
String get noEarlierMessages => 'No earlier messages';

@override
String get mutedSender => 'Muted sender';

@override
String get revealButtonLabel => 'Reveal message for muted sender';
String get revealButtonLabel => 'Reveal message';

@override
String get mutedUser => 'Muted user';
Expand Down
6 changes: 4 additions & 2 deletions lib/model/autocomplete.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import 'compose.dart';
import 'emoji.dart';
import 'narrow.dart';
import 'store.dart';
import 'user.dart';

extension ComposeContentAutocomplete on ComposeContentController {
AutocompleteIntent<ComposeAutocompleteQuery>? autocompleteIntent() {
Expand Down Expand Up @@ -648,7 +649,7 @@ class MentionAutocompleteView extends AutocompleteView<MentionAutocompleteQuery,
}

MentionAutocompleteResult? _testUser(MentionAutocompleteQuery query, User user) {
if (query.testUser(user, store.autocompleteViewManager.autocompleteDataCache)) {
if (query.testUser(user, store.autocompleteViewManager.autocompleteDataCache, store)) {
return UserMentionAutocompleteResult(userId: user.userId);
}
return null;
Expand Down Expand Up @@ -753,9 +754,10 @@ class MentionAutocompleteQuery extends ComposeAutocompleteQuery {
|| wildcardOption.localizedCanonicalString(localizations).contains(_lowercase);
}

bool testUser(User user, AutocompleteDataCache cache) {
bool testUser(User user, AutocompleteDataCache cache, UserStore store) {
// TODO(#236) test email too, not just name
if (!user.isActive) return false;
if (store.isUserMuted(user.userId)) return false;

return _testName(user, cache);
}
Expand Down
18 changes: 9 additions & 9 deletions lib/model/channel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,10 @@ mixin ChannelStore {

/// Whether the given event will change the result of [isTopicVisibleInStream]
/// for its stream and topic, compared to the current state.
VisibilityEffect willChangeIfTopicVisibleInStream(UserTopicEvent event) {
UserTopicVisibilityEffect willChangeIfTopicVisibleInStream(UserTopicEvent event) {
final streamId = event.streamId;
final topic = event.topicName;
return VisibilityEffect._fromBeforeAfter(
return UserTopicVisibilityEffect._fromBeforeAfter(
_isTopicVisibleInStream(topicVisibilityPolicy(streamId, topic)),
_isTopicVisibleInStream(event.visibilityPolicy));
}
Expand Down Expand Up @@ -106,10 +106,10 @@ mixin ChannelStore {

/// Whether the given event will change the result of [isTopicVisible]
/// for its stream and topic, compared to the current state.
VisibilityEffect willChangeIfTopicVisible(UserTopicEvent event) {
UserTopicVisibilityEffect willChangeIfTopicVisible(UserTopicEvent event) {
final streamId = event.streamId;
final topic = event.topicName;
return VisibilityEffect._fromBeforeAfter(
return UserTopicVisibilityEffect._fromBeforeAfter(
_isTopicVisible(streamId, topicVisibilityPolicy(streamId, topic)),
_isTopicVisible(streamId, event.visibilityPolicy));
}
Expand Down Expand Up @@ -137,7 +137,7 @@ mixin ChannelStore {
/// Whether and how a given [UserTopicEvent] will affect the results
/// that [ChannelStore.isTopicVisible] or [ChannelStore.isTopicVisibleInStream]
/// would give for some messages.
enum VisibilityEffect {
enum UserTopicVisibilityEffect {
/// The event will have no effect on the visibility results.
none,

Expand All @@ -147,11 +147,11 @@ enum VisibilityEffect {
/// The event will change some visibility results from false to true.
unmuted;

factory VisibilityEffect._fromBeforeAfter(bool before, bool after) {
factory UserTopicVisibilityEffect._fromBeforeAfter(bool before, bool after) {
return switch ((before, after)) {
(false, true) => VisibilityEffect.unmuted,
(true, false) => VisibilityEffect.muted,
_ => VisibilityEffect.none,
(false, true) => UserTopicVisibilityEffect.unmuted,
(true, false) => UserTopicVisibilityEffect.muted,
_ => UserTopicVisibilityEffect.none,
Comment on lines -152 to +154
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is awkwardly verbose, but we're about to add another kind of
visibility effect, and I think the code will end up clearer if we
make a separate enum for it.

Yeah, agreed.

Some of the verbosity (like in these lines) will at least get resolved by the upcoming Dart feature of "dot shorthands", expected later this year:
https://github.com/dart-lang/language/blob/main/working/3616%20-%20enum%20value%20shorthand/proposal-simple-lrhn.md

I believe with that feature we'll be able to say just .unmuted, .muted, .none here, without the name of the enum type.

};
}
}
Expand Down
43 changes: 30 additions & 13 deletions lib/model/compose.dart
Original file line number Diff line number Diff line change
Expand Up @@ -130,14 +130,33 @@ String wrapWithBacktickFence({required String content, String? infoString}) {
/// To omit the user ID part ("|13313") whenever the name part is unambiguous,
/// pass the full UserStore. This means accepting a linear scan
/// through all users; avoid it in performance-sensitive codepaths.
///
/// See also [userMentionFromMessage].
String userMention(User user, {bool silent = false, UserStore? users}) {
bool includeUserId = users == null
|| users.allUsers.where((u) => u.fullName == user.fullName)
.take(2).length == 2;

return '@${silent ? '_' : ''}**${user.fullName}${includeUserId ? '|${user.userId}' : ''}**';
return _userMentionImpl(
silent: silent,
fullName: user.fullName,
userId: includeUserId ? user.userId : null);
}

/// An @-mention of an individual user, like @**Chris Bobbe|13313**,
/// from sender data in a [Message].
///
/// The user ID part ("|13313") is always included.
///
/// See also [userMention].
String userMentionFromMessage(Message message, {bool silent = false, required UserStore users}) =>
_userMentionImpl(
silent: silent,
fullName: users.senderDisplayName(message, replaceIfMuted: false),
userId: message.senderId);

String _userMentionImpl({required bool silent, required String fullName, int? userId}) =>
'@${silent ? '_' : ''}**$fullName${userId != null ? '|$userId' : ''}**';

/// An @-mention of all the users in a conversation, like @**channel**.
String wildcardMention(WildcardMentionOption wildcardOption, {
required PerAccountStore store,
Expand Down Expand Up @@ -190,13 +209,11 @@ String quoteAndReplyPlaceholder(
PerAccountStore store, {
required Message message,
}) {
final sender = store.getUser(message.senderId);
assert(sender != null); // TODO(#716): should use `store.senderDisplayName`
final url = narrowLink(store,
SendableNarrow.ofMessage(message, selfUserId: store.selfUserId),
nearMessageId: message.id);
// See note in [quoteAndReply] about asking `mention` to omit the |<id> part.
return '${userMention(sender!, silent: true)} ${inlineLink('said', url)}: ' // TODO(#1285)
return '${userMentionFromMessage(message, silent: true, users: store)} '
'${inlineLink('said', url)}: ' // TODO(#1285)
'*${zulipLocalizations.composeBoxLoadingMessage(message.id)}*\n';
}

Expand All @@ -212,14 +229,14 @@ String quoteAndReply(PerAccountStore store, {
required Message message,
required String rawContent,
}) {
final sender = store.getUser(message.senderId);
assert(sender != null); // TODO(#716): should use `store.senderDisplayName`
final url = narrowLink(store,
SendableNarrow.ofMessage(message, selfUserId: store.selfUserId),
nearMessageId: message.id);
// Could ask `mention` to omit the |<id> part unless the mention is ambiguous…
// but that would mean a linear scan through all users, and the extra noise
// won't much matter with the already probably-long message link in there too.
return '${userMention(sender!, silent: true)} ${inlineLink('said', url)}:\n' // TODO(#1285)
'${wrapWithBacktickFence(content: rawContent, infoString: 'quote')}';
// Could ask userMentionFromMessage to omit the |<id> part unless the mention
// is ambiguous… but that would mean a linear scan through all users,
// and the extra noise won't much matter with the already probably-long
// message link in there too.
return '${userMentionFromMessage(message, silent: true, users: store)} '
'${inlineLink('said', url)}:\n' // TODO(#1285)
'${wrapWithBacktickFence(content: rawContent, infoString: 'quote')}';
}
6 changes: 6 additions & 0 deletions lib/model/message.dart
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,12 @@ class MessageStoreImpl extends PerAccountStoreBase with MessageStore, _OutboxMes
}
}

void handleMutedUsersEvent(MutedUsersEvent event) {
for (final view in _messageListViews) {
view.handleMutedUsersEvent(event);
}
}

void handleMessageEvent(MessageEvent event) {
// If the message is one we already know about (from a fetch),
// clobber it with the one from the event system.
Expand Down
Loading