@@ -402,6 +402,46 @@ export interface IOptsReturnString extends IOpts {
402
402
returnString : true ;
403
403
}
404
404
405
+ /**
406
+ * Wraps emojis in <span> to style them separately from the rest of message. Consecutive emojis (and modifiers) are wrapped
407
+ * in the same <span>.
408
+ * @param {string } message the text to format
409
+ * @param {boolean } isHtmlMessage whether the message contains HTML
410
+ * @returns if isHtmlMessage is true, returns an array of strings, otherwise return an array of React Elements for emojis
411
+ * and plain text for everything else
412
+ */
413
+ 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 > ;
416
+ const result : ( JSX . Element | string ) [ ] = [ ] ;
417
+ let text = '' ;
418
+ let emojis = '' ;
419
+ let key = 0 ;
420
+ for ( const char of message ) {
421
+ if ( mightContainEmoji ( char ) || ZWJ_REGEX . test ( char ) || char === '\ufe0f' ) {
422
+ if ( text ) {
423
+ result . push ( text ) ;
424
+ text = '' ;
425
+ }
426
+ emojis += char ;
427
+ } else {
428
+ if ( emojis ) {
429
+ result . push ( emojiToSpan ( emojis , key ) ) ;
430
+ key ++ ;
431
+ emojis = '' ;
432
+ }
433
+ text += char ;
434
+ }
435
+ }
436
+ if ( text ) {
437
+ result . push ( text ) ;
438
+ }
439
+ if ( emojis ) {
440
+ result . push ( emojiToSpan ( emojis , key ) ) ;
441
+ }
442
+ return result ;
443
+ }
444
+
405
445
/* turn a matrix event body into html
406
446
*
407
447
* content: 'content' of the MatrixEvent
@@ -488,6 +528,9 @@ export function bodyToHtml(content: IContent, highlights: string[], opts: IOpts
488
528
} ) ;
489
529
safeBody = phtml . html ( ) ;
490
530
}
531
+ if ( bodyHasEmoji ) {
532
+ safeBody = formatEmojis ( safeBody , true ) . join ( '' ) ;
533
+ }
491
534
}
492
535
} finally {
493
536
delete sanitizeParams . textFilter ;
@@ -530,14 +573,21 @@ export function bodyToHtml(content: IContent, highlights: string[], opts: IOpts
530
573
'markdown-body' : isHtmlMessage && ! emojiBody ,
531
574
} ) ;
532
575
576
+ let emojiBodyElements : JSX . Element [ ] ;
577
+ if ( ! isDisplayedWithHtml && bodyHasEmoji && ! emojiBody ) {
578
+ emojiBodyElements = formatEmojis ( strippedBody , false ) as JSX . Element [ ] ;
579
+ }
580
+
533
581
return isDisplayedWithHtml ?
534
582
< span
535
583
key = "body"
536
584
ref = { opts . ref }
537
585
className = { className }
538
586
dangerouslySetInnerHTML = { { __html : safeBody } }
539
587
dir = "auto"
540
- /> : < span key = "body" ref = { opts . ref } className = { className } dir = "auto" > { strippedBody } </ span > ;
588
+ /> : < span key = "body" ref = { opts . ref } className = { className } dir = "auto" >
589
+ { emojiBodyElements || strippedBody }
590
+ </ span > ;
541
591
}
542
592
543
593
/**
0 commit comments