From 9523520df766d08528d0a744e4b69e5b214993c1 Mon Sep 17 00:00:00 2001 From: ost-ptk Date: Tue, 15 Oct 2024 17:44:04 +0300 Subject: [PATCH 1/2] migrate on Ramp fetching to the casper wallet core --- src/apps/popup/pages/buy-cspr/amount.tsx | 30 +- .../pages/buy-cspr/components/country-row.tsx | 75 ++--- .../buy-cspr/components/currency-row.tsx | 6 +- .../pages/buy-cspr/components/list-row.tsx | 18 +- src/apps/popup/pages/buy-cspr/country.tsx | 38 ++- src/apps/popup/pages/buy-cspr/index.tsx | 285 ++++++++---------- src/apps/popup/pages/buy-cspr/provider.tsx | 8 +- src/apps/popup/pages/buy-cspr/utils.ts | 12 +- src/background/index.ts | 55 +--- src/background/redux/utils.ts | 4 +- src/background/service-message.ts | 34 --- src/background/wallet-repositories.ts | 6 +- .../services/buy-cspr-service/constants.ts | 2 - src/libs/services/buy-cspr-service/index.ts | 81 +---- src/libs/services/buy-cspr-service/types.ts | 73 ----- ...-fetch-on-ramp-countries-and-currencies.ts | 65 ++++ .../use-get-on-ramp-providers.ts | 24 ++ src/libs/services/types.ts | 11 - src/libs/ui/components/spinner/spinner.tsx | 8 +- 19 files changed, 323 insertions(+), 512 deletions(-) delete mode 100644 src/background/service-message.ts delete mode 100644 src/libs/services/buy-cspr-service/constants.ts delete mode 100644 src/libs/services/buy-cspr-service/types.ts create mode 100644 src/libs/services/buy-cspr-service/use-fetch-on-ramp-countries-and-currencies.ts create mode 100644 src/libs/services/buy-cspr-service/use-get-on-ramp-providers.ts delete mode 100644 src/libs/services/types.ts diff --git a/src/apps/popup/pages/buy-cspr/amount.tsx b/src/apps/popup/pages/buy-cspr/amount.tsx index 2b5e65987..c797d94ad 100644 --- a/src/apps/popup/pages/buy-cspr/amount.tsx +++ b/src/apps/popup/pages/buy-cspr/amount.tsx @@ -1,4 +1,5 @@ import { formatNumber } from 'casper-wallet-core'; +import { IOnRampCurrencyItem } from 'casper-wallet-core/src/domain'; import React, { useEffect, useState } from 'react'; import { useForm, useWatch } from 'react-hook-form'; import { Trans, useTranslation } from 'react-i18next'; @@ -15,7 +16,6 @@ import { SpacingSize, VerticalSpaceContainer } from '@libs/layout'; -import { ResponseCurrencyProps } from '@libs/services/buy-cspr-service/types'; import { Input, List, @@ -31,23 +31,23 @@ import { CurrencyRow } from './components/currency-row'; import { ListRow } from './components/list-row'; interface AmountProps { - currencies: ResponseCurrencyProps[]; - selectedCurrency: ResponseCurrencyProps; + availableCurrencies: IOnRampCurrencyItem[]; + selectedCurrency: IOnRampCurrencyItem; setPaymentAmount: React.Dispatch>; setSelectedCurrency: React.Dispatch< - React.SetStateAction + React.SetStateAction >; defaultAmount: string; } export const Amount = ({ - currencies, + availableCurrencies, selectedCurrency, setPaymentAmount, setSelectedCurrency, defaultAmount }: AmountProps) => { - const [sortedCurrency, setSortedCurrency] = useState( + const [sortedCurrency, setSortedCurrency] = useState( [] ); const { t } = useTranslation(); @@ -91,16 +91,17 @@ export const Amount = ({ useEffect(() => { const sortedCurrencies = sortCurrencies( - currencies, - selectedCurrency.code - ).filter(currency => - currency.code - .toLowerCase() - .includes(searchInputValue?.toLowerCase() || '') + availableCurrencies, + selectedCurrency?.code + ).filter( + currency => + currency?.code + .toLowerCase() + .includes(searchInputValue?.toLowerCase() || '') ); setSortedCurrency(sortedCurrencies); - }, [currencies, searchInputValue, selectedCurrency.code]); + }, [availableCurrencies, searchInputValue, selectedCurrency?.code]); return ( @@ -143,7 +144,8 @@ export const Amount = ({ rows={sortedCurrency} height={280} renderRow={currency => { - const isSelected = selectedCurrency.code === currency.code; + const isSelected = + selectedCurrency?.code === currency?.code; return ( void; + isLoadingOnRampCountriesAndCurrencies: boolean; } -export const CountryRow = ({ country, onClick }: CountryRowProps) => { - const [iconSrc, setIconSrc] = useState(''); - +export const CountryRow = ({ + country, + onClick, + isLoadingOnRampCountriesAndCurrencies +}: CountryRowProps) => { const { t } = useTranslation(); - useEffect(() => { - if (country.code) { - setIconSrc( - `https://purecatamphetamine.github.io/country-flag-icons/3x2/${country.code}.svg` - ); - } - }, [country.code]); - - const handleError = () => { - setIconSrc('/assets/icons/placeholder-image-gray.svg'); - }; - return ( - + Country @@ -53,24 +47,31 @@ export const CountryRow = ({ country, onClick }: CountryRowProps) => { - {`${country.name}`} - - {country.name} - + {isLoadingOnRampCountriesAndCurrencies ? ( + + ) : ( + <> + {`${country?.name}`} + + {country?.name} + + + )} - - Change - + {isLoadingOnRampCountriesAndCurrencies ? null : ( + + Change + + )} diff --git a/src/apps/popup/pages/buy-cspr/components/currency-row.tsx b/src/apps/popup/pages/buy-cspr/components/currency-row.tsx index 89e799c28..b50cb5602 100644 --- a/src/apps/popup/pages/buy-cspr/components/currency-row.tsx +++ b/src/apps/popup/pages/buy-cspr/components/currency-row.tsx @@ -1,9 +1,9 @@ +import { IOnRampCurrencyItem } from 'casper-wallet-core/src/domain'; import React from 'react'; import { Trans, useTranslation } from 'react-i18next'; import styled from 'styled-components'; import { CenteredFlexColumn, SpacingSize } from '@libs/layout'; -import { ResponseCurrencyProps } from '@libs/services/buy-cspr-service/types'; import { Tile, Typography } from '@libs/ui/components'; const Container = styled.div` @@ -11,7 +11,7 @@ const Container = styled.div` `; interface CurrencyRowProps { - selectedCurrency: ResponseCurrencyProps; + selectedCurrency: IOnRampCurrencyItem; onClick: () => void; } @@ -33,7 +33,7 @@ export const CurrencyRow = ({ color="contentAction" dataTestId="currency-row" > - {selectedCurrency.code} + {selectedCurrency?.code} diff --git a/src/apps/popup/pages/buy-cspr/components/list-row.tsx b/src/apps/popup/pages/buy-cspr/components/list-row.tsx index c97e13aa8..034847112 100644 --- a/src/apps/popup/pages/buy-cspr/components/list-row.tsx +++ b/src/apps/popup/pages/buy-cspr/components/list-row.tsx @@ -1,3 +1,7 @@ +import { + IOnRampCountry, + IOnRampCurrencyItem +} from 'casper-wallet-core/src/domain'; import React, { useEffect, useState } from 'react'; import styled from 'styled-components'; @@ -6,10 +10,6 @@ import { AlignedSpaceBetweenFlexRow, SpacingSize } from '@libs/layout'; -import { - ResponseCountryPropsWithId, - ResponseCurrencyProps -} from '@libs/services/buy-cspr-service/types'; import { Checkbox, Typography } from '@libs/ui/components'; const Container = styled(AlignedSpaceBetweenFlexRow)` @@ -19,10 +19,10 @@ const Container = styled(AlignedSpaceBetweenFlexRow)` `; interface ListRowProps { - country?: ResponseCountryPropsWithId; + country?: IOnRampCountry; handleSelect: (e: React.MouseEvent) => void; isSelected: boolean; - currency?: ResponseCurrencyProps; + currency?: IOnRampCurrencyItem; } export const ListRow = ({ @@ -53,18 +53,18 @@ export const ListRow = ({ {country && ( <> {`${country.name}`} - {country.name} + {country?.name} )} {currency && ( - {currency.code} + {currency?.code} )} diff --git a/src/apps/popup/pages/buy-cspr/country.tsx b/src/apps/popup/pages/buy-cspr/country.tsx index 87cb8c7d1..54293eabf 100644 --- a/src/apps/popup/pages/buy-cspr/country.tsx +++ b/src/apps/popup/pages/buy-cspr/country.tsx @@ -1,3 +1,4 @@ +import { IOnRampCountry } from 'casper-wallet-core/src/domain'; import React, { useEffect, useState } from 'react'; import { useForm, useWatch } from 'react-hook-form'; import { Trans, useTranslation } from 'react-i18next'; @@ -9,7 +10,6 @@ import { SpacingSize } from '@libs/layout'; import { useFetchWalletBalance } from '@libs/services/balance-service'; -import { ResponseCountryPropsWithId } from '@libs/services/buy-cspr-service/types'; import { ActiveAccountPlate, Input, @@ -25,20 +25,22 @@ import { ListRow } from './components/list-row'; import { sortCountries } from './utils'; interface CountryProps { - availableCountries: ResponseCountryPropsWithId[]; - setSelectedCountry: React.Dispatch< - React.SetStateAction - >; - selectedCountry: ResponseCountryPropsWithId; + availableCountries: IOnRampCountry[]; + setSelectedCountry: React.Dispatch>; + selectedCountry: IOnRampCountry; + isLoadingOnRampCountriesAndCurrencies: boolean; } +type ExtendedOnRampCountry = IOnRampCountry & { id: number }; + export const Country = ({ availableCountries, setSelectedCountry, - selectedCountry + selectedCountry, + isLoadingOnRampCountriesAndCurrencies }: CountryProps) => { const [sortedCountries, setSortedCountries] = useState< - ResponseCountryPropsWithId[] + ExtendedOnRampCountry[] >([]); const { t } = useTranslation(); @@ -54,11 +56,16 @@ export const Country = ({ useEffect(() => { const sortedCountries = sortCountries( availableCountries, - selectedCountry.code - ).filter( - country => - country?.name.toLowerCase().includes(inputValue?.toLowerCase() || '') - ); + selectedCountry?.code + ) + .filter( + country => + country?.name.toLowerCase().includes(inputValue?.toLowerCase() || '') + ) + .map((country, index) => ({ + ...country, + id: index + })); setSortedCountries(sortedCountries); }, [availableCountries, inputValue, selectedCountry]); @@ -92,7 +99,7 @@ export const Country = ({ rows={sortedCountries} height={280} renderRow={country => { - const isSelected = selectedCountry.code === country.code; + const isSelected = selectedCountry?.code === country?.code; return ( setValue('countryNameSearch', '')} + isLoadingOnRampCountriesAndCurrencies={ + isLoadingOnRampCountriesAndCurrencies + } /> )} loading={!sortedCountries.length} diff --git a/src/apps/popup/pages/buy-cspr/index.tsx b/src/apps/popup/pages/buy-cspr/index.tsx index 2391ee9cf..f87eec164 100644 --- a/src/apps/popup/pages/buy-cspr/index.tsx +++ b/src/apps/popup/pages/buy-cspr/index.tsx @@ -1,3 +1,10 @@ +import { + IGetOnRampProvidersData, + IOnRampCountry, + IOnRampCurrencyItem, + IOnRampProvider, + IProviderSelectionData +} from 'casper-wallet-core/src/domain'; import React, { useEffect, useState } from 'react'; import { Trans, useTranslation } from 'react-i18next'; import { useSelector } from 'react-redux'; @@ -14,17 +21,8 @@ import { PopupLayout, createErrorLocationState } from '@libs/layout'; -import { - dispatchFetchOnRampOptionGet, - dispatchFetchOnRampOptionPost, - dispatchFetchOnRampSelectionPost -} from '@libs/services/buy-cspr-service'; -import { - ProviderProps, - ResponseCountryPropsWithId, - ResponseCurrencyProps, - SelectionPostRequestData -} from '@libs/services/buy-cspr-service/types'; +import { useFetchOnRampCountriesAndCurrencies } from '@libs/services/buy-cspr-service/use-fetch-on-ramp-countries-and-currencies'; +import { useGetOnRampProviders } from '@libs/services/buy-cspr-service/use-get-on-ramp-providers'; import { Button, Typography } from '@libs/ui/components'; import { Amount } from './amount'; @@ -36,160 +34,131 @@ import { BuyCSPRSteps } from './utils'; export const BuyCSPRPage = () => { const [buyCSPRStep, setBuyCSPRStep] = useState(BuyCSPRSteps.Country); const [availableCountries, setAvailableCountries] = useState< - ResponseCountryPropsWithId[] + IOnRampCountry[] >([]); - const [selectedCountry, setSelectedCountry] = - useState({ - id: 0, - name: '', - code: '' - }); - const [currencies, setCurrencies] = useState([]); - const [selectedCurrency, setSelectedCurrency] = - useState({ + const [selectedCountry, setSelectedCountry] = useState({ + name: '', + code: '', + flagUri: '' + }); + const [availableCurrencies, setAvailableCurrencies] = useState< + IOnRampCurrencyItem[] + >([]); + const [selectedCurrency, setSelectedCurrency] = useState( + { id: 0, code: '', type_id: '', rate: 0 - }); + } + ); const [defaultAmount, setDefaultAmount] = useState('0'); const [fiatAmount, setFiatAmount] = useState(0); - const [availableProviders, setAvailableProviders] = useState( - [] - ); + const [availableProviders, setAvailableProviders] = useState< + IOnRampProvider[] + >([]); const [selectedProvider, setSelectedProvider] = - useState(null); - const [loadingAvailableProviders, setLoadingAvailableProviders] = - useState(false); - const [loadingRedirectUrl, setLoadingRedirectUrl] = useState(false); + useState(null); const { t } = useTranslation(); const navigate = useTypedNavigate(); const activeAccount = useSelector(selectVaultActiveAccount); - useEffect(() => { - dispatchFetchOnRampOptionGet() - .then(({ payload }) => { - if ('countries' in payload) { - const countriesWithId = payload.countries.map((country, id) => ({ - id, - ...country - })); - - const defaultSelectedCountry = countriesWithId.find( - country => country.code === payload.defaultCountry - ); + const { + currencies, + countries, + defaultCountry, + defaultCurrency, + defaultDepositAmount, + isLoadingOnRampCountriesAndCurrencies, + onRampCountriesAndCurrenciesError + } = useFetchOnRampCountriesAndCurrencies(); + const { + getOnRampProviders, + isProvidersLoading, + isProviderLocationLoading, + getOnRampProviderLocation + } = useGetOnRampProviders(); - const defaultSelectedCurrency = payload.currencies.find( - currency => currency.code === payload.defaultCurrency - ); + useEffect(() => { + if (onRampCountriesAndCurrenciesError) { + navigate( + ErrorPath, + createErrorLocationState({ + errorHeaderText: t('Something went wrong'), + errorContentText: + onRampCountriesAndCurrenciesError.message || + t( + 'Please check browser console for error details, this will be a valuable for our team to fix the issue.' + ), + errorPrimaryButtonLabel: t('Close'), + errorRedirectPath: RouterPath.Home + }) + ); + } + if (isLoadingOnRampCountriesAndCurrencies) return; - setAvailableCountries(countriesWithId); - setCurrencies(payload.currencies); - setSelectedCountry(defaultSelectedCountry!); - setSelectedCurrency(defaultSelectedCurrency!); - setDefaultAmount(payload.defaultAmount); - setFiatAmount(Number(payload.defaultAmount)); - } else { - navigate( - ErrorPath, - createErrorLocationState({ - errorHeaderText: t('Something went wrong'), - errorContentText: - payload.error.message || - t( - 'Please check browser console for error details, this will be a valuable for our team to fix the issue.' - ), - errorPrimaryButtonLabel: t('Close'), - errorRedirectPath: RouterPath.Home - }) - ); - } - }) - .catch(error => { - console.error(error.message, 'countries request failed'); - - navigate( - ErrorPath, - createErrorLocationState({ - errorHeaderText: error.message || t('Something went wrong'), - errorContentText: - typeof error.data === 'string' - ? error.data - : t( - 'Please check browser console for error details, this will be a valuable for our team to fix the issue.' - ), - errorPrimaryButtonLabel: t('Close'), - errorRedirectPath: RouterPath.Home - }) - ); - }); - }, [navigate, t]); + setAvailableCountries(countries); + setAvailableCurrencies(currencies); + setSelectedCountry(defaultCountry!); + setSelectedCurrency(defaultCurrency!); + setDefaultAmount(defaultDepositAmount!); + setFiatAmount(Number(defaultDepositAmount)); + }, [ + countries, + currencies, + defaultCountry, + defaultCurrency, + defaultDepositAmount, + isLoadingOnRampCountriesAndCurrencies, + navigate, + onRampCountriesAndCurrenciesError, + t + ]); const handleAmountSubmit = () => { - setLoadingAvailableProviders(true); - - dispatchFetchOnRampOptionPost({ + const data: IGetOnRampProvidersData = { amount: fiatAmount, - country: selectedCountry.code, - fiatCurrency: selectedCurrency.code, - paymentCurrency: selectedCurrency.code - }) - .then(({ payload }) => { - if ('availableProviders' in payload) { - if (payload.availableProviders.length === 0) { - setBuyCSPRStep(BuyCSPRSteps.NoProvider); - } else { - setAvailableProviders(payload.availableProviders); - - if (payload.availableProviders.length === 1) { - setSelectedProvider(payload.availableProviders[0]); - } + country: selectedCountry?.code, + fiatCurrency: selectedCurrency?.code, + paymentCurrency: selectedCurrency?.code + }; + getOnRampProviders(data, { + onSuccess: onRampProviders => { + if (onRampProviders.availableProviders.length === 0) { + setBuyCSPRStep(BuyCSPRSteps.NoProvider); + } else { + setAvailableProviders(onRampProviders.availableProviders); - setBuyCSPRStep(BuyCSPRSteps.Provider); + if (onRampProviders.availableProviders.length === 1) { + setSelectedProvider(onRampProviders.availableProviders[0]); } - } else { - navigate( - ErrorPath, - createErrorLocationState({ - errorHeaderText: t('Something went wrong'), - errorContentText: - payload.error.message || - t( - 'Please check browser console for error details, this will be a valuable for our team to fix the issue.' - ), - errorPrimaryButtonLabel: t('Close'), - errorRedirectPath: RouterPath.Home - }) - ); - } - }) - .catch(error => { - console.error(error.message, 'available provider request failed'); + setBuyCSPRStep(BuyCSPRSteps.Provider); + } + }, + onError: error => { navigate( ErrorPath, createErrorLocationState({ - errorHeaderText: error.message || t('Something went wrong'), + errorHeaderText: t('Something went wrong'), errorContentText: - typeof error.data === 'string' - ? error.data - : t( - 'Please check browser console for error details, this will be a valuable for our team to fix the issue.' - ), + error.message || + t( + 'Please check browser console for error details, this will be a valuable for our team to fix the issue.' + ), errorPrimaryButtonLabel: t('Close'), errorRedirectPath: RouterPath.Home }) ); - }) - .finally(() => setLoadingAvailableProviders(false)); + } + }); }; const handleSubmit = () => { - setLoadingRedirectUrl(true); if (activeAccount && selectedProvider) { - const data: SelectionPostRequestData = { + const data: IProviderSelectionData = { account: activeAccount.publicKey, fiatCurrency: selectedCurrency.code, cryptoAmount: null, @@ -198,45 +167,28 @@ export const BuyCSPRPage = () => { fiatAmount }; - dispatchFetchOnRampSelectionPost(data) - .then(({ payload }) => { - if ('location' in payload) { - window.open(payload.location, '_blank'); - } else { - navigate( - ErrorPath, - createErrorLocationState({ - errorHeaderText: t('Something went wrong'), - errorContentText: - payload.error.message || - t( - 'Please check browser console for error details, this will be a valuable for our team to fix the issue.' - ), - errorPrimaryButtonLabel: t('Close'), - errorRedirectPath: RouterPath.Home - }) - ); - } - }) - .catch(error => { + getOnRampProviderLocation(data, { + onSuccess: providerLocation => { + window.open(providerLocation.location, '_blank'); + }, + onError: error => { console.error(error.message, 'provider selection request failed'); navigate( ErrorPath, createErrorLocationState({ - errorHeaderText: error.message || t('Something went wrong'), + errorHeaderText: t('Something went wrong'), errorContentText: - typeof error.data === 'string' - ? error.data - : t( - 'Please check browser console for error details, this will be a valuable for our team to fix the issue.' - ), + error.message || + t( + 'Please check browser console for error details, this will be a valuable for our team to fix the issue.' + ), errorPrimaryButtonLabel: t('Close'), errorRedirectPath: RouterPath.Home }) ); - }) - .finally(() => setLoadingRedirectUrl(false)); + } + }); } }; @@ -246,11 +198,14 @@ export const BuyCSPRPage = () => { availableCountries={availableCountries} selectedCountry={selectedCountry} setSelectedCountry={setSelectedCountry} + isLoadingOnRampCountriesAndCurrencies={ + isLoadingOnRampCountriesAndCurrencies + } /> ), [BuyCSPRSteps.Amount]: ( { ), [BuyCSPRSteps.NoProvider]: ( ) }; @@ -302,7 +257,7 @@ export const BuyCSPRPage = () => { <> @@ -317,7 +272,7 @@ export const BuyCSPRPage = () => { ) : null}