UNPKG

react-native-unit-components

Version:

Unit React Native components

175 lines (174 loc) 8.12 kB
/* eslint-disable @typescript-eslint/no-explicit-any */ import React, { useEffect, useImperativeHandle, useRef, useState } from 'react'; import { Linking, Platform } from 'react-native'; import { useDispatch, useSelector } from 'react-redux'; import { WebView } from 'react-native-webview'; import html, { HTML_PLACEHOLDER } from './html'; import { UnitComponentsMessage } from '../messages/webMessages/unitComponentsMessages'; import { getHtmlBody } from '../scripts/html/bodyHtml'; import { fetchUnitScript } from '../unitComponentsSdkManager/UnitComponentsSdk.api'; import { UnitComponentsSDK } from '../unitComponentsSdkManager/UnitComponentsSdkManager'; import { getInfoParams, handleRequestDownload, injectEventToContinue } from './WebComponent.utils'; import { WebComponentType } from '../types/internal/webComponent.types'; import { getFontFacesString } from '../scripts/html/fontFaces'; import { setEvent } from '../slices/SharedEventsSlice'; import AppInfo from '../utils/AppInfo'; import UNStoreManagerHelper from '../nativeModulesHelpers/UNStoreModuleHelper/UNStoreModuleHelper'; import { UserDataKeys } from '../types/internal/unitStore.types'; import { useListenerToEvent } from '../hooks/useListenerToEvent'; import { setItemInWindowUnitStore } from '../utils/windowUnitStore'; export const WebComponent = /*#__PURE__*/React.forwardRef(function WebComponent(props, webOutRef) { const dispatch = useDispatch(); const unitScript = useSelector(state => state.configuration.unitScript); const globalTheme = useSelector(state => state.configuration.theme); const globalLanguage = useSelector(state => state.configuration.language); const customerToken = useSelector(state => state.configuration.customerToken); const [sourceHtml, setSourceHtml] = useState(null); const [baseName, setBaseName] = useState(); const [infoParams, setInfoParams] = useState({}); const webRef = useRef(null); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion useImperativeHandle(webOutRef, () => webRef.current); useEffect(() => { const getAppName = async () => { // For iOS, we extend the app name from the HTML to display a prettier access request message. // On Android, there is no request message sent from HTML. try { if (Platform.OS == 'ios') { const name = await AppInfo.getAppName(); setBaseName(name.replace(/ /g, '-')); } else { // android setBaseName('unit'); } } catch (error) { console.error(error); } }; const updateInfoParams = async () => { const infoParams = await getInfoParams(); setInfoParams(infoParams); }; getAppName(); updateInfoParams(); }, []); useEffect(() => { if (!unitScript) { fetchUnitScript(); return; } const updateSourceHTML = async () => { const componentCurrentTheme = props.theme ?? globalTheme; const componentCurrentLanguage = props.language ?? globalLanguage; const themeParam = componentCurrentTheme ? ` theme="${componentCurrentTheme}"` : ''; const languageParam = componentCurrentLanguage ? ` language="${componentCurrentLanguage}"` : ''; const componentsRequiresExternalTokenInsertion = [WebComponentType.whiteLabelApp]; const customerTokenParam = componentsRequiresExternalTokenInsertion.includes(props.type) ? '' : `customer-token="${customerToken}"\n`; const componentParams = customerTokenParam + (props.params || '') + themeParam + languageParam; const fontFaces = getFontFacesString(UnitComponentsSDK.getFonts()); const windowInfoParams = `window.UnitMobileSDKConfig['info'] = ${JSON.stringify(infoParams)};`; const plaidRedirectUriParam = Platform.OS == 'ios' && UnitComponentsSDK.helpers.redirectUri ? `window.UnitMobileSDKConfig['plaidRedirectUri'] = '${UnitComponentsSDK.helpers.redirectUri}/plaid';` : ''; const unitSessionIdParam = `window.UnitSessionStore.unitSessionId = '${UnitComponentsSDK.helpers.unitSessionId}';`; const unitVerifiedCustomerToken = await UNStoreManagerHelper.getValue(UserDataKeys.unitVerifiedToken); const windowVerifiedCustomerToken = unitVerifiedCustomerToken ? `window.UnitStore['unitVerifiedCustomerToken'] = '${unitVerifiedCustomerToken}';` : ''; const windowParams = `${windowInfoParams} ${unitSessionIdParam} ${plaidRedirectUriParam} ${windowVerifiedCustomerToken} ${props.windowParams || ''}`; let newHtml = html.replace(HTML_PLACEHOLDER.BODY, getHtmlBody(props.type.valueOf(), componentParams, props.presentationMode)); newHtml = newHtml.replace(HTML_PLACEHOLDER.FONT_FACES, fontFaces); newHtml = newHtml.replace(HTML_PLACEHOLDER.SCRIPT_FROM_NATIVE, props.script || ''); newHtml = newHtml.replace(HTML_PLACEHOLDER.WINDOW_PARAMS, windowParams); setSourceHtml(newHtml); }; updateSourceHTML(); }, [props.params, unitScript, props.presentationMode, props.script, props.windowParams, globalTheme, globalLanguage, customerToken, infoParams]); // Listen and update the live webComponents const handleMultiFactorAuthFinished = data => { setItemInWindowUnitStore(webRef.current, UserDataKeys.unitVerifiedToken, data.unitVerifiedCustomerTokenString); injectEventToContinue(webRef.current, { parentInstanceId: data.parentInstanceId, eventToContinue: data.eventToContinue }); }; useListenerToEvent({ busEventKey: UnitComponentsMessage.UNIT_MULTI_FACTOR_AUTH_FINISHED, action: handleMultiFactorAuthFinished }); const onMessage = e => { const message = JSON.parse(e.nativeEvent.data); switch (message.type) { case UnitComponentsMessage.UNIT_REQUEST_REFRESH: message.details && dispatch(setEvent({ key: UnitComponentsMessage.UNIT_REQUEST_REFRESH, data: message.details })); break; case UnitComponentsMessage.UNIT_REQUEST_OPEN_LINK: // eslint-disable-next-line no-case-declarations const { href } = message.details; Linking.openURL(href); break; case UnitComponentsMessage.UNIT_REQUEST_DOWNLOAD: message.details && handleRequestDownload(message.details, () => { dispatch(setEvent({ key: UnitComponentsMessage.UNIT_REQUEST_CLOSE_FLOW, data: {} })); }); break; case UnitComponentsMessage.UNIT_MULTI_FACTOR_AUTH_FINISHED: if (message.details) { const data = message.details; UNStoreManagerHelper.saveValue(UserDataKeys.unitVerifiedToken, data.unitVerifiedCustomerTokenString); // update existing components - namely, the other webComponents will update their window as well as this webComponent dispatch(setEvent({ key: UnitComponentsMessage.UNIT_MULTI_FACTOR_AUTH_FINISHED, data })); } props.onMessage && props.onMessage(message); break; case UnitComponentsMessage.UNIT_UNAUTHORIZED_TOKEN: UnitComponentsSDK.cleanUserData(); break; default: props.onMessage && props.onMessage(message); } }; if (!sourceHtml) return null; const _onScroll = event => { if (props.handleScroll) { props.handleScroll(event); } }; if (!baseName) { return null; } return /*#__PURE__*/React.createElement(WebView, { ref: webRef, originWhitelist: ['*'], mediaPlaybackRequiresUserAction: false, allowsInlineMediaPlayback: true, cacheEnabled: false, scrollEnabled: props.isScrollable, nestedScrollEnabled: props.nestedScrollEnabled, onScroll: _onScroll, overScrollMode: "never", injectedJavaScript: unitScript, style: { width: '100%', flex: 1, opacity: 0.99, backgroundColor: 'transparent' }, source: { html: sourceHtml, baseUrl: `https://${baseName}` }, onMessage: onMessage, androidLayerType: "hardware", webviewDebuggingEnabled: __DEV__, mediaCapturePermissionGrantType: "grant" }); }); //# sourceMappingURL=WebComponent.js.map