@@ -438,22 +438,22 @@ abstract class EmojiNode extends InlineContentNode {
438
438
}
439
439
440
440
class UnicodeEmojiNode extends EmojiNode {
441
- const UnicodeEmojiNode ({super .debugHtmlNode, required this .text });
441
+ const UnicodeEmojiNode ({super .debugHtmlNode, required this .unicode });
442
442
443
- final String text ;
443
+ final String unicode ;
444
444
445
445
@override
446
446
bool operator == (Object other) {
447
- return other is UnicodeEmojiNode && other.text == text ;
447
+ return other is UnicodeEmojiNode && other.unicode == unicode ;
448
448
}
449
449
450
450
@override
451
- int get hashCode => Object .hash ('UnicodeEmojiNode' , text );
451
+ int get hashCode => Object .hash ('UnicodeEmojiNode' , unicode );
452
452
453
453
@override
454
454
void debugFillProperties (DiagnosticPropertiesBuilder properties) {
455
455
super .debugFillProperties (properties);
456
- properties.add (StringProperty ('text ' , text ));
456
+ properties.add (StringProperty ('unicode ' , unicode ));
457
457
}
458
458
}
459
459
@@ -568,7 +568,19 @@ class _ZulipContentParser {
568
568
&& classes.length == 2
569
569
&& classes.contains ('emoji' )
570
570
&& classes.every (_emojiClassRegexp.hasMatch)) {
571
- return UnicodeEmojiNode (text: element.text, debugHtmlNode: debugHtmlNode);
571
+ // TODO: Check if fonts contain the glyph for the specific unicodes.
572
+ // Either by parsing the font and generating a list of
573
+ // unicodes that have a corresponding glyph.
574
+ // Or by checking if an emoji is present in an emoji set,
575
+ // eg. unicode v15 set of emojis.
576
+
577
+ final className = classes
578
+ .firstWhere ((className) => className.startsWith ('emoji-' ), orElse: () => '' )
579
+ .replaceFirst ('emoji-' , '' );
580
+ if (className.isEmpty) return unimplemented ();
581
+ final unicode = tryParseEmojiCodeToUnicode (className);
582
+ if (unicode == null ) return unimplemented ();
583
+ return UnicodeEmojiNode (unicode: unicode, debugHtmlNode: debugHtmlNode);
572
584
}
573
585
574
586
if (localName == 'img'
@@ -627,6 +639,27 @@ class _ZulipContentParser {
627
639
return ListNode (listStyle! , items, debugHtmlNode: debugHtmlNode);
628
640
}
629
641
642
+ // Ported from https://github.com/zulip/zulip-mobile/blob/c979530d6804db33310ed7d14a4ac62017432944/src/emoji/data.js#L108-L112
643
+ //
644
+ // Which was in turn ported from https://github.com/zulip/zulip/blob/63c9296d5339517450f79f176dc02d77b08020c8/zerver/models.py#L3235-L3242
645
+ // and that describes the encoding as follows:
646
+ //
647
+ // > * For Unicode emoji, [emoji_code is] a dash-separated hex encoding of
648
+ // > the sequence of Unicode codepoints that define this emoji in the
649
+ // > Unicode specification. For examples, see "non_qualified" or
650
+ // > "unified" in the following data, with "non_qualified" taking
651
+ // > precedence when both present:
652
+ // > https://raw.githubusercontent.com/iamcal/emoji-data/master/emoji_pretty.json
653
+ String ? tryParseEmojiCodeToUnicode (String code) {
654
+ try {
655
+ return String .fromCharCodes (code.split ('-' ).map ((hex) => int .parse (hex, radix: 16 )));
656
+ } on FormatException { // thrown by `int.parse`
657
+ return null ;
658
+ } on ArgumentError { // thrown by `String.fromCharCodes`
659
+ return null ;
660
+ }
661
+ }
662
+
630
663
BlockContentNode parseCodeBlock (dom.Element divElement) {
631
664
assert (_debugParserContext == _ParserContext .block);
632
665
final mainElement = () {
0 commit comments