import { Image, Platform } from 'react-native'; import { getAssetByID } from './asset-registry'; import { assertExpoModulesPresent, getErrorCallback, type LoadAsyncAsset } from './dynamic-loading-setting'; import type { DynamicLoader, FontSource } from './types'; const loadPromises: { [fontSource: string]: Promise } = {}; const loadFontAsync = async (fontFamily: string, fontSource: FontSource): Promise => { const globalRef = globalThis; assertExpoModulesPresent(globalRef); const expoModules = globalRef.expo.modules; if (loadPromises[fontFamily]) { return loadPromises[fontFamily]; } loadPromises[fontFamily] = (async function LoadFont() { try { const localUri = await (() => { if (typeof fontSource === 'string') { // a local filesystem uri return fontSource; } // a module id const { uri, type, hash } = getLocalFontUrl(fontSource, fontFamily); return expoModules.ExpoAsset.downloadAsync(uri, hash, type); })(); const asset = Platform.select({ web: { uri: localUri, display: 'auto' }, default: localUri, }); await expoModules.ExpoFontLoader.loadAsync(fontFamily, asset); } catch (error) { console.error(`Failed to load font ${fontFamily}`, error); getErrorCallback()?.({ error: error as Error, fontFamily, fontSource, }); } finally { delete loadPromises[fontFamily]; } })(); return loadPromises[fontFamily]; }; const getLocalFontUrl = (fontModuleId: number, fontFamily: string) => { const assetMeta = getAssetByID(fontModuleId); if (!assetMeta) { throw new Error(`no asset found for font family "${fontFamily}", moduleId: ${String(fontModuleId)}`); } const assetSource = Image.resolveAssetSource(fontModuleId); return { ...assetMeta, ...assetSource }; }; const loadedFontsCache: { [name: string]: boolean } = {}; const isLoadedNative = (fontFamily: string) => { if (fontFamily in loadedFontsCache) { return true; } const globalRef = globalThis; assertExpoModulesPresent(globalRef); const loadedNativeFonts = globalRef.expo.modules.ExpoFontLoader.getLoadedFonts(); loadedNativeFonts.forEach((font) => { loadedFontsCache[font] = true; }); return fontFamily in loadedFontsCache; }; export const dynamicLoader: DynamicLoader = { isLoaded: isLoadedNative, loadFontAsync, };