Skip to content

Commit b45ec95

Browse files
committed
autocomplete: Introduce a field for sorted users
This field will be used to maintain a list of sorted users based on the most relevant autocomplete criteria in the upcoming commits. This commit also removes the retry logic for an in-progress query computation as there is no longer the cause that would necessitate such a retry.
1 parent f39aecd commit b45ec95

File tree

2 files changed

+33
-35
lines changed

2 files changed

+33
-35
lines changed

lib/model/autocomplete.dart

+19-19
Original file line numberDiff line numberDiff line change
@@ -164,17 +164,29 @@ class AutocompleteViewManager {
164164
/// * When the object will no longer be used, call [dispose] to free
165165
/// resources on the [PerAccountStore].
166166
class MentionAutocompleteView extends ChangeNotifier {
167-
MentionAutocompleteView._({required this.store, required this.narrow});
167+
MentionAutocompleteView._({
168+
required this.store,
169+
required this.narrow,
170+
required this.sortedUsers,
171+
});
168172

169173
factory MentionAutocompleteView.init({
170174
required PerAccountStore store,
171175
required Narrow narrow,
172176
}) {
173-
final view = MentionAutocompleteView._(store: store, narrow: narrow);
177+
final view = MentionAutocompleteView._(
178+
store: store,
179+
narrow: narrow,
180+
sortedUsers: _usersByRelevance(store: store),
181+
);
174182
store.autocompleteViewManager.registerMentionAutocomplete(view);
175183
return view;
176184
}
177185

186+
static List<User> _usersByRelevance({required PerAccountStore store}) {
187+
return store.users.values.toList(); // TODO(#228): sort for most relevant first
188+
}
189+
178190
@override
179191
void dispose() {
180192
store.autocompleteViewManager.unregisterMentionAutocomplete(this);
@@ -186,6 +198,7 @@ class MentionAutocompleteView extends ChangeNotifier {
186198

187199
final PerAccountStore store;
188200
final Narrow narrow;
201+
final List<User> sortedUsers;
189202

190203
MentionAutocompleteQuery? get query => _query;
191204
MentionAutocompleteQuery? _query;
@@ -209,18 +222,7 @@ class MentionAutocompleteView extends ChangeNotifier {
209222
List<MentionAutocompleteResult> _results = [];
210223

211224
Future<void> _startSearch(MentionAutocompleteQuery query) async {
212-
List<MentionAutocompleteResult>? newResults;
213-
214-
while (true) {
215-
try {
216-
newResults = await _computeResults(query);
217-
break;
218-
} on ConcurrentModificationError {
219-
// Retry
220-
// TODO backoff?
221-
}
222-
}
223-
225+
final newResults = await _computeResults(query);
224226
if (newResults == null) {
225227
// Query was old; new search is in progress. Or, no listeners to notify.
226228
return;
@@ -232,9 +234,7 @@ class MentionAutocompleteView extends ChangeNotifier {
232234

233235
Future<List<MentionAutocompleteResult>?> _computeResults(MentionAutocompleteQuery query) async {
234236
final List<MentionAutocompleteResult> results = [];
235-
final Iterable<User> users = store.users.values;
236-
237-
final iterator = users.iterator;
237+
final iterator = sortedUsers.iterator;
238238
bool isDone = false;
239239
while (!isDone) {
240240
// CPU perf: End this task; enqueue a new one for resuming this work
@@ -245,7 +245,7 @@ class MentionAutocompleteView extends ChangeNotifier {
245245
}
246246

247247
for (int i = 0; i < 1000; i++) {
248-
if (!iterator.moveNext()) { // Can throw ConcurrentModificationError
248+
if (!iterator.moveNext()) {
249249
isDone = true;
250250
break;
251251
}
@@ -256,7 +256,7 @@ class MentionAutocompleteView extends ChangeNotifier {
256256
}
257257
}
258258
}
259-
return results; // TODO(#228) sort for most relevant first
259+
return results;
260260
}
261261
}
262262

test/model/autocomplete_test.dart

+14-16
Original file line numberDiff line numberDiff line change
@@ -274,37 +274,35 @@ void main() {
274274
}
275275
});
276276

277-
test('MentionAutocompleteView mutating store.users while in progress causes retry', () async {
277+
test('MentionAutocompleteView mutating store.users while in progress does not '
278+
'interrupt the current query results', () async {
278279
const narrow = CombinedFeedNarrow();
279280
final store = eg.store();
280-
for (int i = 0; i < 1500; i++) {
281+
for (int i = 0; i < 2500; i++) {
281282
await store.addUser(eg.user(userId: i, email: 'user$i@example.com', fullName: 'User $i'));
282283
}
283284
final view = MentionAutocompleteView.init(store: store, narrow: narrow);
284285

285286
bool done = false;
286287
view.addListener(() { done = true; });
287-
view.query = MentionAutocompleteQuery('User 10000');
288+
view.query = MentionAutocompleteQuery('User 110');
288289

289290
await Future(() {});
290291
check(done).isFalse();
291-
await store.addUser(eg.user(userId: 10000, email: '[email protected]', fullName: 'User 10000'));
292-
await Future(() {});
293-
check(done).isFalse();
292+
await store.addUser(eg.user(userId: 11000, email: '[email protected]', fullName: 'User 11000'));
294293
await Future(() {});
295294
check(done).isFalse();
296-
await Future(() {});
297-
check(done).isTrue();
298-
check(view.results).single
299-
.isA<UserMentionAutocompleteResult>()
300-
.userId.equals(10000);
301-
// new result sticks; no "zombie" result from `store.users` pre-mutation
302-
for (int i = 0; i < 10; i++) { // for good measure
295+
while (!done) {
303296
await Future(() {});
304-
check(view.results).single
305-
.isA<UserMentionAutocompleteResult>()
306-
.userId.equals(10000);
307297
}
298+
check(done).isTrue();
299+
final results = view.results
300+
.map((e) => (e as UserMentionAutocompleteResult).userId);
301+
check(results)
302+
..contains(110)
303+
..contains(1100)
304+
// Does not include the newly-added user as we finish the query with stale users.
305+
..not((results) => results.contains(11000));
308306
});
309307

310308
group('MentionAutocompleteQuery.testUser', () {

0 commit comments

Comments
 (0)