Support Emoji 15.1 & fixes (#4343)

This commit is contained in:
Alexander Zinchuk 2024-03-08 12:48:46 +01:00
parent 7bf1b2b878
commit 6054151969
24 changed files with 73 additions and 67 deletions

8
package-lock.json generated
View File

@ -14,7 +14,7 @@
"async-mutex": "^0.4.0",
"big-integer": "github:painor/BigInteger.js",
"croppie": "^2.6.5",
"emoji-data-ios": "git+https://github.com/korenskoy/emoji-data-ios#2886b318eae174527c4bc9fcd321940ef3a85527",
"emoji-data-ios": "git+https://github.com/korenskoy/emoji-data-ios#de1a69aff2c8eb7548df4e6bcf50c7d2304792fc",
"idb-keyval": "^6.2.1",
"lowlight": "^2.9.0",
"mp4box": "^0.5.2",
@ -11124,9 +11124,9 @@
}
},
"node_modules/emoji-data-ios": {
"version": "0.5.0",
"resolved": "git+ssh://git@github.com/korenskoy/emoji-data-ios.git#2886b318eae174527c4bc9fcd321940ef3a85527",
"integrity": "sha512-kqoSV9kSLVjl5+tCpFwxXKYNnjgASPnRMwY4aLilYGNwX/R9mBYzSiHe/YbPurggz5AisLz/PeJuJeLe7XG7Fg==",
"version": "0.5.1",
"resolved": "git+ssh://git@github.com/korenskoy/emoji-data-ios.git#de1a69aff2c8eb7548df4e6bcf50c7d2304792fc",
"integrity": "sha512-aNh0bvCsHcRuLpTgOoJ2DQBYBHjuhjrpwrZtKKQ1ZyGVg0ab2ys26bjmI+zgWYcgg48hAhO2JQPZNRaR5yOK5w==",
"license": "MIT"
},
"node_modules/emoji-regex": {

View File

@ -139,7 +139,7 @@
"async-mutex": "^0.4.0",
"big-integer": "github:painor/BigInteger.js",
"croppie": "^2.6.5",
"emoji-data-ios": "git+https://github.com/korenskoy/emoji-data-ios#2886b318eae174527c4bc9fcd321940ef3a85527",
"emoji-data-ios": "git+https://github.com/korenskoy/emoji-data-ios#de1a69aff2c8eb7548df4e6bcf50c7d2304792fc",
"idb-keyval": "^6.2.1",
"lowlight": "^2.9.0",
"mp4box": "^0.5.2",

View File

@ -8,7 +8,7 @@ import type { ApiCountryCode } from '../../api/types';
import { ANIMATION_END_DELAY } from '../../config';
import buildClassName from '../../util/buildClassName';
import { isoToEmoji } from '../../util/emoji';
import { isoToEmoji } from '../../util/emoji/emoji';
import { prepareSearchWordsForNeedle } from '../../util/searchWords';
import renderText from '../common/helpers/renderText';

View File

@ -86,9 +86,9 @@ import {
} from '../../global/selectors';
import { selectCurrentLimit } from '../../global/selectors/limits';
import buildClassName from '../../util/buildClassName';
import { processMessageInputForCustomEmoji } from '../../util/customEmojiManager';
import { formatMediaDuration, formatVoiceRecordDuration } from '../../util/dateFormat';
import deleteLastCharacterOutsideSelection from '../../util/deleteLastCharacterOutsideSelection';
import { processMessageInputForCustomEmoji } from '../../util/emoji/customEmojiManager';
import focusEditableElement from '../../util/focusEditableElement';
import { MEMO_EMPTY_ARRAY } from '../../util/memo';
import parseHtmlAsFormattedText from '../../util/parseHtmlAsFormattedText';

View File

@ -10,11 +10,11 @@ import EMOJI_REGEX from '../../../lib/twemojiRegex';
import buildClassName from '../../../util/buildClassName';
import { isDeepLink } from '../../../util/deepLinkParser';
import {
fixNonStandardEmoji,
handleEmojiLoad,
LOADED_EMOJIS,
nativeToUnifiedExtendedWithCache,
} from '../../../util/emoji';
} from '../../../util/emoji/emoji';
import fixNonStandardEmoji from '../../../util/emoji/fixNonStandardEmoji';
import { compact } from '../../../util/iteratees';
import { IS_EMOJI_SUPPORTED } from '../../../util/windowEnvironment';

