Skip to content

Commit e98d0f6

Browse files
committed
MOBILE-4902 format-text: Force redraw message to solve iOS emojis render
1 parent 38b3ae2 commit e98d0f6

File tree

4 files changed

+87
-2
lines changed

4 files changed

+87
-2
lines changed

src/core/directives/format-text.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -476,10 +476,21 @@ export class CoreFormatTextDirective implements OnDestroy, AsyncDirective {
476476
// Emit the afterRender output.
477477
this.afterRender.emit();
478478
if (triggerFilterRender) {
479-
this.filterContentRenderingComplete.emit();
479+
this.finishFilterContentRendering();
480480
}
481481
}
482482

483+
/**
484+
* Finish the rendering once filters have been applied.
485+
*/
486+
protected async finishFilterContentRendering(): Promise<void> {
487+
// Force redraw to make sure emojis are displayed on iOS.
488+
if (CorePlatform.isIOS() && CoreText.containsEmoji(this.element.innerText)) {
489+
await CoreDom.forceElementRedraw(this.element);
490+
}
491+
this.filterContentRenderingComplete.emit();
492+
}
493+
483494
/**
484495
* Format contents and render.
485496
*/
@@ -530,7 +541,7 @@ export class CoreFormatTextDirective implements OnDestroy, AsyncDirective {
530541
this.componentId(),
531542
this.getSiteId(),
532543
).finally(() => {
533-
this.filterContentRenderingComplete.emit();
544+
this.finishFilterContentRendering();
534545
});
535546
}
536547

src/core/singletons/dom.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1259,6 +1259,24 @@ export class CoreDom {
12591259
return elementPoint > window.innerHeight || elementPoint < scrollTopPos;
12601260
}
12611261

1262+
/**
1263+
* Force a redraw of an element.
1264+
*
1265+
* @param element Element to redraw.
1266+
*/
1267+
static async forceElementRedraw(element?: HTMLElement): Promise<void> {
1268+
if (!element) {
1269+
return;
1270+
}
1271+
1272+
const oldDisplay = element.style.display;
1273+
element.style.display = 'none';
1274+
1275+
await CoreWait.nextTick();
1276+
1277+
element.style.display = oldDisplay;
1278+
}
1279+
12621280
}
12631281

12641282
/**

src/core/singletons/tests/text.test.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,4 +126,47 @@ describe('CoreText singleton', () => {
126126
expect(CoreText.camelCaseToKebabCase('mix_of-differentTextCases')).toEqual('mix_of-different-text-cases');
127127
});
128128

129+
it('check strings contains emojis', () => {
130+
expect(CoreText.containsEmoji('')).toBe(false);
131+
expect(CoreText.containsEmoji('Hello!')).toBe(false);
132+
expect(CoreText.containsEmoji('😀')).toBe(true);
133+
expect(CoreText.containsEmoji('Hello 😀!')).toBe(true);
134+
expect(CoreText.containsEmoji('👩‍💻')).toBe(true);
135+
expect(CoreText.containsEmoji('☀️Moodle')).toBe(true);
136+
137+
// Check also emojis with skin tones.
138+
expect(CoreText.containsEmoji('👍')).toBe(true);
139+
expect(CoreText.containsEmoji('👍🏻')).toBe(true);
140+
141+
// Check also flags.
142+
expect(CoreText.containsEmoji('🇺🇦')).toBe(true);
143+
expect(CoreText.containsEmoji('🇵🇸')).toBe(true);
144+
expect(CoreText.containsEmoji('🏳️‍⚧️')).toBe(true);
145+
expect(CoreText.containsEmoji('🏴󠁵󠁳󠁴󠁸󠁿')).toBe(true);
146+
147+
// Check complex emojis.
148+
expect(CoreText.containsEmoji('👩🏽‍⚕️')).toBe(true);
149+
expect(CoreText.containsEmoji('👨‍👩‍👧‍👦')).toBe(true);
150+
expect(CoreText.containsEmoji('🤼🏻‍♀️')).toBe(true);
151+
152+
// Check some edge cases.
153+
expect(CoreText.containsEmoji('1️⃣')).toBe(true);
154+
expect(CoreText.containsEmoji('#️⃣')).toBe(true);
155+
expect(CoreText.containsEmoji('*️⃣')).toBe(true);
156+
expect(CoreText.containsEmoji('©️')).toBe(true);
157+
expect(CoreText.containsEmoji('®️')).toBe(true);
158+
expect(CoreText.containsEmoji('™️')).toBe(true);
159+
expect(CoreText.containsEmoji('0️⃣1️⃣2️⃣3️⃣4️⃣5️⃣6️⃣7️⃣8️⃣9️⃣')).toBe(true);
160+
expect(CoreText.containsEmoji('E=mc²')).toBe(false);
161+
expect(CoreText.containsEmoji('3³')).toBe(false);
162+
expect(CoreText.containsEmoji('10⁹')).toBe(false);
163+
expect(CoreText.containsEmoji('x²')).toBe(false);
164+
expect(CoreText.containsEmoji('SO₄²⁻')).toBe(false);
165+
166+
// Some sequences of characters that look like emojis but aren't.
167+
expect(CoreText.containsEmoji('<:custom_emoji:123456789012345678>')).toBe(false);
168+
expect(CoreText.containsEmoji('<a:custom_emoji:123456789012345678>')).toBe(false);
169+
expect(CoreText.containsEmoji(':)')).toBe(false);
170+
expect(CoreText.containsEmoji(':-)')).toBe(false);
171+
});
129172
});

src/core/singletons/text.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,19 @@ export class CoreText {
665665
return Locutus.unserialize<T>(data);
666666
}
667667

668+
/**
669+
* Check if a string contains any emoji.
670+
*
671+
* @param text Text to check.
672+
* @returns True if contains emoji, false otherwise.
673+
*/
674+
static containsEmoji(text: string): boolean {
675+
const base = String.raw`\p{Emoji}(?:\p{EMod}|[\u{E0020}-\u{E007E}]+\u{E007F}|\uFE0F?\u20E3?)`;
676+
const regexEmoji = new RegExp(String.raw`\p{RI}{2}|(?![#*\d](?!\uFE0F?\u20E3))${base}(?:\u200D${base})*`, 'gu');
677+
678+
return regexEmoji.test(text);
679+
}
680+
668681
}
669682

670683
/**

0 commit comments

Comments
 (0)