From 8bb7cb4a90917b4c5c7d7ce4399e59c5f4ee9625 Mon Sep 17 00:00:00 2001 From: Raphael Fakhri <153192858+RaphaelFakhri@users.noreply.github.com> Date: Tue, 3 Nov 2026 12:22:39 +0000 Subject: [PATCH] fix: keep emoji intact in avatar initials getInitialsFromName took the first UTF-16 code unit of each word, so a name that starts with an emoji or another character outside the BMP rendered a lone surrogate in UserAvatar and the reactions avatar. Take the first code point instead. --- package/src/utils/__tests__/utils.test.ts | 18 +++++++++++++++++- package/src/utils/utils.ts | 4 +++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/package/src/utils/__tests__/utils.test.ts b/package/src/utils/__tests__/utils.test.ts index 0c5e75e93f..d0c9e96979 100644 --- a/package/src/utils/__tests__/utils.test.ts +++ b/package/src/utils/__tests__/utils.test.ts @@ -1,4 +1,4 @@ -import { formatMsToMinSec, getUrlWithoutParams } from '../utils'; +import { formatMsToMinSec, getInitialsFromName, getUrlWithoutParams } from '../utils'; describe('getUrlWithoutParams', () => { const testUrlMap: Record = { @@ -37,3 +37,19 @@ describe('formatMsToMinSec', () => { expect(formatMsToMinSec(7_200_000)).toBe('2h'); }); }); + +describe('getInitialsFromName', () => { + it('should return the uppercased first letter of each word', () => { + expect(getInitialsFromName('john doe')).toBe('JD'); + expect(getInitialsFromName(' Jane Mary Doe ')).toBe('JM'); + expect(getInitialsFromName('Jane Mary Doe', 3)).toBe('JMD'); + expect(getInitialsFromName('Jane', 1)).toBe('J'); + expect(getInitialsFromName(' ')).toBe(''); + }); + + it('should not split characters outside the BMP into lone surrogates', () => { + expect(getInitialsFromName('πŸ˜€ Smiley')).toBe('πŸ˜€S'); + expect(getInitialsFromName('π’œlice 𐐨bob')).toBe('π’œπ€'); + expect(getInitialsFromName('πŸš€', 1)).toBe('πŸš€'); + }); +}); diff --git a/package/src/utils/utils.ts b/package/src/utils/utils.ts index 282a72dcb6..44c3eed0a5 100644 --- a/package/src/utils/utils.ts +++ b/package/src/utils/utils.ts @@ -388,10 +388,12 @@ export const getInitialsFromName = (name: string, numberOfInitials: number = 2) const trimmed = name.trim(); if (!trimmed) return ''; + // Take the first code point of each word, not the first UTF-16 code unit, so names that + // start with an emoji or another non-BMP character do not render a lone surrogate. return trimmed .split(/\s+/) .slice(0, numberOfInitials) - .map((n) => n.charAt(0).toUpperCase()) + .map((n) => Array.from(n)[0].toUpperCase()) .join(''); };