View File

@ -5,7 +5,7 @@ import type { ApiSticker } from '../../../api/types';
import type { GlobalState } from '../../../global/types';
import { selectCanPlayAnimatedEmojis } from '../../../global/selectors';
import { addCustomEmojiCallback, removeCustomEmojiCallback } from '../../../util/customEmojiManager';
import { addCustomEmojiCallback, removeCustomEmojiCallback } from '../../../util/emoji/customEmojiManager';
import useEnsureCustomEmoji from '../../../hooks/useEnsureCustomEmoji';
import useLastCallback from '../../../hooks/useLastCallback';

View File

@ -3,7 +3,7 @@ import React, { memo } from '../../../lib/teact/teact';
import { BASE_URL, IS_PACKAGED_ELECTRON } from '../../../config';
import buildClassName from '../../../util/buildClassName';
import { handleEmojiLoad, LOADED_EMOJIS } from '../../../util/emoji';
import { handleEmojiLoad, LOADED_EMOJIS } from '../../../util/emoji/emoji';
import { IS_EMOJI_SUPPORTED } from '../../../util/windowEnvironment';
import useLastCallback from '../../../hooks/useLastCallback';

View File

@ -10,13 +10,13 @@ import type {
EmojiData,
EmojiModule,
EmojiRawData,
} from '../../../util/emoji';
} from '../../../util/emoji/emoji';
import { MENU_TRANSITION_DURATION, RECENT_SYMBOL_SET_ID } from '../../../config';
import animateHorizontalScroll from '../../../util/animateHorizontalScroll';
import animateScroll from '../../../util/animateScroll';
import buildClassName from '../../../util/buildClassName';
import { uncompressEmoji } from '../../../util/emoji';
import { uncompressEmoji } from '../../../util/emoji/emoji';
import { pick } from '../../../util/iteratees';
import { MEMO_EMPTY_ARRAY } from '../../../util/memo';
import { IS_TOUCH_ENV } from '../../../util/windowEnvironment';

View File

