mirror of
https://github.com/mastodon/mastodon.git
synced 2025-10-05 16:42:47 +00:00
add new render and context components
Some checks are pending
Chromatic / Run Chromatic (push) Waiting to run
Some checks are pending
Chromatic / Run Chromatic (push) Waiting to run
This commit is contained in:
parent
42b5b4247b
commit
e4566d4f5a
77
app/javascript/mastodon/components/emoji/context.tsx
Normal file
77
app/javascript/mastodon/components/emoji/context.tsx
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
import type {
|
||||||
|
ComponentPropsWithoutRef,
|
||||||
|
ElementType,
|
||||||
|
PropsWithChildren,
|
||||||
|
} from 'react';
|
||||||
|
import { createContext, useCallback, useState } from 'react';
|
||||||
|
|
||||||
|
import type { List as ImmutableList } from 'immutable';
|
||||||
|
|
||||||
|
import type { ApiCustomEmojiJSON } from '@/mastodon/api_types/custom_emoji';
|
||||||
|
import { autoPlayGif } from '@/mastodon/initial_state';
|
||||||
|
import { useAppSelector } from '@/mastodon/store';
|
||||||
|
import type { ExtraCustomEmojiMap } from 'mastodon/features/emoji/types';
|
||||||
|
|
||||||
|
// Animation context
|
||||||
|
export const AnimateEmojiContext = createContext(autoPlayGif ?? false);
|
||||||
|
|
||||||
|
// Polymorphic provider component
|
||||||
|
type AnimateEmojiProviderProps<Element extends ElementType = 'div'> =
|
||||||
|
ComponentPropsWithoutRef<Element> & { as: Element } & PropsWithChildren;
|
||||||
|
|
||||||
|
export const AnimateEmojiProvider = ({
|
||||||
|
children,
|
||||||
|
as: Wrapper = 'div',
|
||||||
|
...props
|
||||||
|
}: AnimateEmojiProviderProps<ElementType>) => {
|
||||||
|
const [animate, setAnimate] = useState(autoPlayGif ?? false);
|
||||||
|
|
||||||
|
const handleEnter = useCallback(() => {
|
||||||
|
if (!autoPlayGif) {
|
||||||
|
setAnimate(true);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
const handleLeave = useCallback(() => {
|
||||||
|
if (!autoPlayGif) {
|
||||||
|
setAnimate(false);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Wrapper {...props} onMouseEnter={handleEnter} onMouseLeave={handleLeave}>
|
||||||
|
<AnimateEmojiContext.Provider value={animate}>
|
||||||
|
{children}
|
||||||
|
</AnimateEmojiContext.Provider>
|
||||||
|
</Wrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handle custom emoji
|
||||||
|
export const CustomEmojiContext = createContext<ExtraCustomEmojiMap>({});
|
||||||
|
|
||||||
|
export const CustomEmojiProvider = ({
|
||||||
|
children,
|
||||||
|
emoji,
|
||||||
|
}: PropsWithChildren<{ emoji: ExtraCustomEmojiMap }>) => {
|
||||||
|
return (
|
||||||
|
<CustomEmojiContext.Provider value={emoji}>
|
||||||
|
{children}
|
||||||
|
</CustomEmojiContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const RootCustomEmojiProvider = ({ children }: PropsWithChildren) => {
|
||||||
|
const emoji = useAppSelector((state) =>
|
||||||
|
(state.custom_emojis as ImmutableList<ApiCustomEmojiJSON>)
|
||||||
|
.toJS()
|
||||||
|
.reduce<ExtraCustomEmojiMap>(
|
||||||
|
(acc, emoji) => ({ ...acc, [emoji.shortcode]: emoji }),
|
||||||
|
{},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<CustomEmojiContext.Provider value={emoji}>
|
||||||
|
{children}
|
||||||
|
</CustomEmojiContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
85
app/javascript/mastodon/components/emoji/index.tsx
Normal file
85
app/javascript/mastodon/components/emoji/index.tsx
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
import type { FC } from 'react';
|
||||||
|
import { useContext, useMemo } from 'react';
|
||||||
|
|
||||||
|
import {
|
||||||
|
EMOJI_TYPE_UNICODE,
|
||||||
|
EMOJI_TYPE_CUSTOM,
|
||||||
|
} from '@/mastodon/features/emoji/constants';
|
||||||
|
import { useEmojiAppState } from '@/mastodon/features/emoji/hooks';
|
||||||
|
import { emojiToUnicodeHex } from '@/mastodon/features/emoji/normalize';
|
||||||
|
import { shouldRenderUnicodeImage } from '@/mastodon/features/emoji/render';
|
||||||
|
import type {
|
||||||
|
EmojiStateCustom,
|
||||||
|
EmojiStateUnicode,
|
||||||
|
} from '@/mastodon/features/emoji/types';
|
||||||
|
import { anyEmojiRegex } from '@/mastodon/features/emoji/utils';
|
||||||
|
|
||||||
|
import { AnimateEmojiContext, CustomEmojiContext } from './context';
|
||||||
|
|
||||||
|
export const Emoji: FC<{ code: string; noFallback?: boolean }> = ({
|
||||||
|
code: rawCode,
|
||||||
|
noFallback,
|
||||||
|
}) => {
|
||||||
|
const customEmoji = useContext(CustomEmojiContext);
|
||||||
|
const appState = useEmojiAppState();
|
||||||
|
const animate = useContext(AnimateEmojiContext);
|
||||||
|
|
||||||
|
const state: null | Required<EmojiStateCustom> | EmojiStateUnicode =
|
||||||
|
useMemo(() => {
|
||||||
|
let code = rawCode;
|
||||||
|
if (!anyEmojiRegex().test(code)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (code.startsWith(':') && code.endsWith(':')) {
|
||||||
|
code = code.slice(1, -1); // Remove the colons
|
||||||
|
const data = customEmoji[code];
|
||||||
|
if (!data) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
type: EMOJI_TYPE_CUSTOM,
|
||||||
|
code,
|
||||||
|
data,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it's not custom, check if we should render this based on mode.
|
||||||
|
if (!shouldRenderUnicodeImage(code, appState.mode)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are rendering it, convert it to a hex code.
|
||||||
|
code = emojiToUnicodeHex(code);
|
||||||
|
return {
|
||||||
|
type: EMOJI_TYPE_UNICODE,
|
||||||
|
code,
|
||||||
|
};
|
||||||
|
}, [appState.mode, customEmoji, rawCode]);
|
||||||
|
|
||||||
|
if (!state) {
|
||||||
|
return noFallback ? null : rawCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.type === EMOJI_TYPE_CUSTOM) {
|
||||||
|
return (
|
||||||
|
<img
|
||||||
|
src={animate ? state.data.url : state.data.static_url}
|
||||||
|
alt={state.code}
|
||||||
|
className='emoji'
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Load data
|
||||||
|
if (!state.data) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<img
|
||||||
|
src={`/emoji/${state.code}.svg`}
|
||||||
|
alt={state.data.label}
|
||||||
|
className='emoji'
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
Loading…
Reference in New Issue
Block a user