Skip to content

Commit 957338d

Browse files
committed
autocomplete: Add user avatars to user-mention autocompletes
Fixes: #227
1 parent f3aebc9 commit 957338d

File tree

2 files changed

+50
-16
lines changed

2 files changed

+50
-16
lines changed

lib/widgets/autocomplete.dart

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import 'package:flutter/material.dart';
22

3+
import 'content.dart';
34
import 'store.dart';
45
import '../model/autocomplete.dart';
56
import '../model/compose.dart';
@@ -119,19 +120,25 @@ class _ComposeAutocompleteState extends State<ComposeAutocomplete> with PerAccou
119120

120121
Widget _buildItem(BuildContext _, int index) {
121122
final option = _resultsToDisplay[index];
123+
Widget avatar;
122124
String label;
123125
switch (option) {
124126
case UserMentionAutocompleteResult(:var userId):
125-
// TODO(#227) avatar
127+
avatar = Avatar(userId: userId, size: 32, borderRadius: 3);
126128
label = PerAccountStoreWidget.of(context).users[userId]!.fullName;
127129
}
128130
return InkWell(
129131
onTap: () {
130132
_onTapOption(option);
131133
},
132134
child: Padding(
133-
padding: const EdgeInsets.all(16.0),
134-
child: Text(label)));
135+
padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
136+
child: Row(
137+
children: [
138+
avatar,
139+
const SizedBox(width: 8),
140+
Text(label),
141+
])));
135142
}
136143

137144
@override

test/widgets/autocomplete_test.dart

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,25 @@ import 'package:zulip/api/model/model.dart';
66
import 'package:zulip/api/route/messages.dart';
77
import 'package:zulip/model/compose.dart';
88
import 'package:zulip/model/narrow.dart';
9+
import 'package:zulip/model/store.dart';
910
import 'package:zulip/widgets/message_list.dart';
1011
import 'package:zulip/widgets/store.dart';
1112

1213
import '../api/fake_api.dart';
1314
import '../example_data.dart' as eg;
1415
import '../model/binding.dart';
1516
import '../model/test_store.dart';
17+
import 'content_test.dart';
1618

1719
/// Simulates loading a [MessageListPage] and tapping to focus the compose input.
1820
///
1921
/// Also adds [users] to the [PerAccountStore],
2022
/// so they can show up in autocomplete.
23+
///
24+
/// Also sets [debugNetworkImageHttpClientProvider] to return a constant image.
25+
///
26+
/// The caller must set [debugNetworkImageHttpClientProvider] back to null
27+
/// before the end of the test.
2128
Future<Finder> setupToComposeInput(WidgetTester tester, {
2229
required List<User> users,
2330
}) async {
@@ -39,6 +46,8 @@ Future<Finder> setupToComposeInput(WidgetTester tester, {
3946
messages: [message],
4047
).toJson());
4148

49+
prepareBoringImageHttpClient();
50+
4251
await tester.pumpWidget(
4352
MaterialApp(
4453
localizationsDelegates: ZulipLocalizations.localizationsDelegates,
@@ -65,10 +74,25 @@ void main() {
6574
TestZulipBinding.ensureInitialized();
6675

6776
group('ComposeAutocomplete', () {
77+
78+
Finder findNetworkImage(String url) {
79+
return find.byWidgetPredicate((widget) => switch(widget) {
80+
Image(image: NetworkImage(url: var imageUrl)) when imageUrl == url => true,
81+
_ => false,
82+
});
83+
}
84+
85+
void checkUserShown(User user, PerAccountStore store, {required bool expected}) {
86+
check(find.text(user.fullName).evaluate().length).equals(expected ? 1 : 0);
87+
final avatarFinder =
88+
findNetworkImage(store.tryResolveUrl(user.avatarUrl!).toString());
89+
check(avatarFinder.evaluate().length).equals(expected ? 1 : 0);
90+
}
91+
6892
testWidgets('options appear, disappear, and change correctly', (WidgetTester tester) async {
69-
final user1 = eg.user(userId: 1, fullName: 'User One');
70-
final user2 = eg.user(userId: 2, fullName: 'User Two');
71-
final user3 = eg.user(userId: 3, fullName: 'User Three');
93+
final user1 = eg.user(userId: 1, fullName: 'User One', avatarUrl: 'user1.png');
94+
final user2 = eg.user(userId: 2, fullName: 'User Two', avatarUrl: 'user2.png');
95+
final user3 = eg.user(userId: 3, fullName: 'User Three', avatarUrl: 'user3.png');
7296
final composeInputFinder = await setupToComposeInput(tester, users: [user1, user2, user3]);
7397
final store = await testBinding.globalStore.perAccount(eg.selfAccount.id);
7498

@@ -77,34 +101,37 @@ void main() {
77101
await tester.enterText(composeInputFinder, 'hello @user ');
78102
await tester.enterText(composeInputFinder, 'hello @user t');
79103
await tester.pumpAndSettle(); // async computation; options appear
104+
80105
// "User Two" and "User Three" appear, but not "User One"
81-
check(tester.widgetList(find.text('User One'))).isEmpty();
82-
tester.widget(find.text('User Two'));
83-
tester.widget(find.text('User Three'));
106+
checkUserShown(user1, store, expected: false);
107+
checkUserShown(user2, store, expected: true);
108+
checkUserShown(user3, store, expected: true);
84109

85110
// Finishing autocomplete updates compose box; causes options to disappear
86111
await tester.tap(find.text('User Three'));
87112
await tester.pump();
88113
check(tester.widget<TextField>(composeInputFinder).controller!.text)
89114
.contains(mention(user3, users: store.users));
90-
check(tester.widgetList(find.text('User One'))).isEmpty();
91-
check(tester.widgetList(find.text('User Two'))).isEmpty();
92-
check(tester.widgetList(find.text('User Three'))).isEmpty();
115+
checkUserShown(user1, store, expected: false);
116+
checkUserShown(user2, store, expected: false);
117+
checkUserShown(user3, store, expected: false);
93118

94119
// Then a new autocomplete intent brings up options again
95120
// TODO(#226): Remove this extra edit when this bug is fixed.
96121
await tester.enterText(composeInputFinder, 'hello @user tw');
97122
await tester.enterText(composeInputFinder, 'hello @user two');
98123
await tester.pumpAndSettle(); // async computation; options appear
99-
tester.widget(find.text('User Two'));
124+
checkUserShown(user2, store, expected: true);
100125

101126
// Removing autocomplete intent causes options to disappear
102127
// TODO(#226): Remove one of these edits when this bug is fixed.
103128
await tester.enterText(composeInputFinder, '');
104129
await tester.enterText(composeInputFinder, ' ');
105-
check(tester.widgetList(find.text('User One'))).isEmpty();
106-
check(tester.widgetList(find.text('User Two'))).isEmpty();
107-
check(tester.widgetList(find.text('User Three'))).isEmpty();
130+
checkUserShown(user1, store, expected: false);
131+
checkUserShown(user2, store, expected: false);
132+
checkUserShown(user3, store, expected: false);
133+
134+
debugNetworkImageHttpClientProvider = null;
108135
});
109136
});
110137
}

0 commit comments

Comments
 (0)