From 0625edb3080adc65c021c75e78f42f24f3966ac3 Mon Sep 17 00:00:00 2001 From: Alexander Zinchuk Date: Fri, 27 Aug 2021 21:05:49 +0300 Subject: [PATCH] Avatar: Use full emoji sequence instead of the first one (#1388) --- src/components/common/ProfilePhoto.scss | 2 +- src/components/common/helpers/renderText.tsx | 6 ++++-- src/util/emoji.ts | 21 +++++++++++++++++++- src/util/textFormat.ts | 13 +++++++++++- 4 files changed, 37 insertions(+), 5 deletions(-) diff --git a/src/components/common/ProfilePhoto.scss b/src/components/common/ProfilePhoto.scss index 34108f928..6296c1235 100644 --- a/src/components/common/ProfilePhoto.scss +++ b/src/components/common/ProfilePhoto.scss @@ -4,7 +4,7 @@ cursor: pointer; position: relative; - img { + img:not(.emoji) { width: 100%; object-fit: cover; } diff --git a/src/components/common/helpers/renderText.tsx b/src/components/common/helpers/renderText.tsx index a8ce3e834..248b1f7e5 100644 --- a/src/components/common/helpers/renderText.tsx +++ b/src/components/common/helpers/renderText.tsx @@ -3,7 +3,7 @@ import EMOJI_REGEX, { removeVS16s } from '../../../lib/twemojiRegex'; import { RE_LINK_TEMPLATE, RE_MENTION_TEMPLATE } from '../../../config'; import { IS_EMOJI_SUPPORTED } from '../../../util/environment'; -import { nativeToUnfified } from '../../../util/emoji'; +import { fixNonStandardEmoji, nativeToUnified } from '../../../util/emoji'; import buildClassName from '../../../util/buildClassName'; import MentionLink from '../../middle/message/MentionLink'; @@ -89,12 +89,14 @@ function replaceEmojis(textParts: TextPart[], size: 'big' | 'small', type: 'jsx' return [...result, part]; } + part = fixNonStandardEmoji(part); const parts = part.split(EMOJI_REGEX); const emojis = part.match(EMOJI_REGEX) || []; result.push(parts[0]); return emojis.reduce((emojiResult: TextPart[], emoji, i) => { - const code = nativeToUnfified(removeVS16s(emoji)); + const code = nativeToUnified(removeVS16s(emoji)); + if (!code) return emojiResult; const className = buildClassName( 'emoji', size === 'small' && 'emoji-small', diff --git a/src/util/emoji.ts b/src/util/emoji.ts index dbfa5906b..8d01acbf8 100644 --- a/src/util/emoji.ts +++ b/src/util/emoji.ts @@ -1,3 +1,5 @@ +import EMOJI_REGEX from '../lib/twemojiRegex'; + // Due to the fact that emoji from Apple do not contain some characters, it is necessary to remove them from emoji-data // https://github.com/iamcal/emoji-data/issues/136 const EXCLUDE_EMOJIS = ['female_sign', 'male_sign', 'medical_symbol']; @@ -10,6 +12,13 @@ export type EmojiData = { emojis: Record; }; +// Non-standard variations of emojis, used on some devices +const EMOJI_EXCEPTIONS: [string | RegExp, string][] = [ + [/\u{1f3f3}\u200d\u{1f308}/gu, '\u{1f3f3}\ufe0f\u200d\u{1f308}'], // 🏳‍🌈 + [/\u{1f3f3}\u200d\u26a7\ufe0f/gu, '\u{1f3f3}\ufe0f\u200d\u26a7\ufe0f'], // 🏳️‍⚧️ + [/\u{1f937}\u200d\u2642/gu, '\u{1f937}\u200d\u2642\ufe0f'], // 🤷‍♂️ +] + function unifiedToNative(unified: string) { const unicodes = unified.split('-'); const codePoints = unicodes.map((i) => parseInt(i, 16)); @@ -17,7 +26,17 @@ function unifiedToNative(unified: string) { return String.fromCodePoint(...codePoints); } -export function nativeToUnfified(emoji: string) { +export function fixNonStandardEmoji(text: string) { + // Non-standard sequences typically parsed as separate emojis, so no need to fix text without any + if (!text.match(EMOJI_REGEX)) return text; + for (let [regex, replacement] of EMOJI_EXCEPTIONS) { + text = text.replace(regex, replacement); + } + + return text; +} + +export function nativeToUnified(emoji: string) { let code; if (emoji.length === 1) { diff --git a/src/util/textFormat.ts b/src/util/textFormat.ts index 63f8053b9..2ccd2eb9a 100644 --- a/src/util/textFormat.ts +++ b/src/util/textFormat.ts @@ -1,3 +1,6 @@ +import EMOJI_REGEX from "../lib/twemojiRegex"; +import { fixNonStandardEmoji } from "./emoji"; + export function formatInteger(value: number) { return String(value).replace(/\d(?=(\d{3})+$)/g, '$& '); } @@ -29,6 +32,14 @@ export function getFirstLetters(phrase: string, count = 2) { .trim() .split(/\s+/) .slice(0, count) - .map((word: string) => word.length && word.match(/./u)![0].toUpperCase()) + .map((word: string) => { + if (!word.length) return ''; + word = fixNonStandardEmoji(word); + const emojis = word.match(EMOJI_REGEX); + if (emojis && word.startsWith(emojis[0])) { + return emojis[0]; + } + return word.match(/./u)![0].toUpperCase() + }) .join(''); }