Skip to content

Commit 1107261

Browse files
chrisbobbesm-sayedi
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.) - @-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 23413ab commit 1107261

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
@@ -983,17 +983,13 @@
983983
"@noEarlierMessages": {
984984
"description": "Text to show at the start of a message list if there are no earlier messages."
985985
},
986-
"mutedSender": "Muted sender",
987-
"@mutedSender": {
988-
"description": "Name for a muted user to display in message list."
989-
},
990986
"revealButtonLabel": "Reveal message for muted sender",
991987
"@revealButtonLabel": {
992988
"description": "Label for the button revealing hidden message from a muted sender in message list."
993989
},
994990
"mutedUser": "Muted user",
995991
"@mutedUser": {
996-
"description": "Name for a muted user to display all over the app."
992+
"description": "Text to display in place of a muted user's name."
997993
},
998994
"scrollToBottomTooltip": "Scroll to bottom",
999995
"@scrollToBottomTooltip": {

lib/generated/l10n/zulip_localizations.dart

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

1466-
/// Name for a muted user to display in message list.
1467-
///
1468-
/// In en, this message translates to:
1469-
/// **'Muted sender'**
1470-
String get mutedSender;
1471-
14721466
/// Label for the button revealing hidden message from a muted sender in message list.
14731467
///
14741468
/// In en, this message translates to:
14751469
/// **'Reveal message for muted sender'**
14761470
String get revealButtonLabel;
14771471

1478-
/// Name for a muted user to display all over the app.
1472+
/// Text to display in place of a muted user's name.
14791473
///
14801474
/// In en, this message translates to:
14811475
/// **'Muted user'**

lib/generated/l10n/zulip_localizations_ar.dart

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

799-
@override
800-
String get mutedSender => 'Muted sender';
801-
802799
@override
803800
String get revealButtonLabel => 'Reveal message for muted sender';
804801

lib/generated/l10n/zulip_localizations_de.dart

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

799-
@override
800-
String get mutedSender => 'Muted sender';
801-
802799
@override
803800
String get revealButtonLabel => 'Reveal message for muted sender';
804801

lib/generated/l10n/zulip_localizations_en.dart

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

799-
@override
800-
String get mutedSender => 'Muted sender';
801-
802799
@override
803800
String get revealButtonLabel => 'Reveal message for muted sender';
804801

lib/generated/l10n/zulip_localizations_ja.dart

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

799-
@override
800-
String get mutedSender => 'Muted sender';
801-
802799
@override
803800
String get revealButtonLabel => 'Reveal message for muted sender';
804801

lib/generated/l10n/zulip_localizations_nb.dart

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

799-
@override
800-
String get mutedSender => 'Muted sender';
801-
802799
@override
803800
String get revealButtonLabel => 'Reveal message for muted sender';
804801

lib/generated/l10n/zulip_localizations_pl.dart

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

810-
@override
811-
String get mutedSender => 'Muted sender';
812-
813810
@override
814811
String get revealButtonLabel => 'Reveal message for muted sender';
815812

lib/generated/l10n/zulip_localizations_ru.dart

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

813-
@override
814-
String get mutedSender => 'Muted sender';
815-
816813
@override
817814
String get revealButtonLabel => 'Reveal message for muted sender';
818815

lib/generated/l10n/zulip_localizations_sk.dart

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

801-
@override
802-
String get mutedSender => 'Muted sender';
803-
804801
@override
805802
String get revealButtonLabel => 'Reveal message for muted sender';
806803

lib/generated/l10n/zulip_localizations_uk.dart

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

813-
@override
814-
String get mutedSender => 'Muted sender';
815-
816813
@override
817814
String get revealButtonLabel => 'Reveal message for muted sender';
818815

lib/generated/l10n/zulip_localizations_zh.dart

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

799-
@override
800-
String get mutedSender => 'Muted sender';
801-
802799
@override
803800
String get revealButtonLabel => 'Reveal message for muted sender';
804801

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
@@ -860,8 +860,9 @@ class _FixedDestinationContentInput extends StatelessWidget {
860860
final store = PerAccountStoreWidget.of(context);
861861
final user = store.getUser(otherUserId);
862862
if (user == null) return zulipLocalizations.composeBoxGenericContentHint;
863+
// TODO write a test where the user is muted
863864
return zulipLocalizations.composeBoxDmContentHint(
864-
store.userDisplayName(otherUserId));
865+
store.userDisplayName(otherUserId, replaceIfMuted: false));
865866

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

lib/widgets/inbox.dart

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

390+
// TODO write a test where a/the recipient is muted
390391
final title = switch (narrow.otherRecipientIds) { // TODO dedupe with [RecentDmConversationsItem]
391392
[] => store.selfUser.fullName,
392393
[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)