150 lines
4.0 KiB
TypeScript

import { initialState } from '@/mastodon/initial_state';
import { EMOJI_DB_RELOAD_EVENT } from './constants';
import { toSupportedLocale } from './locale';
import type { EmojiWorkerMessage } from './types';
import { emojiLogger } from './utils';
const userLocale = toSupportedLocale(initialState?.meta.locale ?? 'en');
let worker: Worker | null = null;
const log = emojiLogger('index');
const workerLog = emojiLogger('worker');
// This is too short, but better to fallback quickly than wait.
const WORKER_TIMEOUT = 2_000;
// Handle reload events
window.addEventListener(
EMOJI_DB_RELOAD_EVENT,
() => void handleEmojiDbReload(),
);
export async function initializeEmoji() {
log('initializing emojis');
// Create a temp worker, and assign it to the module-level worker once we know it's ready.
let tempWorker: Worker | null = null;
if (!worker && 'Worker' in window) {
try {
const { default: EmojiWorker } = await import('./worker?worker&inline');
tempWorker = new EmojiWorker();
} catch (err) {
console.warn('Error creating web worker:', err);
}
}
if (!tempWorker) {
void fallbackLoad();
return;
}
const timeoutId = setTimeout(() => {
log('worker is not ready after timeout');
void fallbackLoad();
}, WORKER_TIMEOUT);
tempWorker.addEventListener(
'message',
(event: MessageEvent<EmojiWorkerMessage>) => {
const { data: message } = event;
worker ??= tempWorker;
clearTimeout(timeoutId);
const { type } = message;
if (type === 'log') {
workerLog(message.message);
} else if (type === 'done' && message.storeName === 'custom') {
void loadEmojisToStore();
} else if (type === 'db-blocked') {
window.dispatchEvent(new Event(EMOJI_DB_RELOAD_EVENT));
}
if (type !== 'ready') {
return; // Exit for other messages.
}
const debugValue = localStorage.getItem('debug');
if (debugValue) {
messageWorker({ type: 'debug', debugValue });
}
workerLog('loading data');
messageWorker(userLocale);
messageWorker('custom');
messageWorker('shortcodes');
void loadEmojisToStore();
},
);
}
async function fallbackLoad() {
log('falling back to main thread for loading');
const { importCustomEmojiData, importLegacyShortcodes, importEmojiData } =
await import('./loader');
const customEmojis = await importCustomEmojiData();
if (customEmojis && customEmojis.length > 0) {
log('loaded %d custom emojis', customEmojis.length);
}
const shortcodes = await importLegacyShortcodes();
if (shortcodes?.length) {
log('loaded %d legacy shortcodes', shortcodes.length);
}
const emojis = await importEmojiData(userLocale);
if (emojis) {
log('loaded %d emojis to locale %s', emojis.length, userLocale);
}
await loadEmojisToStore();
}
export async function loadCustomEmoji() {
if (worker) {
messageWorker('custom');
} else {
const { importCustomEmojiData } = await import('./loader');
const emojis = await importCustomEmojiData();
if (emojis && emojis.length > 0) {
log('loaded %d custom emojis', emojis.length);
}
}
await loadEmojisToStore();
}
function messageWorker(data: EmojiWorkerMessage | string) {
if (!worker) {
return;
}
if (typeof data === 'string') {
worker.postMessage({
type: 'load',
storeName: data,
} satisfies EmojiWorkerMessage);
} else {
worker.postMessage(data);
}
}
async function loadEmojisToStore() {
const { store } = await import('@/mastodon/store');
const { loadCustomEmojis, loadLocale } =
await import('@/mastodon/reducers/slices/emojis');
loadLocale(userLocale);
await store.dispatch(loadCustomEmojis());
log('loaded emoji data into store');
}
async function handleEmojiDbReload() {
log('Emoji database reload needed, triggering warning');
const { store } = await import('@/mastodon/store');
const { needsReload } = await import('@/mastodon/actions/app');
store.dispatch(needsReload());
}