@ -16,8 +16,8 @@ import { selectCanPlayAnimatedEmojis, selectDraft, selectIsInSelectMode } from '
import buildClassName from '../../../util/buildClassName';
import captureKeyboardListeners from '../../../util/captureKeyboardListeners';
import { getIsDirectTextInputDisabled } from '../../../util/directInputManager';
import parseEmojiOnlyString from '../../../util/emoji/parseEmojiOnlyString';
import focusEditableElement from '../../../util/focusEditableElement';
import parseEmojiOnlyString from '../../../util/parseEmojiOnlyString';
import { debounce } from '../../../util/schedulers';
import {
IS_ANDROID, IS_EMOJI_SUPPORTED, IS_IOS, IS_TOUCH_ENV,

View File

@ -5,7 +5,7 @@ import { ApiMessageEntityTypes } from '../../../../api/types';
import { EMOJI_SIZES } from '../../../../config';
import buildClassName from '../../../../util/buildClassName';
import { getInputCustomEmojiParams } from '../../../../util/customEmojiManager';
import { getInputCustomEmojiParams } from '../../../../util/emoji/customEmojiManager';
import { REM } from '../../../common/helpers/mediaDimensions';
export const INPUT_CUSTOM_EMOJI_SELECTOR = 'img[data-document-id]';

View File

@ -2,13 +2,13 @@ import { useEffect, useState } from '../../../../lib/teact/teact';
import { getGlobal } from '../../../../global';
import type { ApiSticker } from '../../../../api/types';
import type { EmojiData, EmojiModule, EmojiRawData } from '../../../../util/emoji';
import type { EmojiData, EmojiModule, EmojiRawData } from '../../../../util/emoji/emoji';
import type { Signal } from '../../../../util/signals';
import { EDITABLE_INPUT_CSS_SELECTOR, EDITABLE_INPUT_ID } from '../../../../config';
import { requestNextMutation } from '../../../../lib/fasterdom/fasterdom';
import { selectCustomEmojiForEmojis } from '../../../../global/selectors';
import { uncompressEmoji } from '../../../../util/emoji';
import { uncompressEmoji } from '../../../../util/emoji/emoji';
import focusEditableElement from '../../../../util/focusEditableElement';
import {
buildCollectionByKey, mapValues, pickTruthy, unique, uniqueByField,

View File

@ -13,7 +13,7 @@ import AbsoluteVideo from '../../../../util/AbsoluteVideo';
import {
addCustomEmojiInputRenderCallback,
getCustomEmojiMediaDataForInput,
} from '../../../../util/customEmojiManager';
} from '../../../../util/emoji/customEmojiManager';
import { round } from '../../../../util/math';
import { hexToRgb } from '../../../../util/switchTheme';
import { REM } from '../../../common/helpers/mediaDimensions';

View File

@ -6,7 +6,7 @@ import type { Signal } from '../../../../util/signals';
import { EMOJI_IMG_REGEX } from '../../../../config';
import twemojiRegex from '../../../../lib/twemojiRegex';
import parseEmojiOnlyString from '../../../../util/parseEmojiOnlyString';
import parseEmojiOnlyString from '../../../../util/emoji/parseEmojiOnlyString';
import { IS_EMOJI_SUPPORTED } from '../../../../util/windowEnvironment';
import { prepareForRegExp } from '../helpers/prepareForRegExp';

View File

@ -18,7 +18,7 @@ import {
} from '../../../global/selectors';
import buildClassName from '../../../util/buildClassName';
import { formatDateAtTime, formatDateTimeToString } from '../../../util/dateFormat';
import { isoToEmoji } from '../../../util/emoji';
import { isoToEmoji } from '../../../util/emoji/emoji';
import { getServerTime } from '../../../util/serverTime';
import { callApi } from '../../../api/gramjs';
import { LOCAL_TGS_URLS } from '../../common/helpers/animatedAssets';

View File

@ -814,7 +814,7 @@
--emoji-only-size: #{$size};
.text-content {
width: #{calc($size * $i)};
width: #{calc(($size + 1px) * $i)};
font-size: calc($size * 0.9);
}
}

View File

