1 | import { Asset } from 'expo-asset';
|
2 | import Constants from 'expo-constants';
|
3 | import { Platform } from '@unimodules/core';
|
4 |
|
5 | import ExpoFontLoader from './ExpoFontLoader';
|
6 |
|
7 |
|
8 |
|
9 |
|
10 | type FontSource = string | number | Asset;
|
11 |
|
12 | const isWeb = Platform.OS === 'web';
|
13 | const isInClient = !isWeb && Constants.appOwnership === 'expo';
|
14 | const isInIOSStandalone = Constants.appOwnership === 'standalone' && Platform.OS === 'ios';
|
15 |
|
16 | const loaded: { [name: string]: boolean } = {};
|
17 | const loadPromises: { [name: string]: Promise<void> } = {};
|
18 |
|
19 | function fontFamilyNeedsScoping(name: string): boolean {
|
20 | return (
|
21 | (isInClient || isInIOSStandalone) &&
|
22 | !Constants.systemFonts.includes(name) &&
|
23 | name !== 'System' &&
|
24 | !name.includes(Constants.sessionId)
|
25 | );
|
26 | }
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 |
|
35 | export function processFontFamily(name: string | null): string | null {
|
36 | if (!name || !fontFamilyNeedsScoping(name)) {
|
37 | return name;
|
38 | }
|
39 |
|
40 | if (!isLoaded(name)) {
|
41 | if (__DEV__) {
|
42 | if (isLoading(name)) {
|
43 | console.error(
|
44 | `You started loading the font "${name}", but used it before it finished loading.\n
|
45 | - You need to wait for Font.loadAsync to complete before using the font.\n
|
46 | - We recommend loading all fonts before rendering the app, and rendering only Expo.AppLoading while waiting for loading to complete.`
|
47 | );
|
48 | } else {
|
49 | console.error(
|
50 | `fontFamily "${name}" is not a system font and has not been loaded through Font.loadAsync.\n
|
51 | - 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
|
52 | - If this is a custom font, be sure to load it with Font.loadAsync.`
|
53 | );
|
54 | }
|
55 | }
|
56 |
|
57 | return 'System';
|
58 | }
|
59 |
|
60 | return `ExpoFont-${_getNativeFontName(name)}`;
|
61 | }
|
62 |
|
63 | export function isLoaded(name: string): boolean {
|
64 | return loaded.hasOwnProperty(name);
|
65 | }
|
66 |
|
67 | export function isLoading(name: string): boolean {
|
68 | return loadPromises.hasOwnProperty(name);
|
69 | }
|
70 |
|
71 | export async function loadAsync(
|
72 | nameOrMap: string | { [name: string]: FontSource },
|
73 | source?: FontSource
|
74 | ): Promise<void> {
|
75 | if (typeof nameOrMap === 'object') {
|
76 | const fontMap = nameOrMap;
|
77 | const names = Object.keys(fontMap);
|
78 | await Promise.all(names.map(name => loadAsync(name, fontMap[name])));
|
79 | return;
|
80 | }
|
81 |
|
82 | const name = nameOrMap;
|
83 |
|
84 | if (loaded[name]) {
|
85 | return;
|
86 | }
|
87 |
|
88 | if (loadPromises[name]) {
|
89 | return loadPromises[name];
|
90 | }
|
91 |
|
92 |
|
93 |
|
94 |
|
95 |
|
96 |
|
97 | if (!source) {
|
98 | throw new Error(`No source from which to load font "${name}"`);
|
99 | }
|
100 | const asset = _getAssetForSource(source);
|
101 | loadPromises[name] = (async () => {
|
102 | try {
|
103 | await _loadSingleFontAsync(name, asset);
|
104 | loaded[name] = true;
|
105 | } finally {
|
106 | delete loadPromises[name];
|
107 | }
|
108 | })();
|
109 |
|
110 | await loadPromises[name];
|
111 | }
|
112 |
|
113 | function _getAssetForSource(source: FontSource): Asset {
|
114 | if (source instanceof Asset) {
|
115 | return source;
|
116 | }
|
117 |
|
118 | if (!isWeb && typeof source === 'string') {
|
119 | return Asset.fromURI(source);
|
120 | }
|
121 |
|
122 | if (isWeb || typeof source === 'number') {
|
123 | return Asset.fromModule(source);
|
124 | }
|
125 |
|
126 | // @ts-ignore Error: Type 'string' is not assignable to type 'Asset'
|
127 | // We can't have a string here, we would have thrown an error if !isWeb
|
128 | // or returned Asset.fromModule if isWeb.
|
129 | return source;
|
130 | }
|
131 |
|
132 | async function _loadSingleFontAsync(name: string, asset: Asset): Promise<void> {
|
133 | await asset.downloadAsync();
|
134 | if (!asset.downloaded) {
|
135 | throw new Error(`Failed to download asset for font "${name}"`);
|
136 | }
|
137 | await ExpoFontLoader.loadAsync(_getNativeFontName(name), asset.localUri);
|
138 | }
|
139 |
|
140 | function _getNativeFontName(name: string): string {
|
141 | if (fontFamilyNeedsScoping(name)) {
|
142 | return `${Constants.sessionId}-${name}`;
|
143 | } else {
|
144 | return name;
|
145 | }
|
146 | }
|
147 |
|
148 | declare var module: any;
|
149 |
|
150 | if (module && module.exports) {
|
151 | let wasImportWarningShown = false;
|
152 | // @ts-ignore: Temporarily define an export named "Font" for legacy compatibility
|
153 | Object.defineProperty(exports, 'Font', {
|
154 | get() {
|
155 | if (!wasImportWarningShown) {
|
156 | console.warn(
|
157 | `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.`
|
158 | );
|
159 | wasImportWarningShown = true;
|
160 | }
|
161 | return {
|
162 | processFontFamily,
|
163 | isLoaded,
|
164 | isLoading,
|
165 | loadAsync,
|
166 | };
|
167 | },
|
168 | });
|
169 | }
|
170 |
|
\ | No newline at end of file |