Skip to content

Commit 9a9c794

Browse files
committed
new-dm: Show user status emoji
1 parent 3d7bfdb commit 9a9c794

File tree

2 files changed

+145
-28
lines changed

2 files changed

+145
-28
lines changed

lib/widgets/new_dm_sheet.dart

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,8 @@ class _SelectedUserChip extends StatelessWidget {
317317
fontSize: 16,
318318
height: 16 / 16,
319319
color: designVariables.labelMenuButton)))),
320+
UserStatusEmoji(userId: userId, size: 16,
321+
padding: EdgeInsetsDirectional.only(end: 4)),
320322
])));
321323
}
322324
}
@@ -415,7 +417,12 @@ class _NewDmUserListItem extends StatelessWidget {
415417
Avatar(userId: userId, size: 32, borderRadius: 3),
416418
SizedBox(width: 8),
417419
Expanded(
418-
child: Text(store.userDisplayName(userId),
420+
child: Text.rich(
421+
TextSpan(children: [
422+
TextSpan(text: store.userDisplayName(userId)),
423+
UserStatusEmoji.asWidgetSpan(userId: userId, fontSize: 17,
424+
textScaler: MediaQuery.textScalerOf(context)),
425+
]),
419426
style: TextStyle(
420427
fontSize: 17,
421428
height: 19 / 17,

test/widgets/new_dm_sheet_test.dart

Lines changed: 137 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@ import 'package:flutter/material.dart';
33
import 'package:flutter_checks/flutter_checks.dart';
44
import 'package:flutter_test/flutter_test.dart';
55
import 'package:zulip/api/model/model.dart';
6+
import 'package:zulip/basic.dart';
7+
import 'package:zulip/model/store.dart';
68
import 'package:zulip/widgets/app_bar.dart';
79
import 'package:zulip/widgets/compose_box.dart';
810
import 'package:zulip/widgets/content.dart';
11+
import 'package:zulip/widgets/emoji.dart';
912
import 'package:zulip/widgets/home.dart';
1013
import 'package:zulip/widgets/icons.dart';
1114
import 'package:zulip/widgets/new_dm_sheet.dart';
@@ -19,6 +22,8 @@ import '../model/test_store.dart';
1922
import '../test_navigation.dart';
2023
import 'test_app.dart';
2124

25+
late PerAccountStore store;
26+
2227
Future<void> setupSheet(WidgetTester tester, {
2328
required List<User> users,
2429
List<int>? mutedUserIds,
@@ -30,7 +35,7 @@ Future<void> setupSheet(WidgetTester tester, {
3035
..onPushed = (route, _) => lastPushedRoute = route;
3136

3237
await testBinding.globalStore.add(eg.selfAccount, eg.initialSnapshot());
33-
final store = await testBinding.globalStore.perAccount(eg.selfAccount.id);
38+
store = await testBinding.globalStore.perAccount(eg.selfAccount.id);
3439
await store.addUsers(users);
3540
if (mutedUserIds != null) {
3641
await store.setMutedUsers(mutedUserIds);
@@ -65,7 +70,8 @@ void main() {
6570
}
6671

6772
Finder findUserTile(User user) =>
68-
find.widgetWithText(InkWell, user.fullName).first;
73+
find.ancestor(of: find.textContaining(user.fullName),
74+
matching: find.byType(InkWell)).first;
6975

7076
Finder findUserChip(User user) {
7177
final findAvatar = find.byWidgetPredicate((widget) =>
@@ -120,23 +126,23 @@ void main() {
120126

121127
testWidgets('shows all non-muted users initially', (tester) async {
122128
await setupSheet(tester, users: testUsers, mutedUserIds: [mutedUser.userId]);
123-
check(find.text('Alice Anderson')).findsOne();
124-
check(find.text('Bob Brown')).findsOne();
125-
check(find.text('Charlie Carter')).findsOne();
129+
check(find.textContaining('Alice Anderson')).findsOne();
130+
check(find.textContaining('Bob Brown')).findsOne();
131+
check(find.textContaining('Charlie Carter')).findsOne();
126132

127133
check(find.byIcon(ZulipIcons.check_circle_unchecked)).findsExactly(3);
128134
check(find.byIcon(ZulipIcons.check_circle_checked)).findsNothing();
129-
check(find.text('Someone Muted')).findsNothing();
130-
check(find.text('Muted user')).findsNothing();
135+
check(find.textContaining('Someone Muted')).findsNothing();
136+
check(find.textContaining('Muted user')).findsNothing();
131137
});
132138

133139
testWidgets('shows filtered users based on search', (tester) async {
134140
await setupSheet(tester, users: testUsers);
135141
await tester.enterText(find.byType(TextField), 'Alice');
136142
await tester.pump();
137-
check(find.text('Alice Anderson')).findsOne();
138-
check(find.text('Charlie Carter')).findsNothing();
139-
check(find.text('Bob Brown')).findsNothing();
143+
check(find.textContaining('Alice Anderson')).findsOne();
144+
check(find.textContaining('Charlie Carter')).findsNothing();
145+
check(find.textContaining('Bob Brown')).findsNothing();
140146
});
141147

142148
// TODO test sorting by recent-DMs
@@ -146,43 +152,43 @@ void main() {
146152
await setupSheet(tester, users: testUsers);
147153
await tester.enterText(find.byType(TextField), 'alice');
148154
await tester.pump();
149-
check(find.text('Alice Anderson')).findsOne();
155+
check(find.textContaining('Alice Anderson')).findsOne();
150156

151157
await tester.enterText(find.byType(TextField), 'ALICE');
152158
await tester.pump();
153-
check(find.text('Alice Anderson')).findsOne();
159+
check(find.textContaining('Alice Anderson')).findsOne();
154160
});
155161

156162
testWidgets('partial name and last name search handling', (tester) async {
157163
await setupSheet(tester, users: testUsers);
158164

159165
await tester.enterText(find.byType(TextField), 'Ali');
160166
await tester.pump();
161-
check(find.text('Alice Anderson')).findsOne();
162-
check(find.text('Bob Brown')).findsNothing();
163-
check(find.text('Charlie Carter')).findsNothing();
167+
check(find.textContaining('Alice Anderson')).findsOne();
168+
check(find.textContaining('Bob Brown')).findsNothing();
169+
check(find.textContaining('Charlie Carter')).findsNothing();
164170

165171
await tester.enterText(find.byType(TextField), 'Anderson');
166172
await tester.pump();
167-
check(find.text('Alice Anderson')).findsOne();
168-
check(find.text('Charlie Carter')).findsNothing();
169-
check(find.text('Bob Brown')).findsNothing();
173+
check(find.textContaining('Alice Anderson')).findsOne();
174+
check(find.textContaining('Charlie Carter')).findsNothing();
175+
check(find.textContaining('Bob Brown')).findsNothing();
170176

171177
await tester.enterText(find.byType(TextField), 'son');
172178
await tester.pump();
173-
check(find.text('Alice Anderson')).findsOne();
174-
check(find.text('Charlie Carter')).findsNothing();
175-
check(find.text('Bob Brown')).findsNothing();
179+
check(find.textContaining('Alice Anderson')).findsOne();
180+
check(find.textContaining('Charlie Carter')).findsNothing();
181+
check(find.textContaining('Bob Brown')).findsNothing();
176182
});
177183

178184
testWidgets('shows empty state when no users match', (tester) async {
179185
await setupSheet(tester, users: testUsers);
180186
await tester.enterText(find.byType(TextField), 'Zebra');
181187
await tester.pump();
182-
check(find.text('No users found')).findsOne();
183-
check(find.text('Alice Anderson')).findsNothing();
184-
check(find.text('Bob Brown')).findsNothing();
185-
check(find.text('Charlie Carter')).findsNothing();
188+
check(find.textContaining('No users found')).findsOne();
189+
check(find.textContaining('Alice Anderson')).findsNothing();
190+
check(find.textContaining('Bob Brown')).findsNothing();
191+
check(find.textContaining('Charlie Carter')).findsNothing();
186192
});
187193

188194
testWidgets('search text clears when user is selected', (tester) async {
@@ -252,7 +258,7 @@ void main() {
252258
await tester.tap(findUserTile(eg.selfUser));
253259
await tester.pump();
254260
checkUserSelected(tester, eg.selfUser, true);
255-
check(find.text(eg.selfUser.fullName)).findsExactly(2);
261+
check(find.textContaining(eg.selfUser.fullName)).findsExactly(2);
256262

257263
await tester.tap(findUserTile(otherUser));
258264
await tester.pump();
@@ -264,7 +270,7 @@ void main() {
264270
final otherUser = eg.user(fullName: 'Other User');
265271
await setupSheet(tester, users: [eg.selfUser, otherUser]);
266272

267-
check(find.text(eg.selfUser.fullName)).findsOne();
273+
check(find.textContaining(eg.selfUser.fullName)).findsOne();
268274

269275
await tester.tap(findUserTile(otherUser));
270276
await tester.pump();
@@ -285,6 +291,110 @@ void main() {
285291
});
286292
});
287293

294+
group('User status', () {
295+
void checkTileStatusEmoji(WidgetTester tester, User user, {required bool isPresent}) {
296+
final statusEmojiFinder = find.ancestor(
297+
of: find.byType(UnicodeEmojiWidget),
298+
matching: find.byType(UserStatusEmoji));
299+
final tileStatusEmojiFinder = find.descendant(of: findUserTile(user),
300+
matching: statusEmojiFinder);
301+
302+
if (isPresent) {
303+
check(tester.firstWidget<UserStatusEmoji>(tileStatusEmojiFinder)
304+
.neverAnimate).isTrue();
305+
check(tileStatusEmojiFinder).findsOne();
306+
} else {
307+
check(tileStatusEmojiFinder).findsNothing();
308+
}
309+
}
310+
311+
void checkChipStatusEmoji(WidgetTester tester, User user, {required bool isPresent}) {
312+
final statusEmojiFinder = find.ancestor(
313+
of: find.byType(UnicodeEmojiWidget),
314+
matching: find.byType(UserStatusEmoji));
315+
final chipStatusEmojiFinder = find.descendant(of: findUserChip(user),
316+
matching: statusEmojiFinder);
317+
318+
if (isPresent) {
319+
check(tester.firstWidget<UserStatusEmoji>(chipStatusEmojiFinder)
320+
.neverAnimate).isTrue();
321+
check(chipStatusEmojiFinder).findsOne();
322+
} else {
323+
check(chipStatusEmojiFinder).findsNothing();
324+
}
325+
}
326+
327+
testWidgets('status emoji & text are set -> emoji is displayed, text is not', (tester) async {
328+
final user = eg.user();
329+
await setupSheet(tester, users: [user]);
330+
await store.changeUserStatuses({
331+
user.userId: UserStatusChange(
332+
text: OptionSome('Busy'),
333+
emoji: OptionSome(StatusEmoji(emojiName: 'working_on_it',
334+
emojiCode: '1f6e0', reactionType: ReactionType.unicodeEmoji))),
335+
});
336+
await tester.pump();
337+
338+
checkTileStatusEmoji(tester, user, isPresent: true);
339+
check(find.descendant(of: findUserTile(user),
340+
matching: find.textContaining('Busy'))).findsNothing();
341+
check(findUserChip(user)).findsNothing();
342+
343+
await tester.tap(findUserTile(user));
344+
await tester.pump();
345+
346+
checkTileStatusEmoji(tester, user, isPresent: true);
347+
check(find.descendant(of: findUserTile(user),
348+
matching: find.textContaining('Busy'))).findsNothing();
349+
check(findUserChip(user)).findsOne();
350+
checkChipStatusEmoji(tester, user, isPresent: true);
351+
check(find.descendant(of: findUserChip(user),
352+
matching: find.text('Busy'))).findsNothing();
353+
});
354+
355+
testWidgets('status emoji is not set, text is set -> none of them is displayed', (tester) async {
356+
final user = eg.user();
357+
await setupSheet(tester, users: [user]);
358+
await store.changeUserStatuses({
359+
user.userId: UserStatusChange(
360+
text: OptionSome('Busy'),
361+
emoji: OptionNone()),
362+
});
363+
await tester.pump();
364+
365+
checkTileStatusEmoji(tester, user, isPresent: false);
366+
check(find.descendant(of: findUserTile(user),
367+
matching: find.textContaining('Busy'))).findsNothing();
368+
check(findUserChip(user)).findsNothing();
369+
370+
await tester.tap(findUserTile(user));
371+
await tester.pump();
372+
373+
checkTileStatusEmoji(tester, user, isPresent: false);
374+
check(find.descendant(of: findUserTile(user),
375+
matching: find.textContaining('Busy'))).findsNothing();
376+
check(findUserChip(user)).findsOne();
377+
checkChipStatusEmoji(tester, user, isPresent: false);
378+
check(find.descendant(of: findUserChip(user),
379+
matching: find.text('Busy'))).findsNothing();
380+
});
381+
382+
testWidgets('status is not set -> emoji is not displayed', (tester) async {
383+
final user = eg.user();
384+
await setupSheet(tester, users: [user]);
385+
386+
checkTileStatusEmoji(tester, user, isPresent: false);
387+
check(findUserChip(user)).findsNothing();
388+
389+
await tester.tap(findUserTile(user));
390+
await tester.pump();
391+
392+
checkTileStatusEmoji(tester, user, isPresent: false);
393+
check(findUserChip(user)).findsOne();
394+
checkChipStatusEmoji(tester, user, isPresent: false);
395+
});
396+
});
397+
288398
group('navigation to DM Narrow', () {
289399
Future<void> runAndCheck(WidgetTester tester, {
290400
required List<User> users,

0 commit comments

Comments
 (0)