Skip to content

Commit 90e3683

Browse files
committed
recent-dms: Show user status emoji in recent DMs page
Status emojis are only shown for self-1:1 and 1:1 conversation items. They're ignored for group conversations as that's what the Web does.
1 parent 00088db commit 90e3683

File tree

2 files changed

+99
-11
lines changed

2 files changed

+99
-11
lines changed

lib/widgets/recent_dm_conversations.dart

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -104,23 +104,29 @@ class RecentDmConversationsItem extends StatelessWidget {
104104
final store = PerAccountStoreWidget.of(context);
105105
final designVariables = DesignVariables.of(context);
106106

107-
final String title;
107+
final InlineSpan title;
108108
final Widget avatar;
109109
int? userIdForPresence;
110110
switch (narrow.otherRecipientIds) { // TODO dedupe with DM items in [InboxPage]
111111
case []:
112-
title = store.selfUser.fullName;
112+
title = TextSpan(text: store.selfUser.fullName, children: [
113+
UserStatusEmoji.asWidgetSpan(userId: store.selfUserId,
114+
fontSize: 17, textScaler: MediaQuery.textScalerOf(context)),
115+
]);
113116
avatar = AvatarImage(userId: store.selfUserId, size: _avatarSize);
114117
case [var otherUserId]:
115-
title = store.userDisplayName(otherUserId);
118+
title = TextSpan(text: store.userDisplayName(otherUserId), children: [
119+
UserStatusEmoji.asWidgetSpan(userId: otherUserId,
120+
fontSize: 17, textScaler: MediaQuery.textScalerOf(context)),
121+
]);
116122
avatar = AvatarImage(userId: otherUserId, size: _avatarSize);
117123
userIdForPresence = otherUserId;
118124
default:
119-
// TODO(i18n): List formatting, like you can do in JavaScript:
120-
// new Intl.ListFormat('ja').format(['Chris', 'Greg', 'Alya'])
121-
// // 'ChrisGregAlya'
122-
title = narrow.otherRecipientIds.map(store.userDisplayName)
123-
.join(', ');
125+
title = TextSpan(
126+
// TODO(i18n): List formatting, like you can do in JavaScript:
127+
// new Intl.ListFormat('ja').format(['Chris', 'Greg', 'Alya'])
128+
// // 'Chris、Greg、Alya'
129+
text: narrow.otherRecipientIds.map(store.userDisplayName).join(', '));
124130
avatar = ColoredBox(color: designVariables.avatarPlaceholderBg,
125131
child: Center(
126132
child: Icon(color: designVariables.avatarPlaceholderIcon,
@@ -148,7 +154,7 @@ class RecentDmConversationsItem extends StatelessWidget {
148154
const SizedBox(width: 8),
149155
Expanded(child: Padding(
150156
padding: const EdgeInsets.symmetric(vertical: 4),
151-
child: Text(
157+
child: Text.rich(
152158
style: TextStyle(
153159
fontSize: 17,
154160
height: (20 / 17),

test/widgets/recent_dm_conversations_test.dart

Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import 'package:flutter_checks/flutter_checks.dart';
55
import 'package:flutter_test/flutter_test.dart';
66
import 'package:zulip/api/model/events.dart';
77
import 'package:zulip/api/model/model.dart';
8+
import 'package:zulip/basic.dart';
89
import 'package:zulip/model/narrow.dart';
10+
import 'package:zulip/model/store.dart';
911
import 'package:zulip/widgets/content.dart';
1012
import 'package:zulip/widgets/home.dart';
1113
import 'package:zulip/widgets/icons.dart';
@@ -24,6 +26,8 @@ import 'message_list_checks.dart';
2426
import 'page_checks.dart';
2527
import 'test_app.dart';
2628

29+
late PerAccountStore store;
30+
2731
Future<void> setupPage(WidgetTester tester, {
2832
required List<DmMessage> dmMessages,
2933
required List<User> users,
@@ -34,7 +38,7 @@ Future<void> setupPage(WidgetTester tester, {
3438
addTearDown(testBinding.reset);
3539

3640
await testBinding.globalStore.add(eg.selfAccount, eg.initialSnapshot());
37-
final store = await testBinding.globalStore.perAccount(eg.selfAccount.id);
41+
store = await testBinding.globalStore.perAccount(eg.selfAccount.id);
3842

3943
await store.addUser(eg.selfUser);
4044
for (final user in users) {
@@ -176,7 +180,7 @@ void main() {
176180
// TODO(#232): syntax like `check(find(…), findsOneWidget)`
177181
final widget = tester.widget(find.descendant(
178182
of: find.byType(RecentDmConversationsItem),
179-
matching: find.text(expectedText),
183+
matching: find.textContaining(expectedText),
180184
));
181185
if (expectedLines != null) {
182186
final renderObject = tester.renderObject<RenderParagraph>(find.byWidget(widget));
@@ -186,6 +190,16 @@ void main() {
186190
}
187191
}
188192

193+
void checkFindsStatusEmoji(WidgetTester tester, Finder emojiFinder) {
194+
final statusEmojiFinder = find.ancestor(of: emojiFinder,
195+
matching: find.byType(UserStatusEmoji));
196+
check(statusEmojiFinder).findsOne();
197+
check(tester.widget<UserStatusEmoji>(statusEmojiFinder)
198+
.neverAnimate).isTrue();
199+
check(find.ancestor(of: statusEmojiFinder,
200+
matching: find.byType(RecentDmConversationsItem))).findsOne();
201+
}
202+
189203
Future<void> markMessageAsRead(WidgetTester tester, Message message) async {
190204
final store = await testBinding.globalStore.perAccount(eg.selfAccount.id);
191205
await store.handleEvent(UpdateMessageFlagsAddEvent(
@@ -231,6 +245,31 @@ void main() {
231245
checkTitle(tester, name, 2);
232246
});
233247

248+
group('User status', () {
249+
testWidgets('status emoji & text are set -> emoji is displayed, text is not', (tester) async {
250+
final message = eg.dmMessage(from: eg.selfUser, to: []);
251+
await setupPage(tester, dmMessages: [message], users: []);
252+
await store.changeUserStatus(eg.selfUser.userId, UserStatusChange(
253+
text: OptionSome('Busy'),
254+
emoji: OptionSome(StatusEmoji(emojiName: 'working_on_it',
255+
emojiCode: '1f6e0', reactionType: ReactionType.unicodeEmoji))));
256+
await tester.pump();
257+
258+
checkFindsStatusEmoji(tester, find.text('\u{1f6e0}'));
259+
check(find.text('Busy')).findsNothing();
260+
});
261+
262+
testWidgets('status emoji is not set, text is set -> text is not displayed', (tester) async {
263+
final message = eg.dmMessage(from: eg.selfUser, to: []);
264+
await setupPage(tester, dmMessages: [message], users: []);
265+
await store.changeUserStatus(eg.selfUser.userId, UserStatusChange(
266+
text: OptionSome('Busy'), emoji: OptionNone()));
267+
await tester.pump();
268+
269+
check(find.text('Busy')).findsNothing();
270+
});
271+
});
272+
234273
testWidgets('unread counts', (tester) async {
235274
final message = eg.dmMessage(from: eg.selfUser, to: []);
236275
await setupPage(tester, users: [], dmMessages: [message]);
@@ -291,6 +330,33 @@ void main() {
291330
checkTitle(tester, user.fullName, 2);
292331
});
293332

333+
group('User status', () {
334+
testWidgets('status emoji & text are set -> emoji is displayed, text is not', (tester) async {
335+
final user = eg.user();
336+
final message = eg.dmMessage(from: eg.selfUser, to: [user]);
337+
await setupPage(tester, users: [user], dmMessages: [message]);
338+
await store.changeUserStatus(user.userId, UserStatusChange(
339+
text: OptionSome('Busy'),
340+
emoji: OptionSome(StatusEmoji(emojiName: 'working_on_it',
341+
emojiCode: '1f6e0', reactionType: ReactionType.unicodeEmoji))));
342+
await tester.pump();
343+
344+
checkFindsStatusEmoji(tester, find.text('\u{1f6e0}'));
345+
check(find.text('Busy')).findsNothing();
346+
});
347+
348+
testWidgets('status emoji is not set, text is set -> text is not displayed', (tester) async {
349+
final user = eg.user();
350+
final message = eg.dmMessage(from: eg.selfUser, to: [user]);
351+
await setupPage(tester, users: [user], dmMessages: [message]);
352+
await store.changeUserStatus(user.userId, UserStatusChange(
353+
text: OptionSome('Busy'), emoji: OptionNone()));
354+
await tester.pump();
355+
356+
check(find.text('Busy')).findsNothing();
357+
});
358+
});
359+
294360
testWidgets('unread counts', (tester) async {
295361
final message = eg.dmMessage(from: eg.otherUser, to: [eg.selfUser]);
296362
await setupPage(tester, users: [], dmMessages: [message]);
@@ -379,6 +445,22 @@ void main() {
379445
checkTitle(tester, users.map((u) => u.fullName).join(', '), 2);
380446
});
381447

448+
testWidgets('status emoji & text are set -> none of them is displayed', (tester) async {
449+
final users = usersList(4);
450+
final message = eg.dmMessage(from: eg.selfUser, to: users);
451+
await setupPage(tester, users: users, dmMessages: [message]);
452+
await store.changeUserStatus(users.first.userId, UserStatusChange(
453+
text: OptionSome('Busy'),
454+
emoji: OptionSome(StatusEmoji(emojiName: 'working_on_it',
455+
emojiCode: '1f6e0', reactionType: ReactionType.unicodeEmoji))));
456+
await tester.pump();
457+
458+
check(find.descendant(of: find.byType(RecentDmConversationsItem),
459+
matching: find.text('\u{1f6e0}'))).findsNothing();
460+
check(find.descendant(of: find.byType(RecentDmConversationsItem),
461+
matching: find.text('Busy'))).findsNothing();
462+
});
463+
382464
testWidgets('unread counts', (tester) async {
383465
final message = eg.dmMessage(from: eg.thirdUser, to: [eg.selfUser, eg.otherUser]);
384466
await setupPage(tester, users: [], dmMessages: [message]);

0 commit comments

Comments
 (0)