Skip to content

Commit c8cf146

Browse files
committed
msglist: Add “(you)” indicator for self user in DM conversations
Add a localized “(you)” label beside the self user’s display name in direct-message conversation titles. This improves clarity when viewing DMs, especially in contexts where the participant list may otherwise be ambiguous. Update the RecentDmConversationsItem title logic so that: * In a self-DM (case []), append the localized youLabel to the self user’s name. * In a 1:1 DM, append the label when the other participant is the self user (edge case where user IDs match). * For multi-user DMs, retain the existing comma-joined list without a youLabel, preserving current behavior. Update the test helper checkTitle() to match the new rendering behavior. The helper now inspects the rendered Text widgets directly and matches on startsWith(expectedText), making it robust when the title contains WidgetSpans or the new youLabel. Preserve existing TODO comments and layout behavior. All tests pass locally.
1 parent 9296fb3 commit c8cf146

File tree

3 files changed

+142
-81
lines changed

3 files changed

+142
-81
lines changed

assets/l10n/app_en.arb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,10 @@
665665
"@unknownUserName": {
666666
"description": "Name placeholder to use for a user when we don't know their name."
667667
},
668+
"youLabel": "(you)",
669+
"@youLabel": {
670+
"description": "Label shown after the current user's display name in direct messages."
671+
},
668672
"dmsWithYourselfPageTitle": "DMs with yourself",
669673
"@dmsWithYourselfPageTitle": {
670674
"description": "Message list page title for a DM group that only includes yourself."

lib/widgets/recent_dm_conversations.dart

Lines changed: 53 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import 'theme.dart';
1414
import 'unread_count_badge.dart';
1515
import 'user.dart';
1616

17+
1718
typedef OnDmSelectCallback = void Function(DmNarrow narrow);
1819

1920
class RecentDmConversationsPageBody extends StatefulWidget {
@@ -179,31 +180,67 @@ class RecentDmConversationsItem extends StatelessWidget {
179180
int? userIdForPresence;
180181
switch (narrow.otherRecipientIds) { // TODO dedupe with DM items in [InboxPage]
181182
case []:
182-
title = TextSpan(text: store.selfUser.fullName, children: [
183+
final youLabel = ZulipLocalizations.of(context).youLabel;
184+
title = TextSpan(text: '${store.selfUser.fullName} $youLabel', children: [
183185
UserStatusEmoji.asWidgetSpan(userId: store.selfUserId,
184-
fontSize: 17, textScaler: MediaQuery.textScalerOf(context)),
186+
fontSize: 17, textScaler: MediaQuery.textScalerOf(context)),
185187
]);
186188
avatar = AvatarImage(userId: store.selfUserId, size: _avatarSize);
189+
break;
187190
case [var otherUserId]:
188-
title = TextSpan(text: store.userDisplayName(otherUserId), children: [
189-
UserStatusEmoji.asWidgetSpan(userId: otherUserId,
190-
fontSize: 17, textScaler: MediaQuery.textScalerOf(context)),
191-
]);
191+
final selfId = store.selfUserId;
192+
193+
if (otherUserId == selfId) {
194+
final youLabel = ZulipLocalizations.of(context).youLabel;
195+
title = TextSpan(
196+
text: '${store.userDisplayName(otherUserId)} $youLabel',
197+
children: [
198+
UserStatusEmoji.asWidgetSpan(
199+
userId: otherUserId,
200+
fontSize: 17,
201+
textScaler: MediaQuery.textScalerOf(context),
202+
),
203+
],
204+
);
205+
} else {
206+
title = TextSpan(
207+
text: store.userDisplayName(otherUserId),
208+
children: [
209+
UserStatusEmoji.asWidgetSpan(
210+
userId: otherUserId,
211+
fontSize: 17,
212+
textScaler: MediaQuery.textScalerOf(context),
213+
),
214+
],
215+
);
216+
}
217+
192218
avatar = AvatarImage(userId: otherUserId, size: _avatarSize);
193219
userIdForPresence = otherUserId;
220+
break;
194221
default:
195-
title = TextSpan(
196-
// TODO(i18n): List formatting, like you can do in JavaScript:
197-
// new Intl.ListFormat('ja').format(['Chris', 'Greg', 'Alya'])
198-
// // 'Chris、Greg、Alya'
199-
text: narrow.otherRecipientIds.map(store.userDisplayName).join(', '));
200-
avatar = ColoredBox(color: designVariables.avatarPlaceholderBg,
201-
child: Center(
202-
child: Icon(color: designVariables.avatarPlaceholderIcon,
203-
ZulipIcons.group_dm)));
222+
final names = narrow.otherRecipientIds
223+
.map((id) => store.userDisplayName(id))
224+
.toList();
225+
226+
title = TextSpan(text: names.join(', '));
227+
// TODO(i18n): List formatting, like you can do in JavaScript:
228+
// new Intl.ListFormat('ja').format(['Chris', 'Greg', 'Alya'])
229+
// // 'Chris、Greg、Alya'
230+
231+
avatar = ColoredBox(
232+
color: designVariables.avatarPlaceholderBg,
233+
child: const Center(child: Icon(ZulipIcons.group_dm)),
234+
);
235+
break;
236+
237+
238+
204239
}
205240

206-
// TODO(design) check if this is the right variable
241+
242+
243+
// TODO(design) check if this is the right variable
207244
final backgroundColor = designVariables.background;
208245
return Material(
209246
color: backgroundColor,

0 commit comments

Comments
 (0)