Skip to content

Commit 5d34d08

Browse files
chrisbobbesm-sayedi
authored andcommitted
muted-users: Say "Muted user" to replace a user's name, where applicable
(Done by adding an is-muted condition in store.userDisplayName and store.senderDisplayName, with an opt-out param.) If Chris is muted, we'll now show "Muted user" where before we would show "Chris Bobbe", in the following places: - Message-list page: - DM-narrow app bar. - DM recipient headers. - The sender row on messages. This and message content will get more treatment in a separate commit. - Emoji-reaction chips on messages. - Typing indicators ("Muted user is typing…"), but we'll be excluding muted users, coming up in a separate commit. - Voter names in poll messages. - DM items in the Inbox page. (Messages from muted users are automatically marked as read, but they can end up in the inbox if you un-mark them as read.) - The new-DM sheet, but we'll be excluding muted users, coming up in a separate commit. - @-mention autocomplete, but we'll be excluding muted users, coming up in a separate commit. - Items in the Direct messages ("recent DMs") page. We'll be excluding DMs where everyone is muted, coming up in a separate commit. - User items in custom profile fields. We *don't* do this replacement in the following places, i.e., we'll still show "Chris Bobbe" if Chris is muted: - Sender name in the header of the lightbox page. (This follows web.) - The "hint text" for the compose box in a DM narrow: it will still say "Message @chris Bobbe", not "Message @muted user". (This follows web.) - The user's name at the top of the Profile page. - We won't generate malformed @-mention syntax like @_**Muted user|13313**. Co-authored-by: Sayed Mahmood Sayedi <[email protected]>
1 parent 8878485 commit 5d34d08

25 files changed

+242
-71
lines changed

