import React, { useEffect, useImperativeHandle, useRef, useState } from 'react'; import type WebView from 'react-native-webview'; import { HeightEvent, PageMessage } from '../..//messages/webMessages/pageMessage'; import { RequestRefreshEvent, RequestRenderingEvent, UnitComponentsMessage, UnitRequestRefreshEventTypes, } from '../../messages/webMessages/unitComponentsMessages'; import { WebComponent } from '../../webComponent/WebComponent'; import type { WebViewMessage } from '../../messages/webMessages'; import { getAccountParams, getAccountScript, injectOpenActionsMenuScript, injectRefreshEventIfNeeded, injectRequestAccountActionScript, } from './UNAccountComponent.utils'; import type { UNAccount, UNAccountMenuAction, UNAccountMenuItem, UNCreditAccount, UNComponentsError, UNComponentsOnLoadResponse, UNComponentsOnLoadResponseData, UNAccountStatusPosition } from '../../types/shared'; import { AccountMessage } from '../../messages/webMessages/accountMessage'; import { RESPONSE_KEYS, UnitOnLoadResponseEvent } from '../../messages/webMessages/onLoadMessage'; import { BottomSheetRenderingType, SlotRendering, } from '../../types/internal/bottomSheet.types'; import type { BottomSheetSlotData } from '../../types/internal/bottomSheet.types'; import { PresentationMode, WebComponentType } from '../../types/internal/webComponent.types'; import { BottomSheetNativeMessage } from '../../messages/nativeMessages/bottomSheetMessage'; import { withReduxStoreAndRefForwarding } from '../../helpers/store/helpers'; import { eventBus } from '../../utils/eventBus'; import { useEventListener } from '../../hooks/useEventListener'; import { UNBaseView } from '../../nativeComponents/UNBaseView'; export interface UNAccountComponentProps { // inputs accountId?: string; menuItems?: UNAccountMenuItem[]; // ui theme?: string; language?: string; hideActionsMenuButton?: boolean; hideSelectionMenuButton?: boolean; hideAccountStatus?: boolean; accountStatusPosition?: UNAccountStatusPosition; hideAccountDetails?: boolean; hideAccountButtons?: boolean; hideAccountCtaBanner?: boolean; showLeftToSpend?: boolean; enableFundAccountButton?: boolean; enablePaySomeoneAccountButton?: boolean; // events onLoad?: (response: UNComponentsOnLoadResponse<[UNAccount]>) => void; onAccountChanged?: (account: UNAccount) => void; onRequestLeftToSpendDetails?: (account: UNCreditAccount) => void; } export interface UNAccountRef { openActionsMenu: () => void; openAction: (action: UNAccountMenuAction) => void; refresh: () => void; } export enum UNAccountAction { List = 'account-list', Menu = 'account-menu' } const UNAccountComponent = React.forwardRef(function UNAccountComponent(props, accountRef) { const [height, setHeight] = useState(0); const webRef = useRef(null); // currentAccountId is used to store the current account id for the "imperative" refresh event. const accountIdRef = useRef(undefined); useEffect(() => { accountIdRef.current = props.accountId; }, [props.accountId]); const handleAccountChanged = (account: UNAccount) => { accountIdRef.current = account.id; props.onAccountChanged && props.onAccountChanged(account); }; const requestRefresh = (data: RequestRefreshEvent) => { injectRefreshEventIfNeeded(webRef.current, data); }; const handleRequestLeftToSpendDetails = (accountData: UNCreditAccount) => { props.onRequestLeftToSpendDetails && props.onRequestLeftToSpendDetails(accountData); }; const handleUnitOnLoad = (response: UnitOnLoadResponseEvent) => { if (!props.onLoad) { return; } if (RESPONSE_KEYS.errors in response) { props.onLoad(response as UNComponentsError); return; } if (RESPONSE_KEYS.account in response) { // AccountsOnLoadResponse; const accountResponse = response[RESPONSE_KEYS.account] as UNComponentsOnLoadResponseData<[UNAccount]>; props.onLoad(accountResponse); // if accountId is not provided, set the first account as current if (!props.accountId && accountResponse.data && accountResponse.data.length > 0) { accountIdRef.current = accountResponse.data[0].id; } return; } console.error('On Load Error: unexpected response type.'); return; }; useEventListener({ busEventKey: UnitComponentsMessage.UNIT_REQUEST_REFRESH, action: requestRefresh }); useEventListener({ busEventKey: AccountMessage.UNIT_ACCOUNT_CHANGED, action: handleAccountChanged }); useImperativeHandle(accountRef, () => ({ openActionsMenu() { injectOpenActionsMenuScript(webRef.current); }, openAction(action: UNAccountMenuAction) { injectRequestAccountActionScript(webRef.current, action); }, refresh() { requestRefresh({ type: UnitRequestRefreshEventTypes.REQUEST_REFRESH_EVENT, refEvent: undefined, dependencies: [WebComponentType.account.valueOf()], resourceId: accountIdRef.current, }); }, })); const handleMessage = (message: WebViewMessage) => { switch (message.type) { case UnitComponentsMessage.UNIT_ON_LOAD: handleUnitOnLoad(message.details as UnitOnLoadResponseEvent); break; case UnitComponentsMessage.UNIT_REQUEST_RENDERING: { const slotData: BottomSheetSlotData = { componentName: WebComponentType.account, componentResourceId: props.accountId, requestRenderingEvent: message.details as RequestRenderingEvent, }; const data = { type: BottomSheetRenderingType.Slot, data: slotData, } as SlotRendering; eventBus.emit(BottomSheetNativeMessage.REQUEST_RENDERING, data); break; } case PageMessage.PAGE_HEIGHT: setHeight((message.details as HeightEvent).height); break; case UnitComponentsMessage.UNIT_REQUEST_LEFT_TO_SPEND_DETAILS: handleRequestLeftToSpendDetails(message.details as UNCreditAccount); break; } }; return ( }> handleMessage(message)} isScrollable={false} /> ); }); export default withReduxStoreAndRefForwarding(UNAccountComponent);