@@ -22,6 +22,7 @@ import sanitizeHtml from 'sanitize-html';
22
22
import cheerio from 'cheerio' ;
23
23
import classNames from 'classnames' ;
24
24
import EMOJIBASE_REGEX from 'emojibase-regex' ;
25
+ import { split } from 'lodash' ;
25
26
import katex from 'katex' ;
26
27
import { AllHtmlEntities } from 'html-entities' ;
27
28
import { IContent } from 'matrix-js-sdk/src/models/event' ;
@@ -402,6 +403,11 @@ export interface IOptsReturnString extends IOpts {
402
403
returnString : true ;
403
404
}
404
405
406
+ const emojiToHtmlSpan = ( emoji : string ) =>
407
+ `<span class='mx_EventTile_Emoji' title='${ unicodeToShortcode ( emoji ) } '>${ emoji } </span>` ;
408
+ const emojiToJsxSpan = ( emoji : string , key : number ) =>
409
+ < span key = { key } className = 'mx_EventTile_Emoji' title = { unicodeToShortcode ( emoji ) } > { emoji } </ span > ;
410
+
405
411
/**
406
412
* Wraps emojis in <span> to style them separately from the rest of message. Consecutive emojis (and modifiers) are wrapped
407
413
* in the same <span>.
@@ -411,34 +417,27 @@ export interface IOptsReturnString extends IOpts {
411
417
* and plain text for everything else
412
418
*/
413
419
function formatEmojis ( message : string , isHtmlMessage : boolean ) : ( JSX . Element | string ) [ ] {
414
- const emojiToSpan = isHtmlMessage ? ( emoji : string ) => `<span class='mx_EventTile_Emoji'>${ emoji } </span>` :
415
- ( emoji : string , key : number ) => < span key = { key } className = 'mx_EventTile_Emoji' > { emoji } </ span > ;
420
+ const emojiToSpan = isHtmlMessage ? emojiToHtmlSpan : emojiToJsxSpan ;
416
421
const result : ( JSX . Element | string ) [ ] = [ ] ;
417
422
let text = '' ;
418
- let emojis = '' ;
419
423
let key = 0 ;
420
- for ( const char of message ) {
421
- if ( mightContainEmoji ( char ) || ZWJ_REGEX . test ( char ) || char === '\ufe0f' ) {
424
+
425
+ // We use lodash's grapheme splitter to avoid breaking apart compound emojis
426
+ for ( const char of split ( message , '' ) ) {
427
+ if ( mightContainEmoji ( char ) ) {
422
428
if ( text ) {
423
429
result . push ( text ) ;
424
430
text = '' ;
425
431
}
426
- emojis += char ;
432
+ result . push ( emojiToSpan ( char , key ) ) ;
433
+ key ++ ;
427
434
} else {
428
- if ( emojis ) {
429
- result . push ( emojiToSpan ( emojis , key ) ) ;
430
- key ++ ;
431
- emojis = '' ;
432
- }
433
435
text += char ;
434
436
}
435
437
}
436
438
if ( text ) {
437
439
result . push ( text ) ;
438
440
}
439
- if ( emojis ) {
440
- result . push ( emojiToSpan ( emojis , key ) ) ;
441
- }
442
441
return result ;
443
442
}
444
443
@@ -574,7 +573,7 @@ export function bodyToHtml(content: IContent, highlights: string[], opts: IOpts
574
573
} ) ;
575
574
576
575
let emojiBodyElements : JSX . Element [ ] ;
577
- if ( ! isDisplayedWithHtml && bodyHasEmoji && ! emojiBody ) {
576
+ if ( ! isDisplayedWithHtml && bodyHasEmoji ) {
578
577
emojiBodyElements = formatEmojis ( strippedBody , false ) as JSX . Element [ ] ;
579
578
}
580
579
0 commit comments