Skip to content

Commit 197ce5c

Browse files
committed
autocomplete: Show user status emoji in user-mention autocomplete
1 parent b4ccd4f commit 197ce5c

File tree

2 files changed

+71
-15
lines changed

2 files changed

+71
-15
lines changed

lib/widgets/autocomplete.dart

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -277,29 +277,35 @@ class MentionAutocompleteItem extends StatelessWidget {
277277

278278
Widget avatar;
279279
String label;
280+
Widget? emoji;
280281
String? sublabel;
281282
switch (option) {
282283
case UserMentionAutocompleteResult(:var userId):
283284
avatar = Avatar(userId: userId, size: 36, borderRadius: 4);
284285
label = store.userDisplayName(userId);
286+
emoji = UserStatusEmoji(userId: userId, size: 18,
287+
padding: const EdgeInsetsDirectional.only(start: 5.0));
285288
sublabel = store.userDisplayEmail(userId);
286289
case WildcardMentionAutocompleteResult(:var wildcardOption):
287290
avatar = SizedBox.square(dimension: 36,
288291
child: const Icon(ZulipIcons.three_person, size: 24));
289292
label = wildcardOption.canonicalString;
293+
emoji = null;
290294
sublabel = wildcardSublabel(wildcardOption, context: context, store: store);
291295
}
292296

293-
final labelWidget = Text(
294-
label,
295-
style: TextStyle(
296-
fontSize: 18,
297-
height: 20 / 18,
298-
color: designVariables.contextMenuItemLabel,
299-
).merge(weightVariableTextStyle(context,
300-
wght: sublabel == null ? 500 : 600)),
301-
overflow: TextOverflow.ellipsis,
302-
maxLines: 1);
297+
final labelWidget = Row(children: [
298+
Flexible(child: Text(label,
299+
style: TextStyle(
300+
fontSize: 18,
301+
height: 20 / 18,
302+
color: designVariables.contextMenuItemLabel,
303+
).merge(weightVariableTextStyle(context,
304+
wght: sublabel == null ? 500 : 600)),
305+
overflow: TextOverflow.ellipsis,
306+
maxLines: 1)),
307+
?emoji,
308+
]);
303309

304310
final sublabelWidget = sublabel == null ? null : Text(
305311
sublabel,
@@ -318,10 +324,7 @@ class MentionAutocompleteItem extends StatelessWidget {
318324
Expanded(child: Column(
319325
mainAxisSize: MainAxisSize.min,
320326
crossAxisAlignment: CrossAxisAlignment.start,
321-
children: [
322-
labelWidget,
323-
if (sublabelWidget != null) sublabelWidget,
324-
])),
327+
children: [labelWidget, ?sublabelWidget])),
325328
]));
326329
}
327330
}

test/widgets/autocomplete_test.dart

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@ 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';
1314
import 'package:zulip/model/narrow.dart';
1415
import 'package:zulip/model/store.dart';
1516
import 'package:zulip/model/typing_status.dart';
17+
import 'package:zulip/widgets/autocomplete.dart';
1618
import 'package:zulip/widgets/compose_box.dart';
1719
import 'package:zulip/widgets/content.dart';
1820
import 'package:zulip/widgets/message_list.dart';
@@ -25,6 +27,8 @@ import '../model/test_store.dart';
2527
import '../test_images.dart';
2628
import 'test_app.dart';
2729

30+
late PerAccountStore store;
31+
2832
/// Simulates loading a [MessageListPage] and tapping to focus the compose input.
2933
///
3034
/// Also adds [users] to the [PerAccountStore],
@@ -44,7 +48,7 @@ Future<Finder> setupToComposeInput(WidgetTester tester, {
4448

4549
addTearDown(testBinding.reset);
4650
await testBinding.globalStore.add(eg.selfAccount, eg.initialSnapshot());
47-
final store = await testBinding.globalStore.perAccount(eg.selfAccount.id);
51+
store = await testBinding.globalStore.perAccount(eg.selfAccount.id);
4852
await store.addUsers([eg.selfUser, eg.otherUser]);
4953
await store.addUsers(users);
5054
final connection = store.connection as FakeApiConnection;
@@ -202,6 +206,55 @@ void main() {
202206
debugNetworkImageHttpClientProvider = null;
203207
});
204208

209+
group('User status', () {
210+
void checkFindsStatusEmoji(WidgetTester tester, Finder emojiFinder) {
211+
final statusEmojiFinder = find.ancestor(of: emojiFinder,
212+
matching: find.byType(UserStatusEmoji));
213+
check(statusEmojiFinder).findsOne();
214+
check(tester.widget<UserStatusEmoji>(statusEmojiFinder)
215+
.neverAnimate).isTrue();
216+
check(find.ancestor(of: statusEmojiFinder,
217+
matching: find.byType(MentionAutocompleteItem))).findsOne();
218+
}
219+
220+
testWidgets('status emoji & text are set -> emoji is displayed, text is not', (tester) async {
221+
final user = eg.user(fullName: 'User');
222+
final composeInputFinder = await setupToComposeInput(tester, users: [user]);
223+
await store.changeUserStatus(user.userId, UserStatusChange(
224+
text: OptionSome('Busy'),
225+
emoji: OptionSome(StatusEmoji(emojiName: 'working_on_it',
226+
emojiCode: '1f6e0', reactionType: ReactionType.unicodeEmoji))));
227+
await tester.pump();
228+
229+
// // TODO(#226): Remove this extra edit when this bug is fixed.
230+
await tester.enterText(composeInputFinder, 'hello @u');
231+
await tester.enterText(composeInputFinder, 'hello @');
232+
await tester.pumpAndSettle(); // async computation; options appear
233+
234+
checkFindsStatusEmoji(tester, find.text('\u{1f6e0}'));
235+
check(find.text('Busy')).findsNothing();
236+
237+
debugNetworkImageHttpClientProvider = null;
238+
});
239+
240+
testWidgets('status emoji is not set, text is set -> text is not displayed', (tester) async {
241+
final user = eg.user(fullName: 'User');
242+
final composeInputFinder = await setupToComposeInput(tester, users: [user]);
243+
await store.changeUserStatus(user.userId, UserStatusChange(
244+
text: OptionSome('Busy'), emoji: OptionNone()));
245+
await tester.pump();
246+
247+
// // TODO(#226): Remove this extra edit when this bug is fixed.
248+
await tester.enterText(composeInputFinder, 'hello @u');
249+
await tester.enterText(composeInputFinder, 'hello @');
250+
await tester.pumpAndSettle(); // async computation; options appear
251+
252+
check(find.text('Busy')).findsNothing();
253+
254+
debugNetworkImageHttpClientProvider = null;
255+
});
256+
});
257+
205258
void checkWildcardShown(WildcardMentionOption wildcard, {required bool expected}) {
206259
check(find.text(wildcard.canonicalString)).findsExactly(expected ? 1 : 0);
207260
}

0 commit comments

Comments
 (0)