mirror of
https://github.com/mastodon/mastodon.git
synced 2025-11-27 18:10:58 +00:00
This commit is contained in:
parent
e9e15d5a6a
commit
1f5a52a1fc
|
|
@ -10,6 +10,7 @@ export interface PickerContext {
|
|||
hiddenGroups: string[];
|
||||
recentlyUsed: string[];
|
||||
favourites: string[];
|
||||
setFavourite: (emojiCode: string) => void;
|
||||
}
|
||||
|
||||
const pickerContext = createContext<PickerContext>({
|
||||
|
|
@ -17,6 +18,9 @@ const pickerContext = createContext<PickerContext>({
|
|||
hiddenGroups: [],
|
||||
recentlyUsed: [],
|
||||
favourites: [],
|
||||
setFavourite: () => {
|
||||
throw new Error('setFavourite not implemented');
|
||||
},
|
||||
});
|
||||
|
||||
export const PickerContextProvider = pickerContext.Provider;
|
||||
|
|
|
|||
|
|
@ -70,13 +70,30 @@ export const MockEmojiPicker: FC<MockEmojiPickerProps> = ({
|
|||
);
|
||||
}, []);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- TODO: Set up favourites management
|
||||
const [favourites, setFavourites] = useState<string[]>(['🖤']);
|
||||
const handleSetFavourite = useCallback((emojiCode: string) => {
|
||||
const emojiKey = emojiCode;
|
||||
setFavourites((prev) => {
|
||||
const prevCodes = new Set(prev);
|
||||
if (prevCodes.has(emojiKey)) {
|
||||
prevCodes.delete(emojiKey);
|
||||
} else {
|
||||
prevCodes.add(emojiKey);
|
||||
}
|
||||
return Array.from(prevCodes);
|
||||
});
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<CustomEmojiProvider emojis={mockCustomEmojis}>
|
||||
<PickerContextProvider
|
||||
value={{ skinTone, hiddenGroups, favourites, recentlyUsed }}
|
||||
value={{
|
||||
skinTone,
|
||||
hiddenGroups,
|
||||
favourites,
|
||||
recentlyUsed,
|
||||
setFavourite: handleSetFavourite,
|
||||
}}
|
||||
>
|
||||
<div className={classes.wrapper}>
|
||||
{showSettings ? (
|
||||
|
|
|
|||
|
|
@ -15,7 +15,11 @@ import ArrowIcon from '@/material-icons/400-24px/arrow_drop_down.svg?react';
|
|||
|
||||
import { Emoji } from '..';
|
||||
|
||||
import { groupKeysToNumber, mockCustomEmojis } from './constants';
|
||||
import {
|
||||
groupKeysToNumber,
|
||||
mockCustomEmojis,
|
||||
usePickerContext,
|
||||
} from './constants';
|
||||
import classes from './styles.module.css';
|
||||
|
||||
interface PickerGroupListProps {
|
||||
|
|
@ -57,7 +61,7 @@ export const PickerGroupCustomList: FC<
|
|||
|
||||
// Determine which missing keys are Unicode and which are custom.
|
||||
const [missingUnicodeKeys, missingCustomKeys] = useMemo(() => {
|
||||
const emojisToKeys = emojis?.map(emojiToKey) ?? [];
|
||||
const emojisToKeys = emojis?.map((e) => emojiToKey(e)) ?? [];
|
||||
const unicodeKeys = emojiKeys
|
||||
.filter((key) => !isCustomEmoji(key))
|
||||
.map((code) => emojiToUnicodeHex(code))
|
||||
|
|
@ -94,11 +98,23 @@ export const PickerGroupCustomList: FC<
|
|||
});
|
||||
}
|
||||
|
||||
// If there is an emoji but no key, remove it.
|
||||
if (emojis !== null && emojis.length > emojiKeys.length) {
|
||||
setEmojis(
|
||||
(prevEmojis) =>
|
||||
prevEmojis?.filter((emoji) => emojiKeys.includes(emojiToKey(emoji))) ??
|
||||
[],
|
||||
);
|
||||
}
|
||||
|
||||
return <PickerGroupListInner emojis={emojis} {...props} />;
|
||||
};
|
||||
|
||||
function emojiToKey(emoji: AnyEmojiData): string {
|
||||
return 'hexcode' in emoji ? emoji.hexcode : `:${emoji.shortcode}:`;
|
||||
function emojiToKey(emoji: AnyEmojiData, hexcode = true): string {
|
||||
if ('shortcode' in emoji) {
|
||||
return `:${emoji.shortcode}:`;
|
||||
}
|
||||
return hexcode ? emoji.hexcode : emoji.unicode;
|
||||
}
|
||||
|
||||
function mergeNewEmojis(
|
||||
|
|
@ -106,6 +122,9 @@ function mergeNewEmojis(
|
|||
newEmojis: AnyEmojiData[],
|
||||
emojiKeys: string[],
|
||||
): AnyEmojiData[] {
|
||||
if (newEmojis.length === 0) {
|
||||
return currentEmojis;
|
||||
}
|
||||
const allEmojis = new Map([
|
||||
...currentEmojis.map(
|
||||
(emoji) => [emojiToKey(emoji), emoji] satisfies [string, AnyEmojiData],
|
||||
|
|
@ -117,11 +136,7 @@ function mergeNewEmojis(
|
|||
|
||||
return (
|
||||
emojiKeys
|
||||
.map((key) =>
|
||||
isCustomEmoji(key)
|
||||
? allEmojis.get(key)
|
||||
: allEmojis.get(emojiToUnicodeHex(key)),
|
||||
)
|
||||
.map((key) => allEmojis.get(emojiToUnicodeHex(key)))
|
||||
// Discard any missing emojis.
|
||||
.filter((e) => !!e)
|
||||
);
|
||||
|
|
@ -177,12 +192,21 @@ const PickerListEmoji: FC<PickerListEmojiProps> = ({ emoji, onClick }) => {
|
|||
const handleClick: MouseEventHandler = useCallback(() => {
|
||||
onClick(emoji);
|
||||
}, [emoji, onClick]);
|
||||
const { setFavourite } = usePickerContext();
|
||||
const handleContextMenu: MouseEventHandler = useCallback(
|
||||
(event) => {
|
||||
event.preventDefault();
|
||||
setFavourite(emojiToKey(emoji, false));
|
||||
},
|
||||
[emoji, setFavourite],
|
||||
);
|
||||
return (
|
||||
<li>
|
||||
<button
|
||||
type='button'
|
||||
title={'unicode' in emoji ? emoji.label : `:${emoji.shortcode}:`}
|
||||
onClick={handleClick}
|
||||
onContextMenu={handleContextMenu}
|
||||
className={classes.listButton}
|
||||
>
|
||||
<Emoji
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import {
|
|||
EMOJIS_WITH_LIGHT_BORDER,
|
||||
} from './constants';
|
||||
import type { CustomEmojiMapArg, ExtraCustomEmojiMap } from './types';
|
||||
import { isCustomEmoji } from './utils';
|
||||
|
||||
// Misc codes that have special handling
|
||||
const SKIER_CODE = 0x26f7;
|
||||
|
|
@ -23,6 +24,9 @@ const SPEECH_BUBBLE_CODE = 0x1f5e8;
|
|||
const MS_CLAUS_CODE = 0x1f936;
|
||||
|
||||
export function emojiToUnicodeHex(emoji: string): string {
|
||||
if (isCustomEmoji(emoji)) {
|
||||
return emoji;
|
||||
}
|
||||
const codes: number[] = [];
|
||||
for (const char of emoji) {
|
||||
const code = char.codePointAt(0);
|
||||
|
|
@ -30,6 +34,10 @@ export function emojiToUnicodeHex(emoji: string): string {
|
|||
codes.push(code);
|
||||
}
|
||||
}
|
||||
// Strip trailing variation selector.
|
||||
if (codes.at(-1) === VARIATION_SELECTOR_CODE) {
|
||||
codes.pop();
|
||||
}
|
||||
return hexNumbersToString(codes);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user