assets/l10n/app_en.arb

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,17 +1007,13 @@
10071007
"@noEarlierMessages": {
10081008
"description": "Text to show at the start of a message list if there are no earlier messages."
10091009
},
1010-
"mutedSender": "Muted sender",
1011-
"@mutedSender": {
1012-
"description": "Name for a muted user to display in message list."
1013-
},
10141010
"revealButtonLabel": "Reveal message for muted sender",
10151011
"@revealButtonLabel": {
10161012
"description": "Label for the button revealing hidden message from a muted sender in message list."
10171013
},
10181014
"mutedUser": "Muted user",
10191015
"@mutedUser": {
1020-
"description": "Name for a muted user to display all over the app."
1016+
"description": "Text to display in place of a muted user's name."
10211017
},
10221018
"scrollToBottomTooltip": "Scroll to bottom",
10231019
"@scrollToBottomTooltip": {

lib/generated/l10n/zulip_localizations.dart

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1499,19 +1499,13 @@ abstract class ZulipLocalizations {
14991499
/// **'No earlier messages'**
15001500
String get noEarlierMessages;
15011501

1502-
/// Name for a muted user to display in message list.
1503-
///
1504-
/// In en, this message translates to:
1505-
/// **'Muted sender'**
1506-
String get mutedSender;
1507-
15081502
/// Label for the button revealing hidden message from a muted sender in message list.
15091503
///
15101504
/// In en, this message translates to:
15111505
/// **'Reveal message for muted sender'**
15121506
String get revealButtonLabel;
15131507

1514-
/// Name for a muted user to display all over the app.
1508+
/// Text to display in place of a muted user's name.
15151509
///
15161510
/// In en, this message translates to:
15171511
/// **'Muted user'**

lib/generated/l10n/zulip_localizations_ar.dart

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -819,9 +819,6 @@ class ZulipLocalizationsAr extends ZulipLocalizations {
819819
@override
820820
String get noEarlierMessages => 'No earlier messages';
821821

822-
@override
823-
String get mutedSender => 'Muted sender';
824-
825822
@override
826823
String get revealButtonLabel => 'Reveal message for muted sender';
827824

lib/generated/l10n/zulip_localizations_de.dart

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -819,9 +819,6 @@ class ZulipLocalizationsDe extends ZulipLocalizations {
819819
@override
820820
String get noEarlierMessages => 'No earlier messages';
821821

822-
@override
823-
String get mutedSender => 'Muted sender';
824-
825822
@override
826823
String get revealButtonLabel => 'Reveal message for muted sender';
827824

lib/generated/l10n/zulip_localizations_en.dart

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -819,9 +819,6 @@ class ZulipLocalizationsEn extends ZulipLocalizations {
819819
@override
820820
String get noEarlierMessages => 'No earlier messages';
821821

822-
@override
823-
String get mutedSender => 'Muted sender';
824-
825822
@override
826823
String get revealButtonLabel => 'Reveal message for muted sender';
827824

lib/generated/l10n/zulip_localizations_ja.dart

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -819,9 +819,6 @@ class ZulipLocalizationsJa extends ZulipLocalizations {
819819
@override
820820
String get noEarlierMessages => 'No earlier messages';
821821

822-
@override
823-
String get mutedSender => 'Muted sender';
824-
825822
@override
826823
String get revealButtonLabel => 'Reveal message for muted sender';
827824

lib/generated/l10n/zulip_localizations_nb.dart

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -819,9 +819,6 @@ class ZulipLocalizationsNb extends ZulipLocalizations {
819819
@override
820820
String get noEarlierMessages => 'No earlier messages';
821821

822-
@override
823-
String get mutedSender => 'Muted sender';
824-
825822
@override
826823
String get revealButtonLabel => 'Reveal message for muted sender';
827824

lib/generated/l10n/zulip_localizations_pl.dart

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -832,9 +832,6 @@ class ZulipLocalizationsPl extends ZulipLocalizations {
832832
@override
833833
String get noEarlierMessages => 'Brak historii';
834834

835-
@override
836-
String get mutedSender => 'Wyciszony nadawca';
837-
838835
@override
839836
String get revealButtonLabel => 'Odsłoń wiadomość od wyciszonego użytkownika';
840837

lib/generated/l10n/zulip_localizations_ru.dart

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -834,9 +834,6 @@ class ZulipLocalizationsRu extends ZulipLocalizations {
834834
@override
835835
String get noEarlierMessages => 'Предшествующих сообщений нет';
836836

837-
@override
838-
String get mutedSender => 'Отключенный отправитель';
839-
840837
@override
841838
String get revealButtonLabel => 'Показать сообщение отключенного отправителя';
842839

lib/generated/l10n/zulip_localizations_sk.dart

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -821,9 +821,6 @@ class ZulipLocalizationsSk extends ZulipLocalizations {
821821
@override
822822
String get noEarlierMessages => 'No earlier messages';
823823

824-
@override
825-
String get mutedSender => 'Muted sender';
826-
827824
@override
828825
String get revealButtonLabel => 'Reveal message for muted sender';
829826

lib/generated/l10n/zulip_localizations_uk.dart

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -833,9 +833,6 @@ class ZulipLocalizationsUk extends ZulipLocalizations {
833833
@override
834834
String get noEarlierMessages => 'Немає попередніх повідомлень';
835835

836-
@override
837-
String get mutedSender => 'Muted sender';
838-
839836
@override
840837
String get revealButtonLabel => 'Reveal message for muted sender';
841838

lib/generated/l10n/zulip_localizations_zh.dart

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -819,9 +819,6 @@ class ZulipLocalizationsZh extends ZulipLocalizations {
819819
@override
820820
String get noEarlierMessages => 'No earlier messages';
821821

822-
@override
823-
String get mutedSender => 'Muted sender';
824-
825822
@override
826823
String get revealButtonLabel => 'Reveal message for muted sender';
827824

lib/model/compose.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ String userMention(User user, {bool silent = false, UserStore? users}) {
151151
String userMentionFromMessage(Message message, {bool silent = false, required UserStore users}) =>
152152
_userMentionImpl(
153153
silent: silent,
154-
fullName: users.senderDisplayName(message),
154+
fullName: users.senderDisplayName(message, replaceIfMuted: false),
155155
userId: message.senderId);
156156

157157
String _userMentionImpl({required bool silent, required String fullName, int? userId}) =>

lib/model/user.dart

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,27 +44,40 @@ mixin UserStore on PerAccountStoreBase {
4444

4545
/// The name to show the given user as in the UI, even for unknown users.
4646
///
47-
/// This is the user's [User.fullName] if the user is known,
48-
/// and otherwise a translation of "(unknown user)".
47+
/// If the user is muted and [replaceIfMuted] is true (the default),
48+
/// this is [ZulipLocalizations.mutedUser].
49+
///
50+
/// Otherwise this is the user's [User.fullName] if the user is known,
51+
/// or (if unknown) [ZulipLocalizations.unknownUserName].
4952
///
5053
/// When a [Message] is available which the user sent,
5154
/// use [senderDisplayName] instead for a better-informed fallback.
52-
String userDisplayName(int userId) {
55+
String userDisplayName(int userId, {bool replaceIfMuted = true}) {
56+
if (replaceIfMuted && isUserMuted(userId)) {
57+
return GlobalLocalizations.zulipLocalizations.mutedUser;
58+
}
5359
return getUser(userId)?.fullName
5460
?? GlobalLocalizations.zulipLocalizations.unknownUserName;
5561
}
5662

5763
/// The name to show for the given message's sender in the UI.
5864
///
59-
/// If the user is known (see [getUser]), this is their current [User.fullName].
65+
/// If the sender is muted and [replaceIfMuted] is true (the default),
66+
/// this is [ZulipLocalizations.mutedUser].
67+
///
68+
/// Otherwise, if the user is known (see [getUser]),
69+
/// this is their current [User.fullName].
6070
/// If unknown, this uses the fallback value conveniently provided on the
6171
/// [Message] object itself, namely [Message.senderFullName].
6272
///
6373
/// For a user who isn't the sender of some known message,
6474
/// see [userDisplayName].
65-
String senderDisplayName(Message message) {
66-
return getUser(message.senderId)?.fullName
67-
?? message.senderFullName;
75+
String senderDisplayName(Message message, {bool replaceIfMuted = true}) {
76+
final senderId = message.senderId;
77+
if (replaceIfMuted && isUserMuted(senderId)) {
78+
return GlobalLocalizations.zulipLocalizations.mutedUser;
79+
}
80+
return getUser(senderId)?.fullName ?? message.senderFullName;
6881
}
6982

7083
/// Whether the user with [userId] is muted by the self-user.

lib/widgets/compose_box.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -861,8 +861,9 @@ class _FixedDestinationContentInput extends StatelessWidget {
861861
final store = PerAccountStoreWidget.of(context);
862862
final user = store.getUser(otherUserId);
863863
if (user == null) return zulipLocalizations.composeBoxGenericContentHint;
864+
// TODO write a test where the user is muted
864865
return zulipLocalizations.composeBoxDmContentHint(
865-
store.userDisplayName(otherUserId));
866+
store.userDisplayName(otherUserId, replaceIfMuted: false));
866867

867868
case DmNarrow(): // A group DM thread.
868869
return zulipLocalizations.composeBoxGroupDmContentHint;

lib/widgets/inbox.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,7 @@ class _DmItem extends StatelessWidget {
395395
final store = PerAccountStoreWidget.of(context);
396396
final designVariables = DesignVariables.of(context);
397397

398+
// TODO write a test where a/the recipient is muted
398399
final title = switch (narrow.otherRecipientIds) { // TODO dedupe with [RecentDmConversationsItem]
399400
[] => store.selfUser.fullName,
400401
[var otherUserId] => store.userDisplayName(otherUserId),

lib/widgets/lightbox.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,8 @@ class _LightboxPageLayoutState extends State<_LightboxPageLayout> {
201201
child: RichText(
202202
text: TextSpan(children: [
203203
TextSpan(
204-
text: '${store.senderDisplayName(widget.message)}\n',
204+
// TODO write a test where the sender is muted
205+
text: '${store.senderDisplayName(widget.message, replaceIfMuted: false)}\n',
205206

206207
// Restate default
207208
style: themeData.textTheme.titleLarge!.copyWith(color: appBarForegroundColor)),

lib/widgets/profile.dart

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ class ProfilePage extends StatelessWidget {
4949
Center(
5050
child: Avatar(userId: userId, size: 200, borderRadius: 200 / 8)),
5151
const SizedBox(height: 16),
52-
Text(store.userDisplayName(userId),
52+
// TODO write a test where the user is muted
53+
Text(store.userDisplayName(userId, replaceIfMuted: false),
5354
textAlign: TextAlign.center,
5455
style: _TextStyles.primaryFieldText
5556
.merge(weightVariableTextStyle(context, wght: 700))),
@@ -75,7 +76,9 @@ class ProfilePage extends StatelessWidget {
7576
];
7677

7778
return Scaffold(
78-
appBar: ZulipAppBar(title: Text(store.userDisplayName(userId))),
79+
appBar: ZulipAppBar(
80+
// TODO write a test where the user is muted
81+
title: Text(store.userDisplayName(userId, replaceIfMuted: false))),
7982
body: SingleChildScrollView(
8083
child: Center(
8184
child: ConstrainedBox(

test/model/compose_test.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,15 @@ hello
281281
check(userMentionFromMessage(message, silent: false, users: store))
282282
.equals('@**Full Name|123**');
283283
});
284+
285+
test('userMentionFromMessage, muted user', () async {
286+
final store = eg.store();
287+
await store.addUser(user);
288+
await store.setMutedUsers([user.userId]);
289+
check(store.isUserMuted(user.userId)).isTrue();
290+
check(userMentionFromMessage(message, silent: false, users: store))
291+
.equals('@**Full Name|123**'); // not replaced with 'Muted user'
292+
});
284293
});
285294

286295
test('wildcard', () {

test/model/test_store.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,10 @@ extension PerAccountStoreTestExtension on PerAccountStore {
267267
}
268268
}
269269

270+
Future<void> setMutedUsers(List<int> userIds) async {
271+
await handleEvent(eg.mutedUsersEvent(userIds));
272+
}
273+
270274
Future<void> addStream(ZulipStream stream) async {
271275
await addStreams([stream]);
272276
}

test/widgets/emoji_reaction_test.dart

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,27 @@ void main() {
227227
}
228228
}
229229
}
230+
231+
testWidgets('show "Muted user" label for muted reactors', (tester) async {
232+
final user1 = eg.user(userId: 1, fullName: 'User 1');
233+
final user2 = eg.user(userId: 2, fullName: 'User 2');
234+
235+
await prepare();
236+
await store.addUsers([user1, user2]);
237+
await store.setMutedUsers([user1.userId]);
238+
await setupChipsInBox(tester,
239+
reactions: [
240+
Reaction.fromJson({'emoji_name': '+1', 'emoji_code': '1f44d', 'reaction_type': 'unicode_emoji', 'user_id': user1.userId}),
241+
Reaction.fromJson({'emoji_name': '+1', 'emoji_code': '1f44d', 'reaction_type': 'unicode_emoji', 'user_id': user2.userId}),
242+
]);
243+
244+
final reactionChipFinder = find.byType(ReactionChip);
245+
check(reactionChipFinder).findsOne();
246+
check(find.descendant(
247+
of: reactionChipFinder,
248+
matching: find.text('Muted user, User 2')
249+
)).findsOne();
250+
});
230251
});
231252

232253
testWidgets('Smoke test for light/dark/lerped', (tester) async {

0 commit comments

Comments
 (0)