Skip to content

Commit 919cf12

Browse files
sm-sayedignprice
authored andcommitted
autocomplete: Add user avatars to user-mention autocompletes
Fixes: #227
1 parent 4ab1a82 commit 919cf12

File tree

2 files changed

+51
-16
lines changed

2 files changed

+51
-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: 41 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,26 @@ 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
81+
=> true,
82+
_ => false,
83+
});
84+
}
85+
86+
void checkUserShown(User user, PerAccountStore store, {required bool expected}) {
87+
check(find.text(user.fullName).evaluate().length).equals(expected ? 1 : 0);
88+
final avatarFinder =
89+
findNetworkImage(store.tryResolveUrl(user.avatarUrl!).toString());
90+
check(avatarFinder.evaluate().length).equals(expected ? 1 : 0);
91+
}
92+
6893
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');
94+
final user1 = eg.user(userId: 1, fullName: 'User One', avatarUrl: 'user1.png');
95+
final user2 = eg.user(userId: 2, fullName: 'User Two', avatarUrl: 'user2.png');
96+
final user3 = eg.user(userId: 3, fullName: 'User Three', avatarUrl: 'user3.png');
7297
final composeInputFinder = await setupToComposeInput(tester, users: [user1, user2, user3]);
7398
final store = await testBinding.globalStore.perAccount(eg.selfAccount.id);
7499

@@ -77,34 +102,37 @@ void main() {
77102
await tester.enterText(composeInputFinder, 'hello @user ');
78103
await tester.enterText(composeInputFinder, 'hello @user t');
79104
await tester.pumpAndSettle(); // async computation; options appear
105+
80106
// "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'));
107+
checkUserShown(user1, store, expected: false);
108+
checkUserShown(user2, store, expected: true);
109+
checkUserShown(user3, store, expected: true);
84110

85111
// Finishing autocomplete updates compose box; causes options to disappear
86112
await tester.tap(find.text('User Three'));
87113
await tester.pump();
88114
check(tester.widget<TextField>(composeInputFinder).controller!.text)
89115
.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();
116+
checkUserShown(user1, store, expected: false);
117+
checkUserShown(user2, store, expected: false);
118+
checkUserShown(user3, store, expected: false);
93119

94120
// Then a new autocomplete intent brings up options again
95121
// TODO(#226): Remove this extra edit when this bug is fixed.
96122
await tester.enterText(composeInputFinder, 'hello @user tw');
97123
await tester.enterText(composeInputFinder, 'hello @user two');
98124
await tester.pumpAndSettle(); // async computation; options appear
99-
tester.widget(find.text('User Two'));
125+
checkUserShown(user2, store, expected: true);
100126

101127
// Removing autocomplete intent causes options to disappear
102128
// TODO(#226): Remove one of these edits when this bug is fixed.
103129
await tester.enterText(composeInputFinder, '');
104130
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();
131+
checkUserShown(user1, store, expected: false);
132+
checkUserShown(user2, store, expected: false);
133+
checkUserShown(user3, store, expected: false);
134+
135+
debugNetworkImageHttpClientProvider = null;
108136
});
109137
});
110138
}

0 commit comments

Comments
 (0)