diff --git a/src/buy/components/BuyAmountInput.tsx b/src/buy/components/BuyAmountInput.tsx index 528534c745..4453ebab0f 100644 --- a/src/buy/components/BuyAmountInput.tsx +++ b/src/buy/components/BuyAmountInput.tsx @@ -1,6 +1,6 @@ import { isValidAmount } from '../../core/utils/isValidAmount'; import { TextInput } from '../../internal/components/TextInput'; -import { background, cn, color } from '../../styles/theme'; +import { background, border, cn, color } from '../../styles/theme'; import { formatAmount } from '../../swap/utils/formatAmount'; import { TokenChip } from '../../token'; import { useBuyContext } from './BuyProvider'; @@ -15,8 +15,9 @@ export function BuyAmountInput() { return (
Buy with
diff --git a/src/buy/components/BuyMessage.tsx b/src/buy/components/BuyMessage.tsx index 5bd7afefc5..6de618ca9d 100644 --- a/src/buy/components/BuyMessage.tsx +++ b/src/buy/components/BuyMessage.tsx @@ -1,4 +1,4 @@ -import { cn, color } from '../../styles/theme'; +import { cn, color, text } from '../../styles/theme'; import { isSwapError } from '../../swap/utils/isSwapError'; import { useBuyContext } from './BuyProvider'; @@ -16,7 +16,7 @@ export function BuyMessage() { ? color.foregroundMuted : color.error; - return
{message}
; + return
{message}
; } return null; diff --git a/src/buy/components/BuyOnrampItem.tsx b/src/buy/components/BuyOnrampItem.tsx index c4c263adb6..cc83d4a2c2 100644 --- a/src/buy/components/BuyOnrampItem.tsx +++ b/src/buy/components/BuyOnrampItem.tsx @@ -2,7 +2,7 @@ import { useCallback } from 'react'; import { appleSvg } from '../../internal/svg/appleSvg'; import { cardSvg } from '../../internal/svg/cardSvg'; import { coinbaseLogoSvg } from '../../internal/svg/coinbaseLogoSvg'; -import { cn, color } from '../../styles/theme'; +import { cn, color, text } from '../../styles/theme'; import { useBuyContext } from './BuyProvider'; type OnrampItemReact = { @@ -37,6 +37,7 @@ export function BuyOnrampItem({ className={cn( 'flex items-center gap-2 rounded-lg p-2', 'hover:bg-[var(--ock-bg-inverse)]', + text.label2, )} onClick={handleClick} type="button" diff --git a/src/buy/components/BuyProvider.tsx b/src/buy/components/BuyProvider.tsx index 0f4d0b7adf..bd5a0afaa9 100644 --- a/src/buy/components/BuyProvider.tsx +++ b/src/buy/components/BuyProvider.tsx @@ -99,6 +99,7 @@ export function BuyProvider({ const { onPopupClose } = useOnrampEventListeners({ updateLifecycleStatus, maxSlippage: config.maxSlippage, + lifecycleStatus, }); // used to detect when the popup is closed in order to stop loading state diff --git a/src/buy/components/BuyTokenItem.tsx b/src/buy/components/BuyTokenItem.tsx index 71fabe8241..c4d5cdd554 100644 --- a/src/buy/components/BuyTokenItem.tsx +++ b/src/buy/components/BuyTokenItem.tsx @@ -1,6 +1,6 @@ import { useCallback, useMemo } from 'react'; import { getRoundedAmount } from '../../core/utils/getRoundedAmount'; -import { cn, color, pressable } from '../../styles/theme'; +import { cn, color, pressable, text } from '../../styles/theme'; import type { SwapUnit } from '../../swap/types'; import { TokenImage } from '../../token'; import { useBuyContext } from './BuyProvider'; @@ -37,6 +37,7 @@ export function BuyTokenItem({ swapUnit }: { swapUnit?: SwapUnit }) { className={cn( 'flex items-center gap-2 rounded-lg p-2', !hasInsufficientBalance && pressable.default, + text.label2, )} onClick={handleClick} type="button" diff --git a/src/buy/hooks/useOnrampEventListeners.test.ts b/src/buy/hooks/useOnrampEventListeners.test.ts index 3a890b83dc..57e0881360 100644 --- a/src/buy/hooks/useOnrampEventListeners.test.ts +++ b/src/buy/hooks/useOnrampEventListeners.test.ts @@ -21,6 +21,13 @@ describe('useOnrampEventListeners', () => { useOnrampEventListeners({ updateLifecycleStatus: mockUpdateLifecycleStatus, maxSlippage: 0.5, + lifecycleStatus: { + statusName: 'init', + statusData: { + isMissingRequiredField: false, + maxSlippage: 0.5, + }, + }, }), ); @@ -38,6 +45,13 @@ describe('useOnrampEventListeners', () => { useOnrampEventListeners({ updateLifecycleStatus: mockUpdateLifecycleStatus, maxSlippage: 0.5, + lifecycleStatus: { + statusName: 'init', + statusData: { + isMissingRequiredField: false, + maxSlippage: 0.5, + }, + }, }), ); @@ -61,11 +75,53 @@ describe('useOnrampEventListeners', () => { }); }); + it('should not handle transition_view event if lifecycleStatus is transactionPending', () => { + renderHook(() => + useOnrampEventListeners({ + updateLifecycleStatus: mockUpdateLifecycleStatus, + maxSlippage: 0.5, + lifecycleStatus: { + statusName: 'transactionPending', + statusData: { + isMissingRequiredField: false, + maxSlippage: 0.5, + }, + }, + }), + ); + + const mockedSetupOnrampEventListeners = + setupOnrampEventListeners as unknown as Mock; + const onEventCallback = + mockedSetupOnrampEventListeners.mock.calls[0][0].onEvent; + + act(() => { + onEventCallback({ + eventName: 'transition_view', + }); + }); + + expect(mockUpdateLifecycleStatus).not.toHaveBeenCalledWith({ + statusName: 'transactionPending', + statusData: { + isMissingRequiredField: false, + maxSlippage: 0.5, + }, + }); + }); + it('should handle onramp success', () => { renderHook(() => useOnrampEventListeners({ updateLifecycleStatus: mockUpdateLifecycleStatus, maxSlippage: 0.5, + lifecycleStatus: { + statusName: 'init', + statusData: { + isMissingRequiredField: false, + maxSlippage: 0.5, + }, + }, }), ); @@ -93,6 +149,13 @@ describe('useOnrampEventListeners', () => { useOnrampEventListeners({ updateLifecycleStatus: mockUpdateLifecycleStatus, maxSlippage: 0.5, + lifecycleStatus: { + statusName: 'init', + statusData: { + isMissingRequiredField: false, + maxSlippage: 0.5, + }, + }, }), ); diff --git a/src/buy/hooks/useOnrampEventListeners.ts b/src/buy/hooks/useOnrampEventListeners.ts index 5f01541779..4a41dcf41e 100644 --- a/src/buy/hooks/useOnrampEventListeners.ts +++ b/src/buy/hooks/useOnrampEventListeners.ts @@ -7,15 +7,22 @@ import { setupOnrampEventListeners } from '../../fund/utils/setupOnrampEventList type UseOnrampLifecycleParams = { updateLifecycleStatus: (status: LifecycleStatus) => void; maxSlippage: number; + lifecycleStatus: LifecycleStatus; }; export const useOnrampEventListeners = ({ updateLifecycleStatus, maxSlippage, + lifecycleStatus, }: UseOnrampLifecycleParams) => { const handleOnrampEvent = useCallback( (data: EventMetadata) => { - if (data.eventName === 'transition_view') { + // Only update the lifecycle status if the current status is not 'transactionPending' + // Onramp emits a 'transition_view' event multiple times + if ( + data.eventName === 'transition_view' && + lifecycleStatus?.statusName !== 'transactionPending' + ) { updateLifecycleStatus({ statusName: 'transactionPending', statusData: { @@ -25,7 +32,7 @@ export const useOnrampEventListeners = ({ }); } }, - [maxSlippage, updateLifecycleStatus], + [maxSlippage, updateLifecycleStatus, lifecycleStatus?.statusName], ); const handleOnrampSuccess = useCallback(() => {