mirror of
https://github.com/mastodon/mastodon.git
synced 2025-11-28 02:20:47 +00:00
redo loading emojis from their keys
This commit is contained in:
parent
3779b9f898
commit
e9e15d5a6a
|
|
@ -1,4 +1,4 @@
|
||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
import type { FC, MouseEventHandler } from 'react';
|
import type { FC, MouseEventHandler } from 'react';
|
||||||
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
@ -11,7 +11,6 @@ import { useEmojiAppState } from '@/mastodon/features/emoji/mode';
|
||||||
import { emojiToUnicodeHex } from '@/mastodon/features/emoji/normalize';
|
import { emojiToUnicodeHex } from '@/mastodon/features/emoji/normalize';
|
||||||
import type { AnyEmojiData } from '@/mastodon/features/emoji/types';
|
import type { AnyEmojiData } from '@/mastodon/features/emoji/types';
|
||||||
import { isCustomEmoji } from '@/mastodon/features/emoji/utils';
|
import { isCustomEmoji } from '@/mastodon/features/emoji/utils';
|
||||||
import { usePrevious } from '@/mastodon/hooks/usePrevious';
|
|
||||||
import ArrowIcon from '@/material-icons/400-24px/arrow_drop_down.svg?react';
|
import ArrowIcon from '@/material-icons/400-24px/arrow_drop_down.svg?react';
|
||||||
|
|
||||||
import { Emoji } from '..';
|
import { Emoji } from '..';
|
||||||
|
|
@ -56,55 +55,78 @@ export const PickerGroupCustomList: FC<
|
||||||
return customEmojis.length > 0 ? customEmojis : null;
|
return customEmojis.length > 0 ? customEmojis : null;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Next, load all Unicode emojis.
|
// Determine which missing keys are Unicode and which are custom.
|
||||||
const { currentLocale } = useEmojiAppState();
|
const [missingUnicodeKeys, missingCustomKeys] = useMemo(() => {
|
||||||
const prevKeyCount = usePrevious(emojiKeys.length) ?? null;
|
const emojisToKeys = emojis?.map(emojiToKey) ?? [];
|
||||||
if (
|
|
||||||
prevKeyCount === null ||
|
|
||||||
(prevKeyCount !== emojiKeys.length &&
|
|
||||||
(emojis === null || emojis.length < emojiKeys.length))
|
|
||||||
) {
|
|
||||||
// Convert to Unicode hex codes.
|
|
||||||
const unicodeKeys = emojiKeys
|
const unicodeKeys = emojiKeys
|
||||||
.filter((key) => !isCustomEmoji(key))
|
.filter((key) => !isCustomEmoji(key))
|
||||||
.map((code) => emojiToUnicodeHex(code));
|
.map((code) => emojiToUnicodeHex(code))
|
||||||
if (unicodeKeys.length === 0) {
|
.filter((code) => !emojisToKeys.includes(code));
|
||||||
return;
|
const customKeys = emojiKeys.filter(
|
||||||
}
|
(key) => isCustomEmoji(key) && !emojisToKeys.includes(key),
|
||||||
void searchEmojisByHexcodes(unicodeKeys, currentLocale).then(
|
);
|
||||||
(unicodeEmojis) => {
|
return [unicodeKeys, customKeys];
|
||||||
if (emojis?.length === emojiKeys.length) {
|
}, [emojiKeys, emojis]);
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Combine custom and Unicode emojis based on the original key order.
|
|
||||||
setEmojis((prevEmojis) => {
|
|
||||||
const combinedEmojis = prevEmojis ?? [];
|
|
||||||
|
|
||||||
return (
|
// Next, load all Unicode emojis.
|
||||||
emojiKeys
|
const { currentLocale } = useEmojiAppState();
|
||||||
.map((key) => {
|
const [loading, setLoading] = useState(false); // Use to avoid duplicate loads.
|
||||||
if (isCustomEmoji(key)) {
|
if (missingUnicodeKeys.length > 0 && !loading) {
|
||||||
return combinedEmojis.find(
|
setLoading(true);
|
||||||
(e) => 'shortcode' in e && e.shortcode === key.slice(1, -1),
|
|
||||||
);
|
void searchEmojisByHexcodes(missingUnicodeKeys, currentLocale).then(
|
||||||
}
|
(unicodeEmojis) => {
|
||||||
return (
|
setEmojis((prevEmojis) => {
|
||||||
unicodeEmojis.find(
|
setLoading(false);
|
||||||
(e) => e.hexcode === emojiToUnicodeHex(key),
|
return mergeNewEmojis(prevEmojis ?? [], unicodeEmojis, emojiKeys);
|
||||||
) ?? null
|
|
||||||
);
|
|
||||||
})
|
|
||||||
// Discard any unknown emojis.
|
|
||||||
.filter((e): e is AnyEmojiData => !!e)
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Finally, load all custom emojis that haven't been loaded yet.
|
||||||
|
if (missingCustomKeys.length > 0) {
|
||||||
|
setEmojis((prevEmojis) => {
|
||||||
|
const newCustomEmojis = mockCustomEmojis.filter((emoji) =>
|
||||||
|
missingCustomKeys.includes(`:${emoji.shortcode}:`),
|
||||||
|
);
|
||||||
|
return mergeNewEmojis(prevEmojis ?? [], newCustomEmojis, emojiKeys);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return <PickerGroupListInner emojis={emojis} {...props} />;
|
return <PickerGroupListInner emojis={emojis} {...props} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function emojiToKey(emoji: AnyEmojiData): string {
|
||||||
|
return 'hexcode' in emoji ? emoji.hexcode : `:${emoji.shortcode}:`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mergeNewEmojis(
|
||||||
|
currentEmojis: AnyEmojiData[],
|
||||||
|
newEmojis: AnyEmojiData[],
|
||||||
|
emojiKeys: string[],
|
||||||
|
): AnyEmojiData[] {
|
||||||
|
const allEmojis = new Map([
|
||||||
|
...currentEmojis.map(
|
||||||
|
(emoji) => [emojiToKey(emoji), emoji] satisfies [string, AnyEmojiData],
|
||||||
|
),
|
||||||
|
...newEmojis.map(
|
||||||
|
(emoji) => [emojiToKey(emoji), emoji] satisfies [string, AnyEmojiData],
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
emojiKeys
|
||||||
|
.map((key) =>
|
||||||
|
isCustomEmoji(key)
|
||||||
|
? allEmojis.get(key)
|
||||||
|
: allEmojis.get(emojiToUnicodeHex(key)),
|
||||||
|
)
|
||||||
|
// Discard any missing emojis.
|
||||||
|
.filter((e) => !!e)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const PickerGroupListInner: FC<
|
const PickerGroupListInner: FC<
|
||||||
PickerGroupListProps & { emojis: AnyEmojiData[] | null }
|
PickerGroupListProps & { emojis: AnyEmojiData[] | null }
|
||||||
> = ({ group, name, onSelect, emojis }) => {
|
> = ({ group, name, onSelect, emojis }) => {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user