Skip to content

Commit b14ee1c

Browse files
committed
emoji: Order "popular" emoji canonically amongst themselves
As a bonus, this provides the popular emoji as candidates even when we haven't yet fetched the server's emoji data.
1 parent 9a8b362 commit b14ee1c

File tree

2 files changed

+96
-0
lines changed

2 files changed

+96
-0
lines changed

lib/model/emoji.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,12 +264,18 @@ class EmojiStoreImpl with EmojiStore {
264264
List<EmojiCandidate> _generateAllCandidates() {
265265
final results = <EmojiCandidate>[];
266266

267+
// Include the "popular" emoji, in their canonical order
268+
// relative to each other.
269+
results.addAll(_popularCandidates);
270+
267271
final namesOverridden = {
268272
for (final emoji in realmEmoji.values) emoji.name,
269273
'zulip',
270274
};
271275
// TODO(log) if _serverEmojiData missing
272276
for (final entry in (_serverEmojiData ?? {}).entries) {
277+
if (_popularEmojiCodes.contains(entry.key)) continue;
278+
273279
final allNames = entry.value;
274280
final String emojiName;
275281
final List<String>? aliases;

test/model/emoji_test.dart

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ void main() {
7878
});
7979
});
8080

81+
final popularCandidates = EmojiStore.popularEmojiCandidates;
82+
8183
Condition<Object?> isUnicodeCandidate(String? emojiCode, List<String>? names) {
8284
return (it_) {
8385
final it = it_.isA<EmojiCandidate>();
@@ -108,6 +110,9 @@ void main() {
108110
..aliases.isEmpty();
109111
}
110112

113+
List<Condition<Object?>> arePopularCandidates = popularCandidates.map(
114+
(c) => isUnicodeCandidate(c.emojiCode, null)).toList();
115+
111116
group('allEmojiCandidates', () {
112117
// TODO test emojiDisplay of candidates matches emojiDisplayFor
113118

@@ -123,6 +128,40 @@ void main() {
123128
return store;
124129
}
125130

131+
test('popular emoji appear even when no server emoji data', () {
132+
final store = prepare(unicodeEmoji: null);
133+
check(store.allEmojiCandidates()).deepEquals([
134+
...arePopularCandidates,
135+
isZulipCandidate(),
136+
]);
137+
});
138+
139+
test('popular emoji appear in their canonical order', () {
140+
// In the server's emoji data, have the popular emoji in a permuted order,
141+
// and interspersed with other emoji.
142+
final store = prepare(unicodeEmoji: {
143+
'1f603': ['smiley'],
144+
for (final candidate in popularCandidates.skip(3))
145+
candidate.emojiCode: [candidate.emojiName, ...candidate.aliases],
146+
'1f34a': ['orange', 'tangerine', 'mandarin'],
147+
for (final candidate in popularCandidates.take(3))
148+
candidate.emojiCode: [candidate.emojiName, ...candidate.aliases],
149+
'1f516': ['bookmark'],
150+
});
151+
// In the allEmojiCandidates result, the popular emoji come first
152+
// and are in their canonical order, even though the other Unicode emoji
153+
// are in the same order they were given in.
154+
check(store.allEmojiCandidates()).deepEquals([
155+
for (final candidate in popularCandidates)
156+
isUnicodeCandidate(candidate.emojiCode,
157+
[candidate.emojiName, ...candidate.aliases]),
158+
isUnicodeCandidate('1f603', ['smiley']),
159+
isUnicodeCandidate('1f34a', ['orange', 'tangerine', 'mandarin']),
160+
isUnicodeCandidate('1f516', ['bookmark']),
161+
isZulipCandidate(),
162+
]);
163+
});
164+
126165
test('realm emoji overrides Unicode emoji', () {
127166
final store = prepare(realmEmoji: {
128167
'1': eg.realmEmojiItem(emojiCode: '1', emojiName: 'smiley'),
@@ -131,6 +170,7 @@ void main() {
131170
'1f603': ['smiley'],
132171
});
133172
check(store.allEmojiCandidates()).deepEquals([
173+
...arePopularCandidates,
134174
isUnicodeCandidate('1f516', ['bookmark']),
135175
isRealmCandidate(emojiCode: '1', emojiName: 'smiley'),
136176
isZulipCandidate(),
@@ -144,6 +184,7 @@ void main() {
144184
'1f34a': ['orange', 'tangerine', 'mandarin'],
145185
});
146186
check(store.allEmojiCandidates()).deepEquals([
187+
...arePopularCandidates,
147188
isUnicodeCandidate('1f34a', ['orange', 'mandarin']),
148189
isRealmCandidate(emojiCode: '1', emojiName: 'tangerine'),
149190
isZulipCandidate(),
@@ -157,6 +198,7 @@ void main() {
157198
'1f34a': ['orange', 'tangerine', 'mandarin'],
158199
});
159200
check(store.allEmojiCandidates()).deepEquals([
201+
...arePopularCandidates,
160202
isUnicodeCandidate('1f34a', ['tangerine', 'mandarin']),
161203
isRealmCandidate(emojiCode: '1', emojiName: 'orange'),
162204
isZulipCandidate(),
@@ -166,13 +208,15 @@ void main() {
166208
test('updates on setServerEmojiData', () {
167209
final store = prepare();
168210
check(store.allEmojiCandidates()).deepEquals([
211+
...arePopularCandidates,
169212
isZulipCandidate(),
170213
]);
171214

172215
store.setServerEmojiData(ServerEmojiData(codeToNames: {
173216
'1f516': ['bookmark'],
174217
}));
175218
check(store.allEmojiCandidates()).deepEquals([
219+
...arePopularCandidates,
176220
isUnicodeCandidate('1f516', ['bookmark']),
177221
isZulipCandidate(),
178222
]);
@@ -181,13 +225,15 @@ void main() {
181225
test('updates on RealmEmojiUpdateEvent', () {
182226
final store = prepare();
183227
check(store.allEmojiCandidates()).deepEquals([
228+
...arePopularCandidates,
184229
isZulipCandidate(),
185230
]);
186231

187232
store.handleEvent(RealmEmojiUpdateEvent(id: 1, realmEmoji: {
188233
'1': eg.realmEmojiItem(emojiCode: '1', emojiName: 'happy'),
189234
}));
190235
check(store.allEmojiCandidates()).deepEquals([
236+
...arePopularCandidates,
191237
isRealmCandidate(emojiCode: '1', emojiName: 'happy'),
192238
isZulipCandidate(),
193239
]);
@@ -220,6 +266,9 @@ void main() {
220266
isZulipCandidate());
221267
}
222268

269+
List<Condition<Object?>> arePopularResults = popularCandidates.map(
270+
(c) => isUnicodeResult(emojiCode: c.emojiCode)).toList();
271+
223272
PerAccountStore prepare({
224273
Map<String, String> realmEmoji = const {},
225274
Map<String, List<String>>? unicodeEmoji,
@@ -245,6 +294,7 @@ void main() {
245294
await Future(() {});
246295
check(done).isTrue();
247296
check(view.results).deepEquals([
297+
...arePopularResults,
248298
isRealmResult(emojiName: 'happy'),
249299
isZulipResult(),
250300
isUnicodeResult(names: ['bookmark']),
@@ -286,6 +336,45 @@ void main() {
286336
return view.results;
287337
}
288338

339+
test('results preserve order of popular emoji within each rank', () async {
340+
// In other words, the sorting by rank is a stable sort.
341+
342+
// Full results list matches allEmojiCandidates.
343+
check(prepare().allEmojiCandidates())
344+
.deepEquals([...arePopularCandidates, isZulipCandidate()]);
345+
check(await resultsOf(''))
346+
.deepEquals([...arePopularResults, isZulipResult()]);
347+
348+
// Same list written out explicitly, for comparison with the cases below.
349+
check(await resultsOf('')).deepEquals([
350+
isUnicodeResult(names: ['+1', 'thumbs_up', 'like']),
351+
isUnicodeResult(names: ['tada']),
352+
isUnicodeResult(names: ['smile']),
353+
isUnicodeResult(names: ['heart', 'love', 'love_you']),
354+
isUnicodeResult(names: ['working_on_it', 'hammer_and_wrench', 'tools']),
355+
isUnicodeResult(names: ['octopus']),
356+
isZulipResult(),
357+
]);
358+
359+
check(await resultsOf('t')).deepEquals([
360+
// prefix
361+
isUnicodeResult(names: ['+1', 'thumbs_up', 'like']),
362+
isUnicodeResult(names: ['tada']),
363+
isUnicodeResult(names: ['working_on_it', 'hammer_and_wrench', 'tools']),
364+
// other
365+
isUnicodeResult(names: ['heart', 'love', 'love_you']),
366+
isUnicodeResult(names: ['octopus']),
367+
]);
368+
369+
check(await resultsOf('h')).deepEquals([
370+
// prefix
371+
isUnicodeResult(names: ['heart', 'love', 'love_you']),
372+
isUnicodeResult(names: ['working_on_it', 'hammer_and_wrench', 'tools']),
373+
// other
374+
isUnicodeResult(names: ['+1', 'thumbs_up', 'like']),
375+
]);
376+
});
377+
289378
test('results end-to-end', () async {
290379
// (See more detailed rank tests below, on EmojiAutocompleteQuery.)
291380

@@ -294,6 +383,7 @@ void main() {
294383

295384
// Empty query -> base ordering.
296385
check(await resultsOf('', unicodeEmoji: unicodeEmoji)).deepEquals([
386+
...arePopularResults,
297387
isZulipResult(),
298388
isUnicodeResult(names: ['notebook']),
299389
isUnicodeResult(names: ['bookmark']),

0 commit comments

Comments
 (0)