Skip to content

Commit 6fc9e2a

Browse files
committed
autocomplete: Show user status emoji in user-mention autocomplete
1 parent 3187d37 commit 6fc9e2a

File tree

2 files changed

+59
-4
lines changed

2 files changed

+59
-4
lines changed

lib/widgets/autocomplete.dart

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,19 @@ class _MentionAutocompleteItem extends StatelessWidget {
314314
mainAxisSize: MainAxisSize.min,
315315
crossAxisAlignment: CrossAxisAlignment.start,
316316
children: [
317-
labelWidget,
317+
Row(
318+
children: [
319+
Flexible(child: labelWidget),
320+
if (option case UserMentionAutocompleteResult(:var userId))
321+
Padding(
322+
padding: const EdgeInsetsDirectional.only(start: 5.0),
323+
child: UserStatusEmoji(
324+
userId: userId,
325+
size: 18,
326+
notoColorEmojiTextSize: 15),
327+
)
328+
],
329+
),
318330
if (sublabelWidget != null) sublabelWidget,
319331
])),
320332
]));

test/widgets/autocomplete_test.dart

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import 'package:zulip/api/model/model.dart';
77
import 'package:zulip/api/route/messages.dart';
88
import 'package:zulip/api/route/channels.dart';
99
import 'package:zulip/api/route/realm.dart';
10+
import 'package:zulip/basic.dart';
1011
import 'package:zulip/model/compose.dart';
1112
import 'package:zulip/model/emoji.dart';
1213
import 'package:zulip/model/localizations.dart';
@@ -15,6 +16,7 @@ import 'package:zulip/model/store.dart';
1516
import 'package:zulip/model/typing_status.dart';
1617
import 'package:zulip/widgets/compose_box.dart';
1718
import 'package:zulip/widgets/content.dart';
19+
import 'package:zulip/widgets/emoji.dart';
1820
import 'package:zulip/widgets/message_list.dart';
1921

2022
import '../api/fake_api.dart';
@@ -36,6 +38,7 @@ import 'test_app.dart';
3638
/// before the end of the test.
3739
Future<Finder> setupToComposeInput(WidgetTester tester, {
3840
List<User> users = const [],
41+
List<(int userId, UserStatusChange change)>? userStatuses,
3942
Narrow? narrow,
4043
}) async {
4144
assert(narrow is ChannelNarrow? || narrow is SendableNarrow?);
@@ -47,6 +50,7 @@ Future<Finder> setupToComposeInput(WidgetTester tester, {
4750
final store = await testBinding.globalStore.perAccount(eg.selfAccount.id);
4851
await store.addUsers([eg.selfUser, eg.otherUser]);
4952
await store.addUsers(users);
53+
await store.changeUserStatuses(userStatuses ?? []);
5054
final connection = store.connection as FakeApiConnection;
5155

5256
narrow ??= DmNarrow(
@@ -152,9 +156,22 @@ void main() {
152156
Finder findAvatarImage(int userId) =>
153157
find.byWidgetPredicate((widget) => widget is AvatarImage && widget.userId == userId);
154158

155-
void checkUserShown(User user, {required bool expected}) {
156-
check(find.text(user.fullName)).findsExactly(expected ? 1 : 0);
157-
check(findAvatarImage(user.userId)).findsExactly(expected ? 1 : 0);
159+
void checkUserShown(User user, {required bool expected, bool withStatusEmoji = false}) {
160+
assert(expected || !withStatusEmoji);
161+
162+
final nameFinder = find.text(user.fullName);
163+
check(nameFinder).findsExactly(expected ? 1 : 0);
164+
165+
final avatarFinder = findAvatarImage(user.userId);
166+
check(avatarFinder).findsExactly(expected ? 1 : 0);
167+
168+
final statusEmojiFinder = find.ancestor(of: find.byType(UnicodeEmojiWidget),
169+
matching: find.byType(UserStatusEmoji));
170+
final rowFinder = find.ancestor(of: nameFinder,
171+
matching: find.ancestor(of: avatarFinder,
172+
matching: find.ancestor(of: statusEmojiFinder,
173+
matching: find.byType(Row))));
174+
check(rowFinder).findsExactly(expected && withStatusEmoji ? 1 : 0);
158175
}
159176

160177
testWidgets('user options appear, disappear, and change correctly', (tester) async {
@@ -202,6 +219,32 @@ void main() {
202219
debugNetworkImageHttpClientProvider = null;
203220
});
204221

222+
testWidgets('status emoji is set -> emoji is displayed', (tester) async {
223+
final user1 = eg.user(userId: 1, fullName: 'User One', avatarUrl: 'user1.png');
224+
final user2 = eg.user(userId: 2, fullName: 'User Two', avatarUrl: 'user2.png');
225+
final composeInputFinder = await setupToComposeInput(tester,
226+
users: [user1, user2], userStatuses: [
227+
(
228+
user1.userId,
229+
UserStatusChange(
230+
text: OptionSome('Busy'),
231+
emoji: OptionSome(StatusEmoji(emojiName: 'working_on_it',
232+
emojiCode: '1f6e0', reactionType: ReactionType.unicodeEmoji)))
233+
),
234+
]);
235+
236+
// Options are filtered correctly for query
237+
// // TODO(#226): Remove this extra edit when this bug is fixed.
238+
await tester.enterText(composeInputFinder, 'hello @u');
239+
await tester.enterText(composeInputFinder, 'hello @');
240+
await tester.pumpAndSettle(); // async computation; options appear
241+
242+
checkUserShown(user1, expected: true, withStatusEmoji: true);
243+
checkUserShown(user2, expected: true, withStatusEmoji: false);
244+
245+
debugNetworkImageHttpClientProvider = null;
246+
});
247+
205248
void checkWildcardShown(WildcardMentionOption wildcard, {required bool expected}) {
206249
check(find.text(wildcard.canonicalString)).findsExactly(expected ? 1 : 0);
207250
}

0 commit comments

Comments
 (0)