import React, { useEffect, useState } from 'react' import { Trans, useTranslation } from 'react-i18next' import { Image, LayoutAnimation, StyleSheet, Text, View } from 'react-native' import AppAnalytics from 'src/analytics/AppAnalytics' import { FiatExchangeEvents } from 'src/analytics/Events' import Dialog from 'src/components/Dialog' import Expandable from 'src/components/Expandable' import Touchable from 'src/components/Touchable' import { CryptoAmount, FiatAmount } from 'src/fiatExchanges/amount' import NormalizedQuote from 'src/fiatExchanges/quotes/NormalizedQuote' import { SettlementEstimation, SettlementTime } from 'src/fiatExchanges/quotes/constants' import { getSettlementTimeString } from 'src/fiatExchanges/quotes/utils' import { CICOFlow, PaymentMethod, ProviderSelectionAnalyticsData } from 'src/fiatExchanges/types' import InfoIcon from 'src/icons/InfoIcon' import { getLocalCurrencyCode, usdToLocalCurrencyRateSelector } from 'src/localCurrency/selectors' import { useDispatch, useSelector } from 'src/redux/hooks' import { getFeatureGate } from 'src/statsig' import { StatsigFeatureGates } from 'src/statsig/types' import colors from 'src/styles/colors' import { typeScale } from 'src/styles/fonts' import { useTokenInfo } from 'src/tokens/hooks' const SETTLEMENT_TIME_STRINGS: Record = { [SettlementTime.LESS_THAN_ONE_HOUR]: 'selectProviderScreen.oneHour', [SettlementTime.LESS_THAN_X_HOURS]: 'selectProviderScreen.xHours', [SettlementTime.X_TO_Y_HOURS]: 'selectProviderScreen.xToYHours', [SettlementTime.LESS_THAN_X_DAYS]: 'selectProviderScreen.xDays', [SettlementTime.X_TO_Y_DAYS]: 'selectProviderScreen.xToYDays', } export type PaymentMethodSectionMethods = | PaymentMethod.Bank | PaymentMethod.Card | PaymentMethod.FiatConnectMobileMoney | PaymentMethod.Airtime export interface PaymentMethodSectionProps { paymentMethod: PaymentMethodSectionMethods normalizedQuotes: NormalizedQuote[] flow: CICOFlow tokenId: string analyticsData: ProviderSelectionAnalyticsData } export function PaymentMethodSection({ paymentMethod, normalizedQuotes, flow, tokenId, analyticsData, }: PaymentMethodSectionProps) { const { t } = useTranslation() const dispatch = useDispatch() const sectionQuotes = normalizedQuotes.filter( (quote) => quote.getPaymentMethod() === paymentMethod ) const usdToLocalRate = useSelector(usdToLocalCurrencyRateSelector) const tokenInfo = useTokenInfo(tokenId) const localCurrency = useSelector(getLocalCurrencyCode) const isExpandable = sectionQuotes.length > 1 const [expanded, setExpanded] = useState(isExpandable) const [newDialogVisible, setNewDialogVisible] = useState(false) const showUKCompliantVariant = getFeatureGate(StatsigFeatureGates.SHOW_UK_COMPLIANT_VARIANT) useEffect(() => { if (sectionQuotes.length) { AppAnalytics.track(FiatExchangeEvents.cico_providers_section_impression, { flow, paymentMethod, quoteCount: sectionQuotes.length, providers: sectionQuotes.map((quote) => quote.getProviderId()), }) } }, []) const toggleExpanded = () => { if (expanded) { AppAnalytics.track(FiatExchangeEvents.cico_providers_section_collapse, { flow, paymentMethod, }) } else { AppAnalytics.track(FiatExchangeEvents.cico_providers_section_expand, { flow, paymentMethod, }) } LayoutAnimation.easeInEaseOut() setExpanded(!expanded) } if (!sectionQuotes.length) { return null } const getCategoryTitle = () => { switch (paymentMethod) { case PaymentMethod.Card: return t('selectProviderScreen.card') case PaymentMethod.FiatConnectMobileMoney: return t('selectProviderScreen.mobileMoney') case PaymentMethod.Bank: return t('selectProviderScreen.bank') case PaymentMethod.Airtime: return t('selectProviderScreen.airtime') default: // this should never happen throw new Error('invalid payment method') } } const renderExpandableSection = () => ( <> {getCategoryTitle()} {t('selectProviderScreen.numProviders', { count: sectionQuotes.length })} ) const renderProviderInfo = (quote: NormalizedQuote) => ( {quote.isProviderNew() && ( { AppAnalytics.track(FiatExchangeEvents.cico_providers_new_info_opened, { flow, provider: quote.getProviderId(), paymentMethod, }) setNewDialogVisible(true) }} > <> {t('selectProviderScreen.newLabel')} )} ) // this is used only when there's a single quote, so it directly references sectionQuotes[0] const renderNonExpandableSection = () => ( <> {getCategoryTitle()} {renderAmount(sectionQuotes[0])} {renderInfoText(sectionQuotes[0])} {renderProviderInfo(sectionQuotes[0])} ) const getPaymentMethodSettlementTimeString = (settlementEstimation: SettlementEstimation) => { const { timeString, ...args } = getSettlementTimeString( settlementEstimation, SETTLEMENT_TIME_STRINGS ) return timeString ? t(timeString, args) : t('selectProviderScreen.numDays') } const renderInfoText = (quote: NormalizedQuote) => { const mobileCarrier = quote.getMobileCarrier() const mobileCarrierRequirement = t('selectProviderScreen.mobileCarrierRequirement', { carrier: mobileCarrier, }) const reqsSubtitleInfo = mobileCarrier ? mobileCarrierRequirement : quote.getKycInfo() const reqsSubtitleString = reqsSubtitleInfo ? `${reqsSubtitleInfo} | ` : '' return `${reqsSubtitleString}${getPaymentMethodSettlementTimeString(quote.getTimeEstimation())}` } const renderAmount = (normalizedQuote: NormalizedQuote) => { const defaultText = {t('selectProviderScreen.feesVary')} const receiveAmount = normalizedQuote.getReceiveAmount() if (receiveAmount) { return ( {flow === CICOFlow.CashIn ? ( ) : ( )} ) } else { return defaultText } } return ( {isExpandable ? renderExpandableSection() : renderNonExpandableSection()} {expanded && ( {sectionQuotes.map((normalizedQuote, index) => ( {renderAmount(normalizedQuote)} {renderInfoText(normalizedQuote)} {index === 0 && !!tokenInfo && normalizedQuote.getFeeInCrypto(usdToLocalRate, tokenInfo) && !showUKCompliantVariant && ( {t('selectProviderScreen.bestRate')} )} {renderProviderInfo(normalizedQuote)} ))} )} { setNewDialogVisible(false) }} isActionHighlighted={false} > {t('selectProviderScreen.newDialog.body')} ) } const styles = StyleSheet.create({ container: { borderBottomWidth: 1, borderBottomColor: colors.borderPrimary, }, expandableContainer: { paddingHorizontal: 16, justifyContent: 'space-between', flexDirection: 'row', alignItems: 'center', }, left: { flex: 1, }, right: { flexDirection: 'column', justifyContent: 'center', }, expandedContainer: { borderTopWidth: 1, borderTopColor: colors.borderSecondary, paddingVertical: 16, paddingHorizontal: 16, backgroundColor: colors.backgroundSecondary, justifyContent: 'space-between', alignItems: 'center', flexDirection: 'row', }, providerImage: { flex: 1, }, providerInfoContainer: { flexDirection: 'column', }, imageContainer: { height: 40, width: 40, marginLeft: 'auto', }, newLabelContainer: { backgroundColor: colors.textLink, borderRadius: 100, paddingVertical: 4, paddingHorizontal: 8, marginTop: 4, marginLeft: 'auto', flexDirection: 'row', width: 'auto', }, newLabelText: { ...typeScale.labelSemiBoldSmall, color: colors.contentTertiary, marginRight: 5, }, category: { ...typeScale.bodySmall, }, fee: { ...typeScale.labelSemiBoldSmall, marginTop: 4, }, providerDropdown: { ...typeScale.labelSmall, color: colors.contentSecondary, }, expandedInfo: { ...typeScale.bodySmall, color: colors.contentSecondary, marginTop: 2, }, topInfo: { ...typeScale.bodySmall, color: colors.contentSecondary, marginTop: 4, }, expandedFee: { ...typeScale.labelSemiBoldSmall, }, expandedTag: { ...typeScale.labelSemiBoldSmall, color: colors.accent, fontSize: 12, marginTop: 2, }, })