diff --git a/locales/base/translation.json b/locales/base/translation.json index 377595cc056..0952ca102bb 100644 --- a/locales/base/translation.json +++ b/locales/base/translation.json @@ -1410,6 +1410,12 @@ "title": "New ways to transfer funds", "body": "We are releasing new ways to transfer funds to and from bank accounts, directly from the Valora app.\n\nIf you are seeing this, it means you are able to access this feature in your region.", "dismiss": "Dismiss" + }, + "cashIn": { + "amountSpentInfo": "You'll pay <0> including fees" + }, + "cashOut": { + "amountSpentInfo": "You'll withdraw <0> including fees" } }, "fiatConnectLinkAccountScreen": { diff --git a/src/fiatExchanges/SelectProvider.test.tsx b/src/fiatExchanges/SelectProvider.test.tsx index c162bf4ce4e..26460c8d1bb 100644 --- a/src/fiatExchanges/SelectProvider.test.tsx +++ b/src/fiatExchanges/SelectProvider.test.tsx @@ -8,7 +8,7 @@ import { SelectProviderExchangesLink, SelectProviderExchangesText } from 'src/fi import { LocalCurrencyCode } from 'src/localCurrency/consts' import { navigate } from 'src/navigator/NavigationService' import { Screens } from 'src/navigator/Screens' -import { getExperimentParams } from 'src/statsig' +import { getExperimentParams, getFeatureGate } from 'src/statsig' import { ExperimentConfigs } from 'src/statsig/constants' import { StatsigExperiments } from 'src/statsig/types' import { CiCoCurrency, Currency } from 'src/utils/currencies' @@ -112,6 +112,7 @@ describe(SelectProviderScreen, () => { addFundsExchangesText: SelectProviderExchangesText.CryptoExchange, addFundsExchangesLink: SelectProviderExchangesLink.ExternalExchangesScreen, }) + mocked(getFeatureGate).mockReturnValue(false) }) it('calls fetchProviders correctly', async () => { @@ -156,7 +157,7 @@ describe(SelectProviderScreen, () => { quotes: [mockFiatConnectQuotes[4]], }, }) - const { queryByText, getByTestId, getByText } = render( + const { queryByTestId, queryByText, getByTestId, getByText } = render( @@ -171,6 +172,55 @@ describe(SelectProviderScreen, () => { expect(queryByText('selectProviderScreen.somePaymentsUnavailable')).toBeFalsy() expect(getByText('selectProviderScreen.disclaimer')).toBeTruthy() + expect(queryByTestId('AmountSpentInfo')).toBeFalsy() + }) + it('shows you will pay fiat amount for cash ins if feature flag is true', async () => { + mocked(fetchProviders).mockResolvedValue(mockProviders) + mocked(fetchLegacyMobileMoneyProviders).mockResolvedValue(mockLegacyProviders) + mocked(fetchExchanges).mockResolvedValue(mockExchanges) + mocked(getFeatureGate).mockReturnValue(true) + mockStore = createMockStore({ + ...MOCK_STORE_DATA, + fiatConnect: { + quotesError: null, + quotesLoading: false, + quotes: [mockFiatConnectQuotes[4]], + }, + }) + const { getByTestId, getByText } = render( + + + + ) + await waitFor(() => expect(fetchLegacyMobileMoneyProviders).toHaveBeenCalled()) + + expect(getByTestId('AmountSpentInfo')).toBeTruthy() + expect(getByText(/selectProviderScreen.cashIn.amountSpentInfo/)).toBeTruthy() + expect(getByTestId('AmountSpentInfo/Fiat/value')).toBeTruthy() + }) + it('shows you will withdraw crypto amount for cash outs if feature flag is true', async () => { + mocked(fetchProviders).mockResolvedValue(mockProviders) + mocked(fetchLegacyMobileMoneyProviders).mockResolvedValue(mockLegacyProviders) + mocked(fetchExchanges).mockResolvedValue(mockExchanges) + mocked(getFeatureGate).mockReturnValue(true) + mockStore = createMockStore({ + ...MOCK_STORE_DATA, + fiatConnect: { + quotesError: null, + quotesLoading: false, + quotes: [mockFiatConnectQuotes[4]], + }, + }) + const { getByTestId, getByText } = render( + + + + ) + await waitFor(() => expect(fetchLegacyMobileMoneyProviders).toHaveBeenCalled()) + + expect(getByTestId('AmountSpentInfo')).toBeTruthy() + expect(getByText(/selectProviderScreen.cashOut.amountSpentInfo/)).toBeTruthy() + expect(getByTestId('AmountSpentInfo/Crypto')).toBeTruthy() }) it('shows the limit payment methods dialog when one of the provider types has no options', async () => { mocked(fetchProviders).mockResolvedValue([mockProviders[2]]) diff --git a/src/fiatExchanges/SelectProvider.tsx b/src/fiatExchanges/SelectProvider.tsx index fa79e70f366..98e2626aa0f 100644 --- a/src/fiatExchanges/SelectProvider.tsx +++ b/src/fiatExchanges/SelectProvider.tsx @@ -17,6 +17,11 @@ import TextButton from 'src/components/TextButton' import Touchable from 'src/components/Touchable' import { CoinbasePaymentSection } from 'src/fiatExchanges/CoinbasePaymentSection' import { ExternalExchangeProvider } from 'src/fiatExchanges/ExternalExchanges' +import { + PaymentMethodSection, + PaymentMethodSectionMethods, +} from 'src/fiatExchanges/PaymentMethodSection' +import { CryptoAmount, FiatAmount } from 'src/fiatExchanges/amount' import { normalizeQuotes } from 'src/fiatExchanges/quotes/normalizeQuotes' import { ProviderSelectionAnalyticsData, @@ -31,10 +36,6 @@ import { selectFiatConnectQuoteLoadingSelector, } from 'src/fiatconnect/selectors' import { fetchFiatConnectProviders, fetchFiatConnectQuotes } from 'src/fiatconnect/slice' -import { - PaymentMethodSection, - PaymentMethodSectionMethods, -} from 'src/fiatExchanges/PaymentMethodSection' import { readOnceFromFirebase } from 'src/firebase/firebase' import i18n from 'src/i18n' import { @@ -46,9 +47,9 @@ import { navigate } from 'src/navigator/NavigationService' import { Screens } from 'src/navigator/Screens' import { StackParamList } from 'src/navigator/types' import { userLocationDataSelector } from 'src/networkInfo/selectors' -import { getExperimentParams } from 'src/statsig' +import { getExperimentParams, getFeatureGate } from 'src/statsig' import { ExperimentConfigs } from 'src/statsig/constants' -import { StatsigExperiments } from 'src/statsig/types' +import { StatsigExperiments, StatsigFeatureGates } from 'src/statsig/types' import colors from 'src/styles/colors' import fontStyles from 'src/styles/fonts' import { Spacing } from 'src/styles/styles' @@ -271,8 +272,13 @@ export default function SelectProviderScreen({ route, navigation }: Props) { cryptoType: digitalAsset, }) + const showReceiveAmount = getFeatureGate( + StatsigFeatureGates.SHOW_RECEIVE_AMOUNT_IN_SELECT_PROVIDER + ) + return ( + {showReceiveAmount && } {paymentMethodSections.map((paymentMethod) => ( + + + {flow === CICOFlow.CashIn ? ( + + ) : ( + + )} + + + + ) +} + function LimitedPaymentMethods({ flow }: { flow: CICOFlow }) { const { t } = useTranslation() const [isDialogVisible, setIsDialogVisible] = useState(false) @@ -571,6 +608,17 @@ const styles = StyleSheet.create({ color: colors.gray4, padding: Spacing.Smallest8, }, + amountSpentInfo: { + marginHorizontal: 16, + marginBottom: 8, + padding: 16, + backgroundColor: colors.gray1, + borderRadius: 16, + }, + amountSpentInfoText: { + textAlign: 'center', + ...fontStyles.xsmall600, + }, }) SelectProviderScreen.navigationOptions = ({ route, diff --git a/src/fiatExchanges/amount.test.tsx b/src/fiatExchanges/amount.test.tsx new file mode 100644 index 00000000000..6185233d775 --- /dev/null +++ b/src/fiatExchanges/amount.test.tsx @@ -0,0 +1,49 @@ +import { render } from '@testing-library/react-native' +import React from 'react' +import { Provider } from 'react-redux' +import CurrencyDisplay from 'src/components/CurrencyDisplay' +import TokenDisplay from 'src/components/TokenDisplay' +import { CryptoAmount, FiatAmount } from 'src/fiatExchanges/amount' +import { CiCoCurrency } from 'src/utils/currencies' +import { createMockStore } from 'test/utils' +import { mockCusdAddress } from 'test/values' + +jest.mock('src/components/TokenDisplay') +jest.mock('src/components/CurrencyDisplay') + +describe('CryptoAmount', () => { + it('passes amount and token address to TokenDisplay', () => { + render( + + + + ) + expect(TokenDisplay).toHaveBeenCalledWith( + { + amount: 10, + showLocalAmount: false, + testID: 'cryptoAmt', + tokenAddress: mockCusdAddress, + }, + {} + ) + }) +}) + +describe('FiatAmount', () => { + it('passes amount and currency to CurrencyDisplay', () => { + render() + + expect(CurrencyDisplay).toHaveBeenCalledWith( + expect.objectContaining({ + amount: { + value: 0, + currencyCode: '', + localAmount: { value: 20, currencyCode: 'USD', exchangeRate: 1 }, + }, + testID: 'fiatAmt', + }), + {} + ) + }) +}) diff --git a/src/fiatExchanges/amount.tsx b/src/fiatExchanges/amount.tsx new file mode 100644 index 00000000000..8f9acdba9ce --- /dev/null +++ b/src/fiatExchanges/amount.tsx @@ -0,0 +1,50 @@ +import BigNumber from 'bignumber.js' +import React from 'react' +import CurrencyDisplay, { FormatType } from 'src/components/CurrencyDisplay' +import TokenDisplay from 'src/components/TokenDisplay' +import { useTokenInfoBySymbol } from 'src/tokens/hooks' +import { CiCoCurrency } from 'src/utils/currencies' + +export function CryptoAmount({ + amount, + currency, + testID, +}: { + amount: BigNumber.Value + currency: CiCoCurrency + testID?: string +}) { + const { address } = useTokenInfoBySymbol(currency)! + return ( + + ) +} + +export function FiatAmount({ + amount, + currency, + testID, + formatType, +}: { + amount: BigNumber.Value + currency: string + testID?: string + formatType?: FormatType +}) { + return ( + + ) +} diff --git a/src/fiatconnect/ReviewScreen.tsx b/src/fiatconnect/ReviewScreen.tsx index 9636e0f07e6..a0000d55f49 100644 --- a/src/fiatconnect/ReviewScreen.tsx +++ b/src/fiatconnect/ReviewScreen.tsx @@ -11,10 +11,9 @@ import ValoraAnalytics from 'src/analytics/ValoraAnalytics' import BackButton from 'src/components/BackButton' import Button, { BtnSizes, BtnTypes } from 'src/components/Button' import CancelButton from 'src/components/CancelButton' -import CurrencyDisplay, { FormatType } from 'src/components/CurrencyDisplay' +import { FormatType } from 'src/components/CurrencyDisplay' import Dialog from 'src/components/Dialog' import LineItemRow from 'src/components/LineItemRow' -import TokenDisplay from 'src/components/TokenDisplay' import Touchable from 'src/components/Touchable' import { estimateFee, FeeEstimateState, FeeType } from 'src/fees/reducer' import { feeEstimatesSelector } from 'src/fees/selectors' @@ -24,6 +23,7 @@ import { fiatConnectQuotesLoadingSelector, } from 'src/fiatconnect/selectors' import { createFiatConnectTransfer, refetchQuote } from 'src/fiatconnect/slice' +import { CryptoAmount, FiatAmount } from 'src/fiatExchanges/amount' import { navigateToFiatExchangeStart } from 'src/fiatExchanges/navigator' import FiatConnectQuote from 'src/fiatExchanges/quotes/FiatConnectQuote' import { CICOFlow } from 'src/fiatExchanges/utils' @@ -320,50 +320,6 @@ function ReceiveAmount({ ) } -function CryptoAmount({ - amount, - currency, - testID, -}: { - amount: BigNumber.Value - currency: CiCoCurrency - testID: string -}) { - const { address } = useTokenInfoBySymbol(currency)! - return ( - - ) -} - -function FiatAmount({ - amount, - currency, - testID, - formatType, -}: { - amount: BigNumber.Value - currency: string - testID: string - formatType?: FormatType -}) { - return ( - - ) -} - function getDisplayAmounts({ flow, normalizedQuote, diff --git a/src/statsig/constants.ts b/src/statsig/constants.ts index 9810f79f3ad..8a36c3c3c6b 100644 --- a/src/statsig/constants.ts +++ b/src/statsig/constants.ts @@ -28,6 +28,7 @@ export const FeatureGates = { [StatsigFeatureGates.SHOW_CLAIM_SHORTCUTS]: false, [StatsigFeatureGates.APP_REVIEW]: false, [StatsigFeatureGates.SHOW_IN_APP_NFT_VIEWER]: false, + [StatsigFeatureGates.SHOW_RECEIVE_AMOUNT_IN_SELECT_PROVIDER]: false, } export const ExperimentConfigs = { diff --git a/src/statsig/types.ts b/src/statsig/types.ts index c05cbca7d9f..0521bb04149 100644 --- a/src/statsig/types.ts +++ b/src/statsig/types.ts @@ -22,6 +22,7 @@ export enum StatsigFeatureGates { SHOW_CLAIM_SHORTCUTS = 'show_claim_shortcuts', APP_REVIEW = 'app_review', SHOW_IN_APP_NFT_VIEWER = 'show_in_app_nft_viewer', + SHOW_RECEIVE_AMOUNT_IN_SELECT_PROVIDER = 'show_receive_amount_in_select_provider', } export enum StatsigExperiments {