import { CodedError, UnavailabilityError } from '@unimodules/core'; import ExpoFontLoader from './ExpoFontLoader'; import { FontDisplay } from './Font.types'; import { getAssetForSource, loadSingleFontAsync, fontFamilyNeedsScoping, getNativeFontName, } from './FontLoader'; const loaded = {}; const loadPromises = {}; /** * Used to transform font family names to the scoped name. This does not need to * be called in standalone or bare apps but it will return unscoped font family * names if it is called in those contexts. * note(brentvatne): at some point we may want to warn if this is called * outside of a managed app. * * @param fontFamily name to process * @returns a name processed for use with the [current workflow](https://docs.expo.io/versions/latest/introduction/managed-vs-bare/) */ export function processFontFamily(fontFamily) { if (!fontFamily || !fontFamilyNeedsScoping(fontFamily)) { return fontFamily; } if (!isLoaded(fontFamily)) { if (__DEV__) { if (isLoading(fontFamily)) { console.error(`You started loading the font "${fontFamily}", but used it before it finished loading.\n - You need to wait for Font.loadAsync to complete before using the font.\n - We recommend loading all fonts before rendering the app, and rendering only Expo.AppLoading while waiting for loading to complete.`); } else { console.error(`fontFamily "${fontFamily}" is not a system font and has not been loaded through Font.loadAsync.\n - If you intended to use a system font, make sure you typed the name correctly and that it is supported by your device operating system.\n - If this is a custom font, be sure to load it with Font.loadAsync.`); } } return 'System'; } return `ExpoFont-${getNativeFontName(fontFamily)}`; } /** * Synchronously detect if the font for `fontFamily` has finished loading * * @param fontFamily the name used to load the `FontResource`. * @returns `true` if the the font has fully loaded. */ export function isLoaded(fontFamily) { return fontFamily in loaded; } /** * Synchronously detect if the font for `fontFamily` is still being loaded * * @param fontFamily the name used to load the `FontResource`. * @returns `true` if the the font is still loading. */ export function isLoading(fontFamily) { return fontFamily in loadPromises; } /** * Natively load a font for use with Text elements. * @param fontFamilyOrFontMap string or map of values that can be used as the [`fontFamily`](https://reactnative.dev/docs/text#style) style prop with React Native Text elements. * @param source the font asset that should be loaded into the `fontFamily` namespace. */ export async function loadAsync(fontFamilyOrFontMap, source) { if (typeof fontFamilyOrFontMap === 'object') { if (source) { throw new CodedError(`ERR_FONT_API`, `No fontFamily can be used for the provided source: ${source}. The second argument of \`loadAsync()\` can only be used with a \`string\` value as the first argument.`); } const fontMap = fontFamilyOrFontMap; const names = Object.keys(fontMap); await Promise.all(names.map(name => loadFontInNamespaceAsync(name, fontMap[name]))); return; } return await loadFontInNamespaceAsync(fontFamilyOrFontMap, source); } async function loadFontInNamespaceAsync(fontFamily, source) { if (!source) { throw new CodedError(`ERR_FONT_SOURCE`, `Cannot load null or undefined font source: { "${fontFamily}": ${source} }. Expected asset of type \`FontSource\` for fontFamily of name: "${fontFamily}"`); } if (loaded[fontFamily]) { return; } if (loadPromises[fontFamily]) { return loadPromises[fontFamily]; } // Important: we want all callers that concurrently try to load the same font to await the same // promise. If we're here, we haven't created the promise yet. To ensure we create only one // promise in the program, we need to create the promise synchronously without yielding the event // loop from this point. const asset = getAssetForSource(source); loadPromises[fontFamily] = (async () => { try { await loadSingleFontAsync(fontFamily, asset); loaded[fontFamily] = true; } finally { delete loadPromises[fontFamily]; } })(); await loadPromises[fontFamily]; } /** * Unloads all of the custom fonts. This is used for testing. */ export async function unloadAllAsync() { if (!ExpoFontLoader.unloadAllAsync) { throw new UnavailabilityError('expo-font', 'unloadAllAsync'); } if (Object.keys(loadPromises).length) { throw new CodedError(`ERR_UNLOAD`, `Cannot unload fonts while they're still loading: ${Object.keys(loadPromises).join(', ')}`); } for (const fontFamily of Object.keys(loaded)) { delete loaded[fontFamily]; } await ExpoFontLoader.unloadAllAsync(); } /** * Unload custom fonts matching the `fontFamily`s and display values provided. * Because fonts are automatically unloaded on every platform this is mostly used for testing. * * @param fontFamilyOrFontMap the names of the custom fonts that will be unloaded. * @param source when `fontFamilyOrFontMap` is a string, this should be the font source used to load the custom font originally. */ export async function unloadAsync(fontFamilyOrFontMap, options) { if (!ExpoFontLoader.unloadAsync) { throw new UnavailabilityError('expo-font', 'unloadAsync'); } if (typeof fontFamilyOrFontMap === 'object') { if (options) { throw new CodedError(`ERR_FONT_API`, `No fontFamily can be used for the provided options: ${options}. The second argument of \`unloadAsync()\` can only be used with a \`string\` value as the first argument.`); } const fontMap = fontFamilyOrFontMap; const names = Object.keys(fontMap); await Promise.all(names.map(name => unloadFontInNamespaceAsync(name, fontMap[name]))); return; } return await unloadFontInNamespaceAsync(fontFamilyOrFontMap, options); } async function unloadFontInNamespaceAsync(fontFamily, options) { if (!loaded[fontFamily]) { return; } else { delete loaded[fontFamily]; } // Important: we want all callers that concurrently try to load the same font to await the same // promise. If we're here, we haven't created the promise yet. To ensure we create only one // promise in the program, we need to create the promise synchronously without yielding the event // loop from this point. const nativeFontName = getNativeFontName(fontFamily); if (!nativeFontName) { throw new CodedError(`ERR_FONT_FAMILY`, `Cannot unload an empty name`); } await ExpoFontLoader.unloadAsync(nativeFontName, options); } export { FontDisplay }; //# sourceMappingURL=Font.js.map