mirror of
https://github.com/glitch-soc/mastodon.git
synced 2025-12-21 14:18:36 +00:00
Remove rendering of custom emoji using the database (#37284)
This commit is contained in:
@@ -27,6 +27,7 @@ const config: StorybookConfig = {
|
|||||||
'oops.gif',
|
'oops.gif',
|
||||||
'oops.png',
|
'oops.png',
|
||||||
].map((path) => ({ from: `../public/${path}`, to: `/${path}` })),
|
].map((path) => ({ from: `../public/${path}`, to: `/${path}` })),
|
||||||
|
{ from: '../app/javascript/images/logo.svg', to: '/custom-emoji/logo.svg' },
|
||||||
],
|
],
|
||||||
viteFinal(config) {
|
viteFinal(config) {
|
||||||
// For an unknown reason, Storybook does not use the root
|
// For an unknown reason, Storybook does not use the root
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ import type { ComponentProps } from 'react';
|
|||||||
|
|
||||||
import type { Meta, StoryObj } from '@storybook/react-vite';
|
import type { Meta, StoryObj } from '@storybook/react-vite';
|
||||||
|
|
||||||
|
import { customEmojiFactory } from '@/testing/factories';
|
||||||
|
|
||||||
|
import { CustomEmojiProvider } from './context';
|
||||||
import { Emoji } from './index';
|
import { Emoji } from './index';
|
||||||
|
|
||||||
type EmojiProps = ComponentProps<typeof Emoji> & {
|
type EmojiProps = ComponentProps<typeof Emoji> & {
|
||||||
@@ -34,7 +37,11 @@ const meta = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
render(args) {
|
render(args) {
|
||||||
return <Emoji {...args} />;
|
return (
|
||||||
|
<CustomEmojiProvider emojis={[customEmojiFactory()]}>
|
||||||
|
<Emoji {...args} />
|
||||||
|
</CustomEmojiProvider>
|
||||||
|
);
|
||||||
},
|
},
|
||||||
} satisfies Meta<EmojiProps>;
|
} satisfies Meta<EmojiProps>;
|
||||||
|
|
||||||
@@ -49,9 +56,3 @@ export const CustomEmoji: Story = {
|
|||||||
code: ':custom:',
|
code: ':custom:',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const LegacyEmoji: Story = {
|
|
||||||
args: {
|
|
||||||
code: ':copyright:',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -83,12 +83,8 @@ describe('stringToEmojiState', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('returns custom emoji state for valid custom emoji', () => {
|
test('returns null for custom emoji without data', () => {
|
||||||
expect(stringToEmojiState(':smile:')).toEqual({
|
expect(stringToEmojiState(':smile:')).toBeNull();
|
||||||
type: 'custom',
|
|
||||||
code: 'smile',
|
|
||||||
data: undefined,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('returns custom emoji state with data when provided', () => {
|
test('returns custom emoji state with data when provided', () => {
|
||||||
@@ -108,7 +104,6 @@ describe('stringToEmojiState', () => {
|
|||||||
|
|
||||||
test('returns null for invalid emoji strings', () => {
|
test('returns null for invalid emoji strings', () => {
|
||||||
expect(stringToEmojiState('notanemoji')).toBeNull();
|
expect(stringToEmojiState('notanemoji')).toBeNull();
|
||||||
expect(stringToEmojiState(':invalid-emoji:')).toBeNull();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -142,21 +137,13 @@ describe('loadEmojiDataToState', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('loads custom emoji data into state', async () => {
|
test('returns null for custom emoji without data', async () => {
|
||||||
const dbCall = vi
|
|
||||||
.spyOn(db, 'loadCustomEmojiByShortcode')
|
|
||||||
.mockResolvedValueOnce(customEmojiFactory());
|
|
||||||
const customState = {
|
const customState = {
|
||||||
type: 'custom',
|
type: 'custom',
|
||||||
code: 'smile',
|
code: 'smile',
|
||||||
} as const satisfies EmojiStateCustom;
|
} as const satisfies EmojiStateCustom;
|
||||||
const result = await loadEmojiDataToState(customState, 'en');
|
const result = await loadEmojiDataToState(customState, 'en');
|
||||||
expect(dbCall).toHaveBeenCalledWith('smile');
|
expect(result).toBeNull();
|
||||||
expect(result).toEqual({
|
|
||||||
type: 'custom',
|
|
||||||
code: 'smile',
|
|
||||||
data: customEmojiFactory(),
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('loads unicode data using legacy shortcode', async () => {
|
test('loads unicode data using legacy shortcode', async () => {
|
||||||
@@ -194,16 +181,6 @@ describe('loadEmojiDataToState', () => {
|
|||||||
expect(result).toBeNull();
|
expect(result).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('returns null if custom emoji not found in database', async () => {
|
|
||||||
vi.spyOn(db, 'loadCustomEmojiByShortcode').mockResolvedValueOnce(undefined);
|
|
||||||
const customState = {
|
|
||||||
type: 'custom',
|
|
||||||
code: 'smile',
|
|
||||||
} as const satisfies EmojiStateCustom;
|
|
||||||
const result = await loadEmojiDataToState(customState, 'en');
|
|
||||||
expect(result).toBeNull();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('retries loading emoji data once if initial load fails', async () => {
|
test('retries loading emoji data once if initial load fails', async () => {
|
||||||
const dbCall = vi
|
const dbCall = vi
|
||||||
.spyOn(db, 'loadEmojiByHexcode')
|
.spyOn(db, 'loadEmojiByHexcode')
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import {
|
|||||||
EMOJI_TYPE_CUSTOM,
|
EMOJI_TYPE_CUSTOM,
|
||||||
} from './constants';
|
} from './constants';
|
||||||
import {
|
import {
|
||||||
loadCustomEmojiByShortcode,
|
|
||||||
loadEmojiByHexcode,
|
loadEmojiByHexcode,
|
||||||
loadLegacyShortcodesByShortcode,
|
loadLegacyShortcodesByShortcode,
|
||||||
LocaleNotLoadedError,
|
LocaleNotLoadedError,
|
||||||
@@ -80,7 +79,7 @@ export function tokenizeText(text: string): TokenizedText {
|
|||||||
export function stringToEmojiState(
|
export function stringToEmojiState(
|
||||||
code: string,
|
code: string,
|
||||||
customEmoji: ExtraCustomEmojiMap = {},
|
customEmoji: ExtraCustomEmojiMap = {},
|
||||||
): EmojiState | null {
|
): EmojiStateUnicode | Required<EmojiStateCustom> | null {
|
||||||
if (isUnicodeEmoji(code)) {
|
if (isUnicodeEmoji(code)) {
|
||||||
return {
|
return {
|
||||||
type: EMOJI_TYPE_UNICODE,
|
type: EMOJI_TYPE_UNICODE,
|
||||||
@@ -90,12 +89,14 @@ export function stringToEmojiState(
|
|||||||
|
|
||||||
if (isCustomEmoji(code)) {
|
if (isCustomEmoji(code)) {
|
||||||
const shortCode = code.slice(1, -1);
|
const shortCode = code.slice(1, -1);
|
||||||
|
if (customEmoji[shortCode]) {
|
||||||
return {
|
return {
|
||||||
type: EMOJI_TYPE_CUSTOM,
|
type: EMOJI_TYPE_CUSTOM,
|
||||||
code: shortCode,
|
code: shortCode,
|
||||||
data: customEmoji[shortCode],
|
data: customEmoji[shortCode],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -115,11 +116,15 @@ export async function loadEmojiDataToState(
|
|||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Don't try to load data for custom emoji.
|
||||||
|
if (state.type === EMOJI_TYPE_CUSTOM) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// First, try to load the data from IndexedDB.
|
// First, try to load the data from IndexedDB.
|
||||||
try {
|
try {
|
||||||
const legacyCode = await loadLegacyShortcodesByShortcode(state.code);
|
const legacyCode = await loadLegacyShortcodesByShortcode(state.code);
|
||||||
// This is duplicative, but that's because TS can't distinguish the state type easily.
|
// This is duplicative, but that's because TS can't distinguish the state type easily.
|
||||||
if (state.type === EMOJI_TYPE_UNICODE || legacyCode) {
|
|
||||||
const data = await loadEmojiByHexcode(
|
const data = await loadEmojiByHexcode(
|
||||||
legacyCode?.hexcode ?? state.code,
|
legacyCode?.hexcode ?? state.code,
|
||||||
locale,
|
locale,
|
||||||
@@ -133,15 +138,7 @@ export async function loadEmojiDataToState(
|
|||||||
shortcode: legacyCode?.shortcodes.at(0),
|
shortcode: legacyCode?.shortcodes.at(0),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
const data = await loadCustomEmojiByShortcode(state.code);
|
|
||||||
if (data) {
|
|
||||||
return {
|
|
||||||
...state,
|
|
||||||
data,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If not found, assume it's not an emoji and return null.
|
// If not found, assume it's not an emoji and return null.
|
||||||
log(
|
log(
|
||||||
'Could not find emoji %s of type %s for locale %s',
|
'Could not find emoji %s of type %s for locale %s',
|
||||||
|
|||||||
@@ -128,8 +128,8 @@ export function customEmojiFactory(
|
|||||||
): CustomEmojiData {
|
): CustomEmojiData {
|
||||||
return {
|
return {
|
||||||
shortcode: 'custom',
|
shortcode: 'custom',
|
||||||
static_url: 'emoji/custom/static',
|
static_url: '/custom-emoji/logo.svg',
|
||||||
url: 'emoji/custom',
|
url: '/custom-emoji/logo.svg',
|
||||||
visible_in_picker: true,
|
visible_in_picker: true,
|
||||||
...data,
|
...data,
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user