diff --git a/packages/checkout/widgets-lib/src/widgets/sale/SaleWidget.tsx b/packages/checkout/widgets-lib/src/widgets/sale/SaleWidget.tsx index 89f97d876b..01295330c9 100644 --- a/packages/checkout/widgets-lib/src/widgets/sale/SaleWidget.tsx +++ b/packages/checkout/widgets-lib/src/widgets/sale/SaleWidget.tsx @@ -40,7 +40,9 @@ import { sendSaleWidgetCloseEvent } from './SaleWidgetEvents'; import { EventTargetContext } from '../../context/event-target-context/EventTargetContext'; type OptionalWidgetParams = Pick; -type RequiredWidgetParams = Required>; +type RequiredWidgetParams = Required< +Omit +>; type WidgetParams = RequiredWidgetParams & OptionalWidgetParams; export interface SaleWidgetProps extends WidgetParams { @@ -94,9 +96,7 @@ export default function SaleWidget(props: SaleWidgetProps) { viewDispatch({ payload: { type: ViewActions.UPDATE_VIEW, - view: { - type: SaleWidgetViews.PAYMENT_METHODS, - }, + view: { type: SaleWidgetViews.PAYMENT_METHODS }, }, }); } @@ -164,6 +164,14 @@ export default function SaleWidget(props: SaleWidgetProps) { showSwapOption={config.isSwapEnabled} showBridgeOption={config.isBridgeEnabled} onCloseButtonClick={() => sendSaleWidgetCloseEvent(eventTarget)} + onBackButtonClick={() => { + viewDispatch({ + payload: { + type: ViewActions.UPDATE_VIEW, + view: { type: SaleWidgetViews.PAYMENT_METHODS }, + }, + }); + }} amount={viewState.view.data?.amount} tokenAddress={viewState.view.data?.tokenAddress} heading={viewState.view.data?.heading} diff --git a/packages/checkout/widgets-lib/src/widgets/sale/context/SaleContextProvider.tsx b/packages/checkout/widgets-lib/src/widgets/sale/context/SaleContextProvider.tsx index b3bb227eb0..af03edee6e 100644 --- a/packages/checkout/widgets-lib/src/widgets/sale/context/SaleContextProvider.tsx +++ b/packages/checkout/widgets-lib/src/widgets/sale/context/SaleContextProvider.tsx @@ -23,6 +23,7 @@ import { SaleWidgetViews, } from '../../../context/view-context/SaleViewContextTypes'; import { + SharedViews, ViewActions, ViewContext, } from '../../../context/view-context/ViewContext'; @@ -39,6 +40,7 @@ import { SmartCheckoutError, SmartCheckoutErrorTypes, } from '../types'; +import { getTopUpViewData } from '../functions/smartCheckoutUtils'; import { useSmartCheckout } from '../hooks/useSmartCheckout'; import { useClientConfig, defaultClientConfig } from '../hooks/useClientConfig'; @@ -188,10 +190,7 @@ export function SaleContextProvider(props: { const fromTokenAddress = currency?.erc20Address || ''; const goBackToPaymentMethods = useCallback( - ( - type?: SalePaymentTypes | undefined, - data?: Record, - ) => { + (type?: SalePaymentTypes | undefined, data?: Record) => { setPaymentMethod(type); viewDispatch({ payload: { @@ -321,7 +320,20 @@ export function SaleContextProvider(props: { === SmartCheckoutErrorTypes.FRACTIONAL_BALANCE_BLOCKED ) { disablePaymentTypes([SalePaymentTypes.CRYPTO]); - goBackToPaymentMethods(undefined); + setPaymentMethod(undefined); + viewDispatch({ + payload: { + type: ViewActions.UPDATE_VIEW, + view: { + type: SharedViews.TOP_UP_VIEW, + data: getTopUpViewData( + smartCheckoutError, + fromTokenAddress, + ), + }, + }, + }); + return; } goToErrorView(smartCheckoutError.type, smartCheckoutError.data); @@ -379,7 +391,7 @@ export function SaleContextProvider(props: { break; } } - }, [smartCheckoutResult]); + }, [smartCheckoutResult, smartCheckoutError, sign, amount, fromTokenAddress]); useEffect(() => { const invalidItems = !items || items.length === 0; diff --git a/packages/checkout/widgets-lib/src/widgets/sale/functions/smartCheckoutUtils.ts b/packages/checkout/widgets-lib/src/widgets/sale/functions/smartCheckoutUtils.ts index abe9401ae4..bc741142f6 100644 --- a/packages/checkout/widgets-lib/src/widgets/sale/functions/smartCheckoutUtils.ts +++ b/packages/checkout/widgets-lib/src/widgets/sale/functions/smartCheckoutUtils.ts @@ -14,6 +14,7 @@ import { } from '@imtbl/checkout-sdk'; import { BigNumber } from 'ethers'; import { calculateCryptoToFiat, formatFiatString } from '../../../lib/utils'; +import { SmartCheckoutError } from '../types'; export const MAX_GAS_LIMIT = '30000000'; @@ -155,3 +156,45 @@ export const filterSmartCheckoutResult = ( }, }; }; + +export const getTopUpViewData = (smartCheckoutError: SmartCheckoutError, tokenAddress: string) => { + const transactionRequirements = smartCheckoutError?.data?.transactionRequirements || []; + + const native = transactionRequirements.find( + ({ type }) => type === ItemType.NATIVE, + ); + const erc20 = transactionRequirements.find( + ({ type }) => type === ItemType.ERC20, + ); + + // FIXME: Get token symbols from requirements (ie. erc20.symbol) + const balances = { + erc20: { + value: erc20?.delta.formattedBalance, + symbol: 'USDC', + }, + native: { + value: native?.delta.formattedBalance, + symbol: 'IMX', + }, + }; + + const heading = ['views.PAYMENT_METHODS.topUp.heading']; + let subheading = ['views.PAYMENT_METHODS.topUp.subheading.both', balances]; + + if (native?.sufficient && !erc20?.sufficient) { + subheading = ['views.PAYMENT_METHODS.topUp.subheading.erc20', balances]; + } + if (!native?.sufficient && erc20?.sufficient) { + subheading = ['views.PAYMENT_METHODS.topUp.subheading.native', balances]; + } + + const amount = erc20?.delta.balance.toNumber() || 0; + + return { + amount, + heading, + subheading, + tokenAddress, + }; +}; diff --git a/packages/checkout/widgets-lib/src/widgets/sale/hooks/useInsufficientBalance.ts b/packages/checkout/widgets-lib/src/widgets/sale/hooks/useInsufficientBalance.ts deleted file mode 100644 index 941418d2b1..0000000000 --- a/packages/checkout/widgets-lib/src/widgets/sale/hooks/useInsufficientBalance.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { - SharedViews, - ViewActions, - ViewContext, -} from 'context/view-context/ViewContext'; -import { useContext, useEffect } from 'react'; -import { ItemType } from '@imtbl/checkout-sdk'; -import { useSaleContext } from '../context/SaleContextProvider'; -import { SmartCheckoutErrorTypes } from '../types'; - -export const useInsufficientBalance = () => { - const { viewDispatch } = useContext(ViewContext); - const { - smartCheckoutError, - fromTokenAddress: tokenAddress, - amount, - } = useSaleContext(); - - useEffect(() => { - if ( - smartCheckoutError?.data?.error?.message - !== SmartCheckoutErrorTypes.FRACTIONAL_BALANCE_BLOCKED - ) return; - - const transactionRequirements = smartCheckoutError?.data?.transactionRequirements || []; - - const native = transactionRequirements.find( - ({ type }) => type === ItemType.NATIVE, - ); - const erc20 = transactionRequirements.find( - ({ type }) => type === ItemType.ERC20, - ); - - // FIXME: Get token symbols from requirements (ie. erc20.symbol) - const balances = { - erc20: { - value: erc20?.delta.formattedBalance, - symbol: 'USDC', - }, - native: { - value: native?.delta.formattedBalance, - symbol: 'IMX', - }, - }; - - const heading = ['views.PAYMENT_METHODS.topUp.heading']; - let subheading = ['views.PAYMENT_METHODS.topUp.subheading.both', balances]; - - if (native?.sufficient && !erc20?.sufficient) { - subheading = ['views.PAYMENT_METHODS.topUp.subheading.erc20', balances]; - } - if (!native?.sufficient && erc20?.sufficient) { - subheading = ['views.PAYMENT_METHODS.topUp.subheading.native', balances]; - } - - viewDispatch({ - payload: { - type: ViewActions.UPDATE_VIEW, - view: { - type: SharedViews.TOP_UP_VIEW, - data: { - tokenAddress, - amount, - heading, - subheading, - }, - }, - }, - }); - }, [smartCheckoutError]); - - return null; -}; diff --git a/packages/checkout/widgets-lib/src/widgets/sale/views/PaymentMethods.tsx b/packages/checkout/widgets-lib/src/widgets/sale/views/PaymentMethods.tsx index 57addd3d7b..8300686aed 100644 --- a/packages/checkout/widgets-lib/src/widgets/sale/views/PaymentMethods.tsx +++ b/packages/checkout/widgets-lib/src/widgets/sale/views/PaymentMethods.tsx @@ -20,10 +20,8 @@ import { PaymentOptions } from '../components/PaymentOptions'; import { useSaleContext } from '../context/SaleContextProvider'; import { useSaleEvent } from '../hooks/useSaleEvents'; import { SaleErrorTypes, SignPaymentTypes } from '../types'; -import { useInsufficientBalance } from '../hooks/useInsufficientBalance'; export function PaymentMethods() { - useInsufficientBalance(); const { t } = useTranslation(); const { viewDispatch } = useContext(ViewContext); const { diff --git a/packages/checkout/widgets-lib/src/widgets/sale/views/SaleErrorView.tsx b/packages/checkout/widgets-lib/src/widgets/sale/views/SaleErrorView.tsx index e502c9367a..22f449da65 100644 --- a/packages/checkout/widgets-lib/src/widgets/sale/views/SaleErrorView.tsx +++ b/packages/checkout/widgets-lib/src/widgets/sale/views/SaleErrorView.tsx @@ -3,7 +3,10 @@ import { useContext } from 'react'; import { useTranslation } from 'react-i18next'; import { SalePaymentTypes } from '@imtbl/checkout-sdk'; import { StatusType } from '../../../components/Status/StatusType'; -import { StatusView, StatusViewProps } from '../../../components/Status/StatusView'; +import { + StatusView, + StatusViewProps, +} from '../../../components/Status/StatusView'; import { SaleErrorTypes } from '../types'; import { useSaleContext } from '../context/SaleContextProvider'; import { sendSaleWidgetCloseEvent } from '../SaleWidgetEvents'; @@ -17,18 +20,23 @@ interface ErrorHandlerConfig { } type SaleErrorViewProps = { - biomeTheme: BaseTokens - errorType: SaleErrorTypes | undefined, - transactionHash?: string, - blockExplorerLink?: string, + biomeTheme: BaseTokens; + errorType: SaleErrorTypes | undefined; + transactionHash?: string; + blockExplorerLink?: string; }; export function SaleErrorView({ - errorType = SaleErrorTypes.DEFAULT, transactionHash, blockExplorerLink, biomeTheme, + biomeTheme, + transactionHash, + blockExplorerLink, + errorType = SaleErrorTypes.DEFAULT, }: SaleErrorViewProps) { const { t } = useTranslation(); const { goBackToPaymentMethods } = useSaleContext(); - const { eventTargetState: { eventTarget } } = useContext(EventTargetContext); + const { + eventTargetState: { eventTarget }, + } = useContext(EventTargetContext); const closeWidget = () => { sendSaleWidgetCloseEvent(eventTarget); @@ -37,9 +45,11 @@ export function SaleErrorView({ const errorHandlersConfig: Record = { [SaleErrorTypes.TRANSACTION_FAILED]: { onActionClick: goBackToPaymentMethods, - onSecondaryActionClick: transactionHash ? () => { - window.open(blockExplorerLink); - } : closeWidget, + onSecondaryActionClick: transactionHash + ? () => { + window.open(blockExplorerLink); + } + : closeWidget, statusType: StatusType.FAILURE, statusIconStyles: { fill: biomeTheme.color.status.destructive.dim, @@ -126,8 +136,8 @@ export function SaleErrorView({ const getErrorViewProps = (): StatusViewProps => { const handlers = errorHandlersConfig[errorType] || {}; const secondaryActionText = errorType === SaleErrorTypes.TRANSACTION_FAILED && transactionHash - ? t(`views.SALE_FAIL.errors.${SaleErrorTypes.DEFAULT}.secondaryAction`) - : t(`views.SALE_FAIL.errors.${errorType}.secondaryAction`); + ? t(`views.SALE_FAIL.errors.${errorType}.secondaryAction`) + : t(`views.SALE_FAIL.errors.${SaleErrorTypes.DEFAULT}.secondaryAction`); return { testId: 'fail-view', @@ -145,7 +155,5 @@ export function SaleErrorView({ }, }; }; - return ( - - ); + return ; }