Skip to content

Commit 3d7bfdb

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 b8a3dce commit 3d7bfdb

File tree

2 files changed

+139
-11
lines changed

2 files changed

+139
-11
lines changed

lib/widgets/recent_dm_conversations.dart

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -104,23 +104,31 @@ 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(children: [
113+
TextSpan(text: store.selfUser.fullName),
114+
UserStatusEmoji.asWidgetSpan(userId: store.selfUserId,
115+
fontSize: 17, textScaler: MediaQuery.textScalerOf(context)),
116+
]);
113117
avatar = AvatarImage(userId: store.selfUserId, size: _avatarSize);
114118
case [var otherUserId]:
115-
title = store.userDisplayName(otherUserId);
119+
title = TextSpan(children: [
120+
TextSpan(text: store.userDisplayName(otherUserId)),
121+
UserStatusEmoji.asWidgetSpan(userId: otherUserId,
122+
fontSize: 17, textScaler: MediaQuery.textScalerOf(context)),
123+
]);
116124
avatar = AvatarImage(userId: otherUserId, size: _avatarSize);
117125
userIdForPresence = otherUserId;
118126
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(', ');
127+
title = TextSpan(
128+
// TODO(i18n): List formatting, like you can do in JavaScript:
129+
// new Intl.ListFormat('ja').format(['Chris', 'Greg', 'Alya'])
130+
// // 'Chris、Greg、Alya'
131+
text: narrow.otherRecipientIds.map(store.userDisplayName).join(', '));
124132
avatar = ColoredBox(color: designVariables.avatarPlaceholderBg,
125133
child: Center(
126134
child: Icon(color: designVariables.avatarPlaceholderIcon,
@@ -148,7 +156,7 @@ class RecentDmConversationsItem extends StatelessWidget {
148156
const SizedBox(width: 8),
149157
Expanded(child: Padding(
150158
padding: const EdgeInsets.symmetric(vertical: 4),
151-
child: Text(
159+
child: Text.rich(
152160
style: TextStyle(
153161
fontSize: 17,
154162
height: (20 / 17),

test/widgets/recent_dm_conversations_test.dart

Lines changed: 122 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@ 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';
12+
import 'package:zulip/widgets/emoji.dart';
1013
import 'package:zulip/widgets/home.dart';
1114
import 'package:zulip/widgets/icons.dart';
1215
import 'package:zulip/widgets/message_list.dart';
@@ -24,6 +27,8 @@ import 'message_list_checks.dart';
2427
import 'page_checks.dart';
2528
import 'test_app.dart';
2629

30+
late PerAccountStore store;
31+
2732
Future<void> setupPage(WidgetTester tester, {
2833
required List<DmMessage> dmMessages,
2934
required List<User> users,
@@ -34,7 +39,7 @@ Future<void> setupPage(WidgetTester tester, {
3439
addTearDown(testBinding.reset);
3540

3641
await testBinding.globalStore.add(eg.selfAccount, eg.initialSnapshot());
37-
final store = await testBinding.globalStore.perAccount(eg.selfAccount.id);
42+
store = await testBinding.globalStore.perAccount(eg.selfAccount.id);
3843

3944
await store.addUser(eg.selfUser);
4045
for (final user in users) {
@@ -176,7 +181,7 @@ void main() {
176181
// TODO(#232): syntax like `check(find(…), findsOneWidget)`
177182
final widget = tester.widget(find.descendant(
178183
of: find.byType(RecentDmConversationsItem),
179-
matching: find.text(expectedText),
184+
matching: find.textContaining(expectedText),
180185
));
181186
if (expectedLines != null) {
182187
final renderObject = tester.renderObject<RenderParagraph>(find.byWidget(widget));
@@ -186,6 +191,23 @@ void main() {
186191
}
187192
}
188193

194+
void checkStatusEmoji(WidgetTester tester, {required bool isPresent}) {
195+
final statusEmojiFinder = find.ancestor(
196+
of: find.byType(UnicodeEmojiWidget),
197+
matching: find.byType(UserStatusEmoji));
198+
if (isPresent) {
199+
check(statusEmojiFinder).findsOne();
200+
check(tester.firstWidget<UserStatusEmoji>(statusEmojiFinder)
201+
.neverAnimate).isTrue();
202+
203+
final itemFinder = find.ancestor(of: statusEmojiFinder,
204+
matching: find.byType(RecentDmConversationsItem));
205+
check(itemFinder).findsOne();
206+
} else {
207+
check(statusEmojiFinder).findsNothing();
208+
}
209+
}
210+
189211
Future<void> markMessageAsRead(WidgetTester tester, Message message) async {
190212
final store = await testBinding.globalStore.perAccount(eg.selfAccount.id);
191213
await store.handleEvent(UpdateMessageFlagsAddEvent(
@@ -231,6 +253,45 @@ void main() {
231253
checkTitle(tester, name, 2);
232254
});
233255

256+
group('User status', () {
257+
testWidgets('status emoji & text are set -> emoji is displayed, text is not', (tester) async {
258+
final message = eg.dmMessage(from: eg.selfUser, to: []);
259+
await setupPage(tester, dmMessages: [message], users: []);
260+
await store.changeUserStatuses({
261+
eg.selfUser.userId: UserStatusChange(
262+
text: OptionSome('Busy'),
263+
emoji: OptionSome(StatusEmoji(emojiName: 'working_on_it',
264+
emojiCode: '1f6e0', reactionType: ReactionType.unicodeEmoji))),
265+
});
266+
await tester.pump();
267+
268+
checkStatusEmoji(tester, isPresent: true);
269+
check(find.descendant(of: find.byType(RecentDmConversationsItem),
270+
matching: find.text('Busy'))).findsNothing();
271+
});
272+
273+
testWidgets('status emoji is not set, text is set -> none of them is displayed', (tester) async {
274+
final message = eg.dmMessage(from: eg.selfUser, to: []);
275+
await setupPage(tester, dmMessages: [message], users: []);
276+
await store.changeUserStatuses({
277+
eg.selfUser.userId: UserStatusChange(
278+
text: OptionSome('Busy'),
279+
emoji: OptionNone()),
280+
});
281+
await tester.pump();
282+
283+
checkStatusEmoji(tester, isPresent: false);
284+
check(find.descendant(of: find.byType(RecentDmConversationsItem),
285+
matching: find.text('Busy'))).findsNothing();
286+
});
287+
288+
testWidgets('status is not set -> emoji is not displayed', (tester) async {
289+
final message = eg.dmMessage(from: eg.selfUser, to: []);
290+
await setupPage(tester, dmMessages: [message], users: []);
291+
checkStatusEmoji(tester, isPresent: false);
292+
});
293+
});
294+
234295
testWidgets('unread counts', (tester) async {
235296
final message = eg.dmMessage(from: eg.selfUser, to: []);
236297
await setupPage(tester, users: [], dmMessages: [message]);
@@ -291,6 +352,48 @@ void main() {
291352
checkTitle(tester, user.fullName, 2);
292353
});
293354

355+
group('User status', () {
356+
testWidgets('status emoji & text are set -> emoji is displayed, text is not', (tester) async {
357+
final user = eg.user();
358+
final message = eg.dmMessage(from: eg.selfUser, to: [user]);
359+
await setupPage(tester, users: [user], dmMessages: [message]);
360+
await store.changeUserStatuses({
361+
user.userId: UserStatusChange(
362+
text: OptionSome('Busy'),
363+
emoji: OptionSome(StatusEmoji(emojiName: 'working_on_it',
364+
emojiCode: '1f6e0', reactionType: ReactionType.unicodeEmoji))),
365+
});
366+
await tester.pump();
367+
368+
checkStatusEmoji(tester, isPresent: true);
369+
check(find.descendant(of: find.byType(RecentDmConversationsItem),
370+
matching: find.text('Busy'))).findsNothing();
371+
});
372+
373+
testWidgets('status emoji is not set, text is set -> none of them is displayed', (tester) async {
374+
final user = eg.user();
375+
final message = eg.dmMessage(from: eg.selfUser, to: [user]);
376+
await setupPage(tester, users: [user], dmMessages: [message]);
377+
await store.changeUserStatuses({
378+
user.userId: UserStatusChange(
379+
text: OptionSome('Busy'),
380+
emoji: OptionNone()),
381+
});
382+
await tester.pump();
383+
384+
checkStatusEmoji(tester, isPresent: false);
385+
check(find.descendant(of: find.byType(RecentDmConversationsItem),
386+
matching: find.text('Busy'))).findsNothing();
387+
});
388+
389+
testWidgets('status is not set -> emoji is not displayed', (tester) async {
390+
final user = eg.user();
391+
final message = eg.dmMessage(from: eg.selfUser, to: [user]);
392+
await setupPage(tester, users: [user], dmMessages: [message]);
393+
checkStatusEmoji(tester, isPresent: false);
394+
});
395+
});
396+
294397
testWidgets('unread counts', (tester) async {
295398
final message = eg.dmMessage(from: eg.otherUser, to: [eg.selfUser]);
296399
await setupPage(tester, users: [], dmMessages: [message]);
@@ -379,6 +482,23 @@ void main() {
379482
checkTitle(tester, users.map((u) => u.fullName).join(', '), 2);
380483
});
381484

485+
testWidgets('status emoji & text are set -> none of them is displayed', (tester) async {
486+
final users = usersList(4);
487+
final message = eg.dmMessage(from: eg.selfUser, to: users);
488+
await setupPage(tester, users: users, dmMessages: [message]);
489+
await store.changeUserStatuses({
490+
users.first.userId: UserStatusChange(
491+
text: OptionSome('Busy'),
492+
emoji: OptionSome(StatusEmoji(emojiName: 'working_on_it',
493+
emojiCode: '1f6e0', reactionType: ReactionType.unicodeEmoji))),
494+
});
495+
await tester.pump();
496+
497+
checkStatusEmoji(tester, isPresent: false);
498+
check(find.descendant(of: find.byType(RecentDmConversationsItem),
499+
matching: find.text('Busy'))).findsNothing();
500+
});
501+
382502
testWidgets('unread counts', (tester) async {
383503
final message = eg.dmMessage(from: eg.thirdUser, to: [eg.selfUser, eg.otherUser]);
384504
await setupPage(tester, users: [], dmMessages: [message]);

0 commit comments

Comments
 (0)