@ -1,7 +1,7 @@
import type { MediaContent } from '../../api/types';
import { ApiMessageEntityTypes } from '../../api/types';
import parseEmojiOnlyString from '../../util/parseEmojiOnlyString';
import parseEmojiOnlyString from '../../util/emoji/parseEmojiOnlyString';
export function getEmojiOnlyCountForMessage(content: MediaContent, groupedId?: string): number | undefined {
if (!content.text) return undefined;

View File

@ -1,6 +1,6 @@
import { getActions, getGlobal } from '../global';
import { addCustomEmojiInputRenderCallback } from '../util/customEmojiManager';
import { addCustomEmojiInputRenderCallback } from '../util/emoji/customEmojiManager';
import { throttle } from '../util/schedulers';
let LOAD_QUEUE = new Set<string>();

File diff suppressed because one or more lines are too long

View File

@ -1,21 +1,21 @@
import { addCallback } from '../lib/teact/teactn';
import { getGlobal } from '../global';
import { addCallback } from '../../lib/teact/teactn';
import { getGlobal } from '../../global';
import type { ApiSticker } from '../api/types';
import type { GlobalState } from '../global/types';
import { ApiMediaFormat } from '../api/types';
import type { ApiSticker } from '../../api/types';
import type { GlobalState } from '../../global/types';
import { ApiMediaFormat } from '../../api/types';
import { requestMutation } from '../lib/fasterdom/fasterdom';
import { getStickerPreviewHash } from '../global/helpers';
import { selectCanPlayAnimatedEmojis } from '../global/selectors';
import { createCallbackManager } from './callbacks';
import generateUniqueId from './generateUniqueId';
import * as mediaLoader from './mediaLoader';
import { throttle } from './schedulers';
import { IS_WEBM_SUPPORTED } from './windowEnvironment';
import { requestMutation } from '../../lib/fasterdom/fasterdom';
import { getStickerPreviewHash } from '../../global/helpers';
import { selectCanPlayAnimatedEmojis } from '../../global/selectors';
import { createCallbackManager } from '../callbacks';
import generateUniqueId from '../generateUniqueId';
import * as mediaLoader from '../mediaLoader';
import { throttle } from '../schedulers';
import { IS_WEBM_SUPPORTED } from '../windowEnvironment';
import blankSrc from '../assets/blank.png';
import placeholderSrc from '../assets/square.svg';
import blankSrc from '../../assets/blank.png';
import placeholderSrc from '../../assets/square.svg';
type CustomEmojiLoadCallback = (customEmojis: GlobalState['customEmojis']) => void;
type CustomEmojiInputRenderCallback = (emojiId: string) => void;

View File

@ -1,6 +1,8 @@
import { requestMutation } from '../lib/fasterdom/fasterdom';
import EMOJI_REGEX, { removeVS16s } from '../lib/twemojiRegex';
import withCache from './withCache';
import { addExtraClass } from '../../lib/teact/teact-dom';
import { requestMutation } from '../../lib/fasterdom/fasterdom';
import { removeVS16s } from '../../lib/twemojiRegex';
import withCache from '../withCache';
// 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
@ -16,13 +18,6 @@ export type EmojiData = {
emojis: Record<string, Emoji>;
};
// 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[^\ufe0f]/gu, '\u{1f937}\u200d\u2642\ufe0f'], // 🤷
];
function unifiedToNative(unified: string) {
const unicodes = unified.split('-');
const codePoints = unicodes.map((i) => parseInt(i, 16));
@ -38,21 +33,10 @@ export function handleEmojiLoad(event: React.SyntheticEvent<HTMLImageElement>) {
LOADED_EMOJIS.add(event.currentTarget.dataset.path!);
requestMutation(() => {
emoji.classList.add('open');
addExtraClass(emoji, 'open');
});
}
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;
// eslint-disable-next-line no-restricted-syntax
for (const [regex, replacement] of EMOJI_EXCEPTIONS) {
text = text.replace(regex, replacement);
}
return text;
}
export function nativeToUnified(emoji: string) {
let code;

View File

@ -0,0 +1,20 @@
import EMOJI_REGEX from '../../lib/twemojiRegex';
// 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'], // 🏳
[/\u26d3\u200d\u{1f4a5}/gu, '\u26d3\ufe0f\u200d\u{1f4a5}'], // 💥
[/\u200d([\u2640\u2642])(?!\ufe0f)/gu, '\u200d$1\ufe0f'], // Gender variation without 0xFE0F
];
export default 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;
// eslint-disable-next-line no-restricted-syntax
for (const [regex, replacement] of EMOJI_EXCEPTIONS) {
text = text.replace(regex, replacement);
}
return text;
}

View File

@ -1,11 +1,13 @@
import twemojiRegex from '../lib/twemojiRegex';
import twemojiRegex from '../../lib/twemojiRegex';
import fixNonStandardEmoji from './fixNonStandardEmoji';
const DETECT_UP_TO = 100;
const MAX_LENGTH = DETECT_UP_TO * 8; // Maximum 8 per one emoji.
const RE_EMOJI_ONLY = new RegExp(`^(?:${twemojiRegex.source})+$`, '');
const parseEmojiOnlyString = (text: string): number | false => {
const lines = text.split('\n');
const standardizedText = fixNonStandardEmoji(text);
const lines = standardizedText.split('\n');
const textWithoutNewlines = lines.join('');
if (textWithoutNewlines.length > MAX_LENGTH) {
return false;

View File

@ -1,7 +1,7 @@
import type { LangFn } from '../hooks/useLang';
import EMOJI_REGEX from '../lib/twemojiRegex';
import { fixNonStandardEmoji } from './emoji';
import fixNonStandardEmoji from './emoji/fixNonStandardEmoji';
import withCache from './withCache';
export function formatInteger(value: number) {

View File

@ -125,7 +125,7 @@ function isLastEmojiVersionSupported() {
inlineEl.classList.add('emoji-test-element');
document.body.appendChild(inlineEl);
inlineEl.innerText = '🫸🏻'; // Emoji from 15.0 version
inlineEl.innerText = '🐦‍🔥'; // Emoji from 15.1 version
const newEmojiWidth = inlineEl.offsetWidth;
inlineEl.innerText = '❤️'; // Emoji from 1.0 version
const legacyEmojiWidth = inlineEl.offsetWidth;