react-native-unit-components
Version:
Unit React Native components
175 lines (174 loc) • 8.12 kB
JavaScript
/* 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