Skip to content

Commit 6020458

Browse files
committed
Merge remote-tracking branch 'pr/1506'
2 parents f408879 + 076b689 commit 6020458

File tree

9 files changed

+276
-149
lines changed

9 files changed

+276
-149
lines changed

lib/model/emoji.dart

Lines changed: 43 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -115,13 +115,7 @@ mixin EmojiStore {
115115
///
116116
/// See description in the web code:
117117
/// https://github.com/zulip/zulip/blob/83a121c7e/web/shared/src/typeahead.ts#L3-L21
118-
// Someday this list may start varying rather than being hard-coded,
119-
// and then this will become a non-static member on EmojiStore.
120-
// For now, though, the fact it's constant is convenient when writing
121-
// tests of the logic that uses this data; so we guarantee it in the API.
122-
static Iterable<EmojiCandidate> get popularEmojiCandidates {
123-
return EmojiStoreImpl._popularCandidates;
124-
}
118+
Iterable<EmojiCandidate> popularEmojiCandidates();
125119

126120
Iterable<EmojiCandidate> allEmojiCandidates();
127121

@@ -218,36 +212,54 @@ class EmojiStoreImpl extends PerAccountStoreBase with EmojiStore {
218212
/// retrieving the data.
219213
Map<String, List<String>>? _serverEmojiData;
220214

221-
static final _popularCandidates = _generatePopularCandidates();
215+
List<EmojiCandidate>? _popularCandidates;
222216

223-
static List<EmojiCandidate> _generatePopularCandidates() {
224-
EmojiCandidate candidate(String emojiCode, String emojiUnicode,
225-
List<String> names) {
226-
final emojiName = names.removeAt(0);
227-
assert(emojiUnicode == tryParseEmojiCodeToUnicode(emojiCode));
217+
List<EmojiCandidate> _generatePopularCandidates() {
218+
EmojiCandidate candidate(String emojiCode, List<String> names) {
219+
final [emojiName, ...aliases] = names;
220+
final emojiUnicode = tryParseEmojiCodeToUnicode(emojiCode);
221+
assert(emojiUnicode != null);
228222
return EmojiCandidate(emojiType: ReactionType.unicodeEmoji,
229-
emojiCode: emojiCode, emojiName: emojiName, aliases: names,
223+
emojiCode: emojiCode, emojiName: emojiName, aliases: aliases,
230224
emojiDisplay: UnicodeEmojiDisplay(
231-
emojiName: emojiName, emojiUnicode: emojiUnicode));
225+
emojiName: emojiName, emojiUnicode: emojiUnicode!));
232226
}
233-
return [
234-
// This list should match web:
235-
// https://github.com/zulip/zulip/blob/83a121c7e/web/shared/src/typeahead.ts#L22-L29
236-
candidate('1f44d', '👍', ['+1', 'thumbs_up', 'like']),
237-
candidate('1f389', '🎉', ['tada']),
238-
candidate('1f642', '🙂', ['smile']),
239-
candidate( '2764', '❤', ['heart', 'love', 'love_you']),
240-
candidate('1f6e0', '🛠', ['working_on_it', 'hammer_and_wrench', 'tools']),
241-
candidate('1f419', '🐙', ['octopus']),
242-
];
227+
if (_serverEmojiData == null) return [];
228+
229+
final result = <EmojiCandidate>[];
230+
for (final emojiCode in _popularEmojiCodesList) {
231+
final names = _serverEmojiData![emojiCode];
232+
if (names == null) continue; // TODO(log)
233+
result.add(candidate(emojiCode, names));
234+
}
235+
return result;
243236
}
244237

245-
static final _popularEmojiCodes = (() {
246-
assert(_popularCandidates.every((c) =>
247-
c.emojiType == ReactionType.unicodeEmoji));
248-
return Set.of(_popularCandidates.map((c) => c.emojiCode));
238+
@override
239+
Iterable<EmojiCandidate> popularEmojiCandidates() {
240+
return _popularCandidates ??= _generatePopularCandidates();
241+
}
242+
243+
/// Codes for the popular emoji, in order; all are Unicode emoji.
244+
// This list should match web:
245+
// https://github.com/zulip/zulip/blob/9feba0f16/web/shared/src/typeahead.ts#L22-L29
246+
static final List<String> _popularEmojiCodesList = (() {
247+
String check(String emojiCode, String emojiUnicode) {
248+
assert(emojiUnicode == tryParseEmojiCodeToUnicode(emojiCode));
249+
return emojiCode;
250+
}
251+
return [
252+
check('1f44d', '👍'),
253+
check('1f389', '🎉'),
254+
check('1f642', '🙂'),
255+
check('2764', '❤'),
256+
check('1f6e0', '🛠'),
257+
check('1f419', '🐙'),
258+
];
249259
})();
250260

261+
static final Set<String> _popularEmojiCodes = Set.of(_popularEmojiCodesList);
262+
251263
static bool _isPopularEmoji(EmojiCandidate candidate) {
252264
return candidate.emojiType == ReactionType.unicodeEmoji
253265
&& _popularEmojiCodes.contains(candidate.emojiCode);
@@ -307,7 +319,7 @@ class EmojiStoreImpl extends PerAccountStoreBase with EmojiStore {
307319

308320
// Include the "popular" emoji, in their canonical order
309321
// relative to each other.
310-
results.addAll(_popularCandidates);
322+
results.addAll(popularEmojiCandidates());
311323

312324
final namesOverridden = {
313325
for (final emoji in activeRealmEmoji) emoji.name,
@@ -366,6 +378,7 @@ class EmojiStoreImpl extends PerAccountStoreBase with EmojiStore {
366378
@override
367379
void setServerEmojiData(ServerEmojiData data) {
368380
_serverEmojiData = data.codeToNames;
381+
_popularCandidates = null;
369382
_allEmojiCandidates = null;
370383
}
371384

lib/model/store.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,9 @@ class PerAccountStore extends PerAccountStoreBase with ChangeNotifier, EmojiStor
612612
notifyListeners();
613613
}
614614

615+
@override
616+
Iterable<EmojiCandidate> popularEmojiCandidates() => _emoji.popularEmojiCandidates();
617+
615618
@override
616619
Iterable<EmojiCandidate> allEmojiCandidates() => _emoji.allEmojiCandidates();
617620

lib/widgets/action_sheet.dart

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,8 @@ void showMessageActionSheet({required BuildContext context, required Message mes
575575
final pageContext = PageRoot.contextOf(context);
576576
final store = PerAccountStoreWidget.of(pageContext);
577577

578+
final popularEmojiLoaded = store.popularEmojiCandidates().isNotEmpty;
579+
578580
// The UI that's conditioned on this won't live-update during this appearance
579581
// of the action sheet (we avoid calling composeBoxControllerOf in a build
580582
// method; see its doc).
@@ -588,7 +590,8 @@ void showMessageActionSheet({required BuildContext context, required Message mes
588590
final showMarkAsUnreadButton = markAsUnreadSupported && isMessageRead;
589591

590592
final optionButtons = [
591-
ReactionButtons(message: message, pageContext: pageContext),
593+
if (popularEmojiLoaded)
594+
ReactionButtons(message: message, pageContext: pageContext),
592595
StarButton(message: message, pageContext: pageContext),
593596
if (isComposeBoxOffered)
594597
QuoteAndReplyButton(message: message, pageContext: pageContext),
@@ -718,11 +721,18 @@ class ReactionButtons extends StatelessWidget {
718721

719722
@override
720723
Widget build(BuildContext context) {
721-
assert(EmojiStore.popularEmojiCandidates.every(
724+
final store = PerAccountStoreWidget.of(pageContext);
725+
final popularEmojiCandidates = store.popularEmojiCandidates();
726+
assert(popularEmojiCandidates.every(
722727
(emoji) => emoji.emojiType == ReactionType.unicodeEmoji));
728+
// (if this is empty, the widget isn't built in the first place)
729+
assert(popularEmojiCandidates.isNotEmpty);
730+
// UI not designed to handle more than 6 popular emoji.
731+
// (We might have fewer if ServerEmojiData is lacking expected data,
732+
// but that looks fine in manual testing, even when there's just one.)
733+
assert(popularEmojiCandidates.length <= 6);
723734

724735
final zulipLocalizations = ZulipLocalizations.of(context);
725-
final store = PerAccountStoreWidget.of(pageContext);
726736
final designVariables = DesignVariables.of(context);
727737

728738
bool hasSelfVote(EmojiCandidate emoji) {
@@ -738,7 +748,7 @@ class ReactionButtons extends StatelessWidget {
738748
color: designVariables.contextMenuItemBg.withFadedAlpha(0.12)),
739749
child: Row(children: [
740750
Flexible(child: Row(spacing: 1, children: List.unmodifiable(
741-
EmojiStore.popularEmojiCandidates.mapIndexed((index, emoji) =>
751+
popularEmojiCandidates.mapIndexed((index, emoji) =>
742752
_buildButton(
743753
context: context,
744754
emoji: emoji,

test/api/route/realm_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ void main() {
2222
}
2323

2424
final fakeResult = ServerEmojiData(codeToNames: {
25-
'1f642': ['smile'],
25+
'1f642': ['slight_smile'],
2626
'1f34a': ['orange', 'tangerine', 'mandarin'],
2727
});
2828

test/example_data.dart

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,42 @@ GetServerSettingsResult serverSettings({
130130
);
131131
}
132132

133+
ServerEmojiData serverEmojiDataPopular = ServerEmojiData(codeToNames: {
134+
'1f44d': ['+1', 'thumbs_up', 'like'],
135+
'1f389': ['tada'],
136+
'1f642': ['slight_smile'],
137+
'2764': ['heart', 'love', 'love_you'],
138+
'1f6e0': ['working_on_it', 'hammer_and_wrench', 'tools'],
139+
'1f419': ['octopus'],
140+
});
141+
142+
ServerEmojiData serverEmojiDataPopularPlus(ServerEmojiData data) {
143+
final a = serverEmojiDataPopular;
144+
final b = data;
145+
final result = ServerEmojiData(
146+
codeToNames: {...a.codeToNames, ...b.codeToNames},
147+
);
148+
assert(
149+
result.codeToNames.length == a.codeToNames.length + b.codeToNames.length,
150+
'eg.serverEmojiDataPopularPlus called with data that collides with eg.serverEmojiDataPopular',
151+
);
152+
return result;
153+
}
154+
155+
/// Like [serverEmojiDataPopular], but with the legacy '1f642': ['smile']
156+
/// instead of '1f642': ['slight_smile']; see zulip/zulip@9feba0f16f.
157+
///
158+
/// zulip/zulip@9feba0f16f is a Server 11 commit.
159+
// TODO(server-11) can drop this
160+
ServerEmojiData serverEmojiDataPopularLegacy = ServerEmojiData(codeToNames: {
161+
'1f44d': ['+1', 'thumbs_up', 'like'],
162+
'1f389': ['tada'],
163+
'1f642': ['smile'],
164+
'2764': ['heart', 'love', 'love_you'],
165+
'1f6e0': ['working_on_it', 'hammer_and_wrench', 'tools'],
166+
'1f419': ['octopus'],
167+
});
168+
133169
RealmEmojiItem realmEmojiItem({
134170
required String emojiCode,
135171
required String emojiName,

0 commit comments

Comments
 (0)