UNPKG

4.32 kBJavaScriptView Raw
1// @flow
2
3import invariant from 'invariant';
4import { NativeModules } from 'react-native';
5
6import Asset from './Asset';
7import Constants from './Constants';
8
9type FontSource = string | number | Asset;
10
11const loaded: { [name: string]: boolean } = {};
12const loadPromises: { [name: string]: Promise<void> } = {};
13
14export function processFontFamily(name: ?string): ?string {
15 if (!name || Constants.systemFonts.includes(name) || name === 'System') {
16 return name;
17 }
18
19 if (name.includes(Constants.sessionId)) {
20 return name;
21 }
22
23 if (!isLoaded(name)) {
24 if (__DEV__) {
25 if (isLoading(name)) {
26 console.error(
27 `You started loading '${name}', but used it before it finished loading\n\n` +
28 `- You need to wait for Expo.Font.loadAsync to complete before using the font.\n\n` +
29 `- We recommend loading all fonts before rendering the app, and rendering only ` +
30 `Expo.AppLoading while waiting for loading to complete.`
31 );
32 } else {
33 console.error(
34 `fontFamily '${name}' is not a system font and has not been loaded through ` +
35 `Expo.Font.loadAsync.\n\n` +
36 `- If you intended to use a system font, make sure you typed the name ` +
37 `correctly and that it is supported by your device operating system.\n\n` +
38 `- If this is a custom font, be sure to load it with Expo.Font.loadAsync.`
39 );
40 }
41 }
42
43 return 'System';
44 }
45
46 return `ExponentFont-${_getNativeFontName(name)}`;
47}
48
49export function isLoaded(name: string): boolean {
50 return loaded.hasOwnProperty(name);
51}
52
53export function isLoading(name: string): boolean {
54 return loadPromises.hasOwnProperty(name);
55}
56
57export async function loadAsync(
58 nameOrMap: string | { [string]: FontSource } | Array<{ [string]: FontSource }>,
59 uriOrModuleOrAsset?: FontSource
60): Promise<void> {
61 if (Array.isArray(nameOrMap)) {
62 console.warn(
63 `Passing in an array to Font.loadAsync like Font.loadAsync([fontMap1, fontMap2, fontMap3]) is deprecated and will be removed in SDK 25. Instead, pass in a single font map. The object spread syntax may help with this: Font.loadAsync({ ...fontMap1, ...fontMap2, ...fontMap3 })`
64 );
65 await Promise.all(nameOrMap.map(loadAsync));
66 return;
67 } else if (typeof nameOrMap === 'object') {
68 const fontMap = nameOrMap;
69 const names = Object.keys(fontMap);
70 await Promise.all(names.map(name => loadAsync(name, fontMap[name])));
71 return;
72 }
73
74 const name = nameOrMap;
75
76 if (loaded[name]) {
77 return;
78 }
79
80 if (loadPromises[name]) {
81 return loadPromises[name];
82 }
83
84 // Important: we want all callers that concurrently try to load the same font to await the same
85 // promise. If we're here, we haven't created the promise yet. To ensure we create only one
86 // promise in the program, we need to create the promise synchronously without yielding the event
87 // loop from this point.
88
89 invariant(uriOrModuleOrAsset, `No source from which to load font "${name}"`);
90 const asset = _getAssetForSource(uriOrModuleOrAsset);
91 loadPromises[name] = (async () => {
92 try {
93 await _loadSingleFontAsync(name, asset);
94 loaded[name] = true;
95 } finally {
96 delete loadPromises[name];
97 }
98 })();
99
100 await loadPromises[name];
101}
102
103function _getAssetForSource(uriOrModuleOrAsset: FontSource): Asset {
104 if (typeof uriOrModuleOrAsset === 'string') {
105 // TODO(nikki): need to implement Asset.fromUri(...)
106 // asset = Asset.fromUri(uriOrModuleOrAsset);
107 throw new Error(
108 'Loading fonts from remote URIs is temporarily not supported. Please download the font file and load it using require. See: https://docs.expo.io/versions/latest/guides/using-custom-fonts.html#downloading-the-font'
109 );
110 }
111
112 if (typeof uriOrModuleOrAsset === 'number') {
113 return Asset.fromModule(uriOrModuleOrAsset);
114 }
115
116 return uriOrModuleOrAsset;
117}
118
119async function _loadSingleFontAsync(name: string, asset: Asset): Promise<void> {
120 await asset.downloadAsync();
121 if (!asset.downloaded) {
122 throw new Error(`Failed to download asset for font "${name}"`);
123 }
124
125 await NativeModules.ExponentFontLoader.loadAsync(_getNativeFontName(name), asset.localUri);
126}
127
128function _getNativeFontName(name: string): string {
129 return `${Constants.sessionId}-${name}`;
130}