import React, { useRef, useState } from 'react'; import { WebComponent } from '../../webComponent/WebComponent'; import type { WebViewMessage } from '../../messages/webMessages'; import type { UNComponentsOnLoadResponse, UNComponentsError, UNComponentsOnLoadResponseData, UNACHOnLoadData, UNACHPayment, UNPlaidAccountFilter, UNCounterparty, UNAccount } from '../../types/shared'; import { HeightEvent, PageMessage } from '../../messages/webMessages/pageMessage'; import { PlaidLinkTokenData, PlaidTokenCreatedEvent, RequestExternalSdkEvent, RequestRenderingEvent, UnitComponentsMessage, } from '../../messages/webMessages/unitComponentsMessages'; import { getACHDebitParams, getACHDebitScript, injectRequestCounterpartyDeletedScript, injectUnitPlaidResponse } from './UNACHDebitComponent.utils'; import { LinkExit, LinkSuccess, open as openPlaidLink, create as createPlaidLink } from 'react-native-plaid-link-sdk'; import type WebView from 'react-native-webview'; import { PaymentMessage } from '../../messages/webMessages/paymentsMessage'; import type { ACHEvent } from '../../messages/webMessages/achCreditMessage'; import { ACHOnLoadResponse, RESPONSE_KEYS, UnitOnLoadResponseEvent } from '../../messages/webMessages/onLoadMessage'; import { PresentationMode, WebComponentType } from '../../types/internal/webComponent.types'; import { withReduxStore } from '../../helpers/store/helpers'; import { PlaidMessage } from '../../messages/webMessages/plaidMessages'; import { UNBaseView } from '../../nativeComponents/UNBaseView'; import { ensureArray } from '../../utils/onLoadMessages.utils'; import { BottomSheetRenderingType, BottomSheetSlotData, SlotRendering } from '../../types/internal/bottomSheet.types'; import { BottomSheetNativeMessage } from '../../messages/nativeMessages/bottomSheetMessage'; import { eventBus } from '../../utils/eventBus'; import { useEventListener } from '../../hooks/useEventListener'; import { PayeeManagementMessage } from '../../messages/webMessages/payeeManagementMessage'; export interface UNACHDebitComponentProps { accountId?: string; theme?: string; language?: string; isAutoFocus?: boolean; sameDay?: boolean; showSameDaySelection?: boolean; onPaymentCreated?: (data: UNACHPayment) => void; onLoad?: (response: UNComponentsOnLoadResponse) => void; onInitialStageBackButtonClicked?: () => void; onFinalStageDoneButtonClicked?: () => void; plaidAccountFilters?: UNPlaidAccountFilter[]; plaidLinkCustomizationName?: string; initialStageBackButton?: boolean; finalStageDoneButton?: boolean; allowCounterpartyDelete?: boolean; } const UNACHDebitComponent = (props: UNACHDebitComponentProps) => { const [height, setHeight] = useState(0); const webRef = useRef(null); const [presentationMode, setPresentationMode] = useState(PresentationMode.Inherit); const handleCounterpartyDeleted = (data: UNCounterparty) => { injectRequestCounterpartyDeletedScript(webRef.current, data); }; useEventListener({ busEventKey: PayeeManagementMessage.UNIT_COUNTERPARTY_DELETED, action: handleCounterpartyDeleted }); 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 && RESPONSE_KEYS.counterpartyAccount in response) { const account = response[RESPONSE_KEYS.account].data; // ACHOnLoadResponse; const achOnload: UNComponentsOnLoadResponseData = { data: { [RESPONSE_KEYS.account]: ensureArray(account), [RESPONSE_KEYS.counterpartyAccount]: response[RESPONSE_KEYS.counterpartyAccount].data, }, }; props.onLoad(achOnload); return; } if (RESPONSE_KEYS.counterpartiesResult in response) { const counterparties = response[RESPONSE_KEYS.counterpartiesResult].data; const accountResponse = response as Partial; const accountData = accountResponse[RESPONSE_KEYS.account]?.data; const achOnload: UNComponentsOnLoadResponseData = { data: { [RESPONSE_KEYS.account]: (accountData ? ensureArray(accountData) : []) as [UNAccount], [RESPONSE_KEYS.counterpartyAccount]: counterparties, }, }; props.onLoad(achOnload); return; } console.error('On Load Error: unexpected response type'); return; }; const handleWebViewMessage = (message: WebViewMessage) => { if (!message || !message.details) return; switch (message.type) { case PaymentMessage.INITIAL_STAGE_BACK_BUTTON_CLICKED: { props.onInitialStageBackButtonClicked && props.onInitialStageBackButtonClicked(); break; } case PaymentMessage.FINAL_STAGE_DONE_BUTTON_CLICKED: { props.onFinalStageDoneButtonClicked && props.onFinalStageDoneButtonClicked(); break; } case PaymentMessage.PAYMENT_CREATED: { const achDebitEvent = message.details as ACHEvent; props.onPaymentCreated && props.onPaymentCreated(achDebitEvent.data as UNACHPayment); break; } case UnitComponentsMessage.UNIT_ON_LOAD: handleUnitOnLoad(message.details as UnitOnLoadResponseEvent); break; case UnitComponentsMessage.UNIT_REQUEST_RENDERING: { const slotData: BottomSheetSlotData = { componentName: WebComponentType.achDebitPayment, 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: { const currentHeight = (message.details as HeightEvent).height; setHeight(currentHeight); if (presentationMode === PresentationMode.Inherit && currentHeight === 0) { setPresentationMode(PresentationMode.Default); } break; } case PlaidMessage.UNIT_PLAID_TOKEN_CREATED: { const linkTokenEvent = message.details as PlaidTokenCreatedEvent; const linkTokenData = linkTokenEvent.linkToken as PlaidLinkTokenData; const link: string = linkTokenData.attributes.linkToken; createPlaidLink({ token: link }); break; } case UnitComponentsMessage.UNIT_REQUEST_EXTERNAL_SDK: { const externalSdkType = (message.details as RequestExternalSdkEvent).type; if (externalSdkType !== 'plaid') return; openPlaidLink({ onSuccess: (success: LinkSuccess) => { injectUnitPlaidResponse(webRef.current, success); }, onExit: (exit: LinkExit) => { console.log(exit); } }); break; } } }; const style = presentationMode === PresentationMode.Inherit ? { flex: 1 } : { height: height }; return ( }> handleWebViewMessage(message)} isScrollable={true} theme={props.theme} language={props.language} /> ); }; export default withReduxStore(UNACHDebitComponent);