Skip to content

Commit 9a6757b

Browse files
committed
autocomplete: Add "recent DM conversations" criterion
In @-mention autocomplete, users are suggested based on: 1. Recent DM conversations. Fixes part of: #228
1 parent e6b86e9 commit 9a6757b

File tree

2 files changed

+120
-2
lines changed

2 files changed

+120
-2
lines changed

lib/model/autocomplete.dart

+37-2
Original file line numberDiff line numberDiff line change
@@ -267,13 +267,48 @@ class MentionAutocompleteView extends ChangeNotifier {
267267

268268
List<User>? _sortedUsers;
269269

270-
List<User> sortByRelevance({required List<User> users}) {
270+
/// Determines which of the two users are more recent in DM conversations.
271+
///
272+
/// Returns a negative number if [userA] is more recent than [userB],
273+
/// returns a positive number if [userB] is more recent than [userA],
274+
/// and returns `0` if both [userA] and [userB] are equally recent
275+
/// or there is no DM exchanged with them whatsoever.
276+
int compareByDms(User userA, User userB) {
277+
final recentDms = store.recentDmConversationsView;
278+
final aLatestMessageId = recentDms.latestMessagesByRecipient[userA.userId] ?? -1;
279+
final bLatestMessageId = recentDms.latestMessagesByRecipient[userB.userId] ?? -1;
280+
281+
return bLatestMessageId.compareTo(aLatestMessageId);
282+
}
283+
284+
int compareByRelevance({
285+
required User userA,
286+
required User userB,
287+
}) {
288+
final dmPrecedence = compareByDms(userA, userB);
289+
return dmPrecedence;
290+
}
291+
292+
List<User> sortByRelevance({
293+
required List<User> users,
294+
required Narrow narrow,
295+
}) {
296+
switch (narrow) {
297+
case StreamNarrow():
298+
case TopicNarrow():
299+
case DmNarrow():
300+
users.sort((userA, userB) => compareByRelevance(
301+
userA: userA,
302+
userB: userB));
303+
case AllMessagesNarrow():
304+
// do nothing in this case for now
305+
}
271306
return users;
272307
}
273308

274309
void _sortUsers() {
275310
final users = store.users.values.toList();
276-
_sortedUsers = sortByRelevance(users: users);
311+
_sortedUsers = sortByRelevance(users: users, narrow: narrow);
277312
}
278313

279314
Future<List<MentionAutocompleteResult>?> _computeResults(MentionAutocompleteQuery query) async {

test/model/autocomplete_test.dart

+83
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ import 'package:checks/checks.dart';
55
import 'package:fake_async/fake_async.dart';
66
import 'package:flutter/cupertino.dart';
77
import 'package:test/scaffolding.dart';
8+
import 'package:zulip/api/model/initial_snapshot.dart';
89
import 'package:zulip/api/model/model.dart';
910
import 'package:zulip/model/autocomplete.dart';
1011
import 'package:zulip/model/narrow.dart';
12+
import 'package:zulip/model/store.dart';
1113
import 'package:zulip/widgets/compose_box.dart';
1214

1315
import '../example_data.dart' as eg;
@@ -349,4 +351,85 @@ void main() {
349351
doCheck('Four F', eg.user(fullName: 'Full Name Four Words'), false);
350352
});
351353
});
354+
355+
group('MentionAutocompleteView users results', () {
356+
late PerAccountStore store;
357+
late MentionAutocompleteView view;
358+
359+
void prepare({
360+
required List<User> users,
361+
required List<RecentDmConversation> dmConversations,
362+
required Narrow narrow,
363+
}) {
364+
store = eg.store(
365+
initialSnapshot: eg.initialSnapshot(
366+
recentPrivateConversations: dmConversations
367+
));
368+
for (final user in users) {
369+
store.addUser(user);
370+
}
371+
view = MentionAutocompleteView.init(store: store, narrow: narrow);
372+
}
373+
374+
test('compareByDms gives priority to user with DM exchanged more recently', () {
375+
prepare(
376+
users: [],
377+
dmConversations: [
378+
RecentDmConversation(userIds: [1], maxMessageId: 200),
379+
RecentDmConversation(userIds: [1, 2], maxMessageId: 100),
380+
],
381+
narrow: const AllMessagesNarrow(),
382+
);
383+
384+
final userA = eg.user(userId: 1);
385+
final userB = eg.user(userId: 2);
386+
final compareAB = view.compareByDms(userA, userB);
387+
check(compareAB).isNegative();
388+
});
389+
390+
test('autocomplete suggests relevant users in the following order: '
391+
'1. Users most recent in the DM conversations', () async {
392+
final users = [
393+
eg.user(userId: 1),
394+
eg.user(userId: 2),
395+
eg.user(userId: 3),
396+
eg.user(userId: 4),
397+
eg.user(userId: 5),
398+
];
399+
400+
final dmConversations = [
401+
RecentDmConversation(userIds: [4], maxMessageId: 300),
402+
RecentDmConversation(userIds: [1], maxMessageId: 200),
403+
RecentDmConversation(userIds: [1, 2], maxMessageId: 100),
404+
];
405+
406+
Future<void> checkResultsIn(Narrow narrow, {required List<int> expected}) async {
407+
prepare(users: users, dmConversations: dmConversations, narrow: narrow);
408+
409+
bool done = false;
410+
view.addListener(() { done = true; });
411+
view.query = MentionAutocompleteQuery('');
412+
await Future(() {});
413+
check(done).isTrue();
414+
final results = view.results
415+
.map((e) => (e as UserMentionAutocompleteResult).userId)
416+
.toList();
417+
check(results).deepEquals(expected);
418+
}
419+
420+
const streamNarrow = StreamNarrow(1);
421+
await checkResultsIn(streamNarrow, expected: [4, 1, 2, 3, 5]);
422+
423+
const topicNarrow = TopicNarrow(1, 'topic');
424+
await checkResultsIn(topicNarrow, expected: [4, 1, 2, 3, 5]);
425+
426+
final dmNarrow = DmNarrow(allRecipientIds: [eg.selfUser.userId], selfUserId: eg.selfUser.userId);
427+
await checkResultsIn(dmNarrow, expected: [4, 1, 2, 3, 5]);
428+
429+
const allMessagesNarrow = AllMessagesNarrow();
430+
// Results are in the original order as we do not sort them for
431+
// [AllMessagesNarrow] because we can not access autocomplete for now.
432+
await checkResultsIn(allMessagesNarrow, expected: [1, 2, 3, 4, 5]);
433+
});
434+
});
352435
}

0 commit comments

Comments
 (0)