-
Notifications
You must be signed in to change notification settings - Fork 2
/
Font.ts
153 lines (128 loc) · 4.35 KB
/
Font.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
import { Asset } from 'expo-asset';
import Constants from 'expo-constants';
import { Platform } from '@unimodules/core';
import ExpoFontLoader from './ExpoFontLoader';
/**
* A font source can be a URI, a module ID, or an Expo Asset.
*/
type FontSource = string | number | Asset;
const isWeb = Platform.OS === 'web';
const loaded: { [name: string]: boolean } = {};
const loadPromises: { [name: string]: Promise<void> } = {};
export function processFontFamily(name: string | null): string | null {
if (typeof name !== 'string' || Constants.systemFonts.includes(name) || name === 'System') {
return name;
}
if (name.includes(Constants.sessionId)) {
return name;
}
if (!isLoaded(name)) {
if (__DEV__) {
if (isLoading(name)) {
console.error(
`You started loading the font "${name}", 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 "${name}" 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(name)}`;
}
export function isLoaded(name: string): boolean {
return loaded.hasOwnProperty(name);
}
export function isLoading(name: string): boolean {
return loadPromises.hasOwnProperty(name);
}
export async function loadAsync(
nameOrMap: string | { [name: string]: FontSource },
source?: FontSource
): Promise<void> {
if (typeof nameOrMap === 'object') {
const fontMap = nameOrMap;
const names = Object.keys(fontMap);
await Promise.all(names.map(name => loadAsync(name, fontMap[name])));
return;
}
const name = nameOrMap;
if (loaded[name]) {
return;
}
if (loadPromises[name]) {
return loadPromises[name];
}
// 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.
if (!source) {
throw new Error(`No source from which to load font "${name}"`);
}
const asset = _getAssetForSource(source);
loadPromises[name] = (async () => {
try {
await _loadSingleFontAsync(name, asset);
loaded[name] = true;
} finally {
delete loadPromises[name];
}
})();
await loadPromises[name];
}
function _getAssetForSource(source: FontSource): Asset {
if (source instanceof Asset) {
return source;
}
if (!isWeb && typeof source === 'string') {
return Asset.fromURI(source);
}
if (isWeb || typeof source === 'number') {
return Asset.fromModule(source);
}
// @ts-ignore Error: Type 'string' is not assignable to type 'Asset'
// We can't have a string here, we would have thrown an error if !isWeb
// or returned Asset.fromModule if isWeb.
return source;
}
async function _loadSingleFontAsync(name: string, asset: Asset): Promise<void> {
await asset.downloadAsync();
if (!asset.downloaded) {
throw new Error(`Failed to download asset for font "${name}"`);
}
await ExpoFontLoader.loadAsync(_getNativeFontName(name), asset.localUri);
}
function _getNativeFontName(name: string): string {
if (isWeb) {
return name;
}
return `${Constants.sessionId}-${name}`;
}
declare var module: any;
if (module && module.exports) {
let wasImportWarningShown = false;
// @ts-ignore: Temporarily define an export named "Font" for legacy compatibility
Object.defineProperty(exports, 'Font', {
get() {
if (!wasImportWarningShown) {
console.warn(
`The syntax "import { Font } from 'expo-font'" is deprecated. Use "import * as Font from 'expo-font'" or import named exports instead. Support for the old syntax will be removed in SDK 33.`
);
wasImportWarningShown = true;
}
return {
processFontFamily,
isLoaded,
isLoading,
loadAsync,
};
},
});
}