UNPKG

@kirz/react-native-toolkit

Version:

Toolkit to speed up React Native development

204 lines (203 loc) 9.23 kB
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } import { PortalHost, PortalProvider } from '@gorhom/portal'; import chalk from 'chalk'; import PQueue from 'p-queue'; import React, { useCallback, useRef, useState } from 'react'; import { Alert, Button, StatusBar, Text, View } from 'react-native'; import { useAsyncEffect } from 'use-async-effect'; import { AppSplashScreen } from '../components/AppSplashScreen'; import { DropDownProvider } from '../contexts/DropDownContext'; import { InAppPurchaseProvider } from '../contexts/InAppPurchaseContext'; import { PluginsBundleProvider } from '../contexts/PluginsBundleContext'; import { AlertsProvider, ControlledPromise, PromiseUtils, scaleX, scaleY, timeout } from '../index'; import { PluginsBundle } from '../plugins/Plugin'; const chalkCtx = new chalk.Instance({ level: 3 }); export function AppBootstrapper(_ref) { let { children, plugins, splashScreenProps } = _ref; const [isInitialized, setIsInitialized] = useState(false); const [isRetrying, setIsRetrying] = useState(false); const [initializationError, setInitializationError] = useState(null); const [pluginsBundle, setPluginsBundle] = useState(new PluginsBundle([])); const initializedPlugins = useRef([]); const ErrorFallbackScreen = useRef(null); const isInitializedRef = useRef(false); const initialize = useCallback(async function () { let async = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; if (!plugins) { return; } const initializePlugin = async (plugin, getBundle, group, id) => { const initializationStartTime = new Date().valueOf(); if ('dependsOn' in plugin && plugin.dependsOn && plugin.dependsOn.length) { await PromiseUtils.waitUntil(() => !plugin.dependsOn.find(x => !initializedPlugins.current.find(y => y.plugin.name === x))); } try { if ('useValue' in plugin) { await timeout(plugin.useValue.initialize(getBundle()), plugin.timeout === null ? null : plugin.timeout ?? plugin.useValue.initializationTimeout, `${plugin.useValue.name} timeout error`); initializedPlugins.current.push({ plugin: plugin.useValue, id }); } else if ('useFactory' in plugin) { const initializedPlugin = await timeout(plugin.useFactory(getBundle()), plugin.timeout === null ? null : plugin.timeout, `${plugin.name} timeout error`); if (initializedPlugin) { await timeout(initializedPlugin.initialize(getBundle()), plugin.timeout === null ? null : plugin.timeout ?? initializedPlugin.initializationTimeout, `${initializedPlugin.name} timeout error`); initializedPlugins.current.push({ plugin: initializedPlugin, id }); } } else if ('useDeferredFactory' in plugin) { const promise = new ControlledPromise(); const pluginName = plugin.name ?? 'DeferredPlugin'; const initializedPlugin = await timeout(plugin.useDeferredFactory(getBundle(), promise.resolve, promise.reject), plugin.timeout, `${pluginName} timeout error`); const [, additionalData] = await timeout(Promise.all([initializedPlugin === null || initializedPlugin === void 0 ? void 0 : initializedPlugin.initialize(getBundle()), promise.wait()]), plugin.timeout === null ? null : plugin.timeout ?? (initializedPlugin === null || initializedPlugin === void 0 ? void 0 : initializedPlugin.initializationTimeout), `${pluginName} timeout error`); if (initializedPlugin) { initializedPlugin.payload = additionalData; initializedPlugins.current.push({ plugin: initializedPlugin, id }); } } else { initializedPlugins.current.push({ plugin, id }); } const lastPlugin = initializedPlugins.current[initializedPlugins.current.length - 1].plugin; console.info([chalkCtx[group ? 'blue' : 'yellow'](`[${lastPlugin.name}]`), chalkCtx.green(`Plugin${group ? ` {group: ${group}} ` : ' '}initialized`), chalkCtx[group ? 'blue' : 'yellow'](`+${(new Date().valueOf() - initializationStartTime).toFixed(0)}ms`)].join(' ')); } catch (err) { if (err) { const errorMessage = err instanceof Error ? err.message : err.toString(); if (errorMessage.includes('timeout error')) { const pluginName = errorMessage.replace(' timeout error', ''); console.error([chalkCtx[group ? 'blue' : 'yellow'](`[${pluginName}]`), chalkCtx.red(`Plugin${group ? ` {group: ${group}} ` : ' '}initialization timeout`), chalkCtx[group ? 'blue' : 'yellow'](`+${(new Date().valueOf() - initializationStartTime).toFixed(0)}ms`)].join(' ')); } // eslint-disable-next-line @typescript-eslint/no-throw-literal throw err; } throw err; } }; const asyncQueue = {}; for (let i = 0; i < plugins.length; i += 1) { const plugin = plugins[i]; const id = i; if (initializedPlugins.current.find(y => y.id === id)) { continue; } try { if ('group' in plugin && plugin.group && async) { if (!asyncQueue[plugin.group]) { asyncQueue[plugin.group] = new PQueue({ concurrency: 1 }); } const queue = asyncQueue[plugin.group]; queue.add(async () => { try { await initializePlugin(plugin, () => new PluginsBundle(initializedPlugins.current.map(x => x.plugin)), plugin.group, id); } catch { // no-op } if (isInitializedRef.current && !queue.size) { setPluginsBundle(new PluginsBundle(initializedPlugins.current.map(x => x.plugin))); } }); continue; } await initializePlugin(plugin, () => new PluginsBundle(initializedPlugins.current.map(x => x.plugin)), null, id); } catch (err) { if ('optional' in plugin && plugin.optional) { continue; } const errorMessage = err instanceof Error ? err.message : err.toString(); if (errorMessage.includes('timeout error')) { // no-op } else { console.error(errorMessage); } ErrorFallbackScreen.current = 'fallbackScreen' in plugin ? plugin.fallbackScreen : null; setInitializationError(errorMessage); throw new Error(errorMessage); } } setPluginsBundle(new PluginsBundle(initializedPlugins.current.map(x => x.plugin))); isInitializedRef.current = true; }, [plugins]); const retryInitialization = useCallback(async () => { try { setIsRetrying(true); await initialize(false); setInitializationError(null); } catch (err) { Alert.alert('Error', err.message); } finally { setIsRetrying(false); } }, [initialize]); useAsyncEffect(async () => { try { await initialize(true); } catch { // no-op } finally { setIsInitialized(true); } }, []); const renderError = useCallback(() => { if (!initializationError) { return null; } if (ErrorFallbackScreen.current) { return /*#__PURE__*/React.createElement(ErrorFallbackScreen.current, { error: initializationError, isRetrying: isRetrying, retry: retryInitialization }); } return /*#__PURE__*/React.createElement(View, { style: { flex: 1, alignItems: 'center', justifyContent: 'center' } }, /*#__PURE__*/React.createElement(StatusBar, { barStyle: "dark-content" }), /*#__PURE__*/React.createElement(Text, { style: { fontSize: scaleX(20), marginBottom: scaleY(5) } }, initializationError), /*#__PURE__*/React.createElement(Button, { disabled: isRetrying, onPress: retryInitialization, title: "Retry" })); }, [initializationError, ErrorFallbackScreen, isRetrying, retryInitialization]); return /*#__PURE__*/React.createElement(AppSplashScreen, _extends({ visible: !isInitialized }, splashScreenProps), !initializationError ? /*#__PURE__*/React.createElement(PortalProvider, null, /*#__PURE__*/React.createElement(PluginsBundleProvider, { bundle: pluginsBundle, retryInitialization: retryInitialization }, /*#__PURE__*/React.createElement(AlertsProvider, null, /*#__PURE__*/React.createElement(DropDownProvider, null, /*#__PURE__*/React.createElement(InAppPurchaseProvider, null, children, /*#__PURE__*/React.createElement(View, { pointerEvents: "box-none", style: { position: 'absolute', left: 0, top: 0, bottom: 0, right: 0 } }, /*#__PURE__*/React.createElement(PortalHost, { name: "ModalHost" }))))))) : renderError()); } //# sourceMappingURL=AppBootstrapper.js.map