Skip to content

Commit

Permalink
refactor: remove swap findChildren
Browse files Browse the repository at this point in the history
  • Loading branch information
alessey committed Mar 4, 2025
1 parent c2d4c67 commit e9360fd
Show file tree
Hide file tree
Showing 9 changed files with 202 additions and 92 deletions.
94 changes: 57 additions & 37 deletions playground/nextjs-app-router/components/demo/Swap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,45 +103,65 @@ function SwapComponent() {
</div>
) : null}

<Swap
className="w-full border sm:w-[500px]"
onStatus={handleOnStatus}
onSuccess={handleOnSuccess}
onError={handleOnError}
config={{
maxSlippage: defaultMaxSlippage || FALLBACK_DEFAULT_MAX_SLIPPAGE,
}}
isSponsored={isSponsored}
>
<SwapSettings>
<SwapSettingsSlippageTitle>Max. slippage</SwapSettingsSlippageTitle>
<SwapSettingsSlippageDescription>
Your swap will revert if the prices change by more than the selected
percentage.
</SwapSettingsSlippageDescription>
<SwapSettingsSlippageInput />
</SwapSettings>
<SwapAmountInput
label="Sell"
<div className="relative flex flex-col gap-6">
<h2>Swap Default</h2>
<Swap
className="w-full border sm:w-[500px]"
swappableTokens={swappableTokens}
token={ethToken}
type="from"
toToken={usdcToken}
fromToken={ethToken}
onStatus={handleOnStatus}
onSuccess={handleOnSuccess}
onError={handleOnError}
config={{
maxSlippage: defaultMaxSlippage || FALLBACK_DEFAULT_MAX_SLIPPAGE,
}}
isSponsored={isSponsored}
headerLeftContent={<div>test</div>}
/>
<SwapToggleButton />
<SwapAmountInput
label="Buy"
swappableTokens={swappableTokens}
token={usdcToken}
type="to"
/>
<SwapButton
disabled={
ENVIRONMENT_VARIABLES[ENVIRONMENT.ENVIRONMENT] === 'production'
}
/>
<SwapMessage />
<SwapToast />
</Swap>

<h2>Swap with Children</h2>
<Swap
className="w-full border sm:w-[500px]"
onStatus={handleOnStatus}
onSuccess={handleOnSuccess}
onError={handleOnError}
config={{
maxSlippage: defaultMaxSlippage || FALLBACK_DEFAULT_MAX_SLIPPAGE,
}}
isSponsored={isSponsored}
headerLeftContent={<div>test</div>}
>
<SwapSettings>
<SwapSettingsSlippageTitle>Max. slippage</SwapSettingsSlippageTitle>
<SwapSettingsSlippageDescription>
Your swap will revert if the prices change by more than the selected
percentage.
</SwapSettingsSlippageDescription>
<SwapSettingsSlippageInput />
</SwapSettings>
<SwapAmountInput
label="Sell"
swappableTokens={swappableTokens}
token={ethToken}
type="from"
/>
<SwapToggleButton />
<SwapAmountInput
label="Buy"
swappableTokens={swappableTokens}
token={usdcToken}
type="to"
/>
<SwapButton
disabled={
ENVIRONMENT_VARIABLES[ENVIRONMENT.ENVIRONMENT] === 'production'
}
/>
<SwapMessage />
<SwapToast />
</Swap>
</div>
</div>
);
}
Expand Down
2 changes: 1 addition & 1 deletion playground/nextjs-app-router/onchainkit/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@coinbase/onchainkit",
"version": "0.37.4",
"version": "0.37.5",
"type": "module",
"repository": "https://github.com/coinbase/onchainkit.git",
"license": "MIT",
Expand Down
80 changes: 48 additions & 32 deletions src/swap/components/Swap.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
'use client';
import { Children, useMemo } from 'react';
import { useIsMounted } from '../../internal/hooks/useIsMounted';
import { useTheme } from '../../internal/hooks/useTheme';
import { findComponent } from '../../internal/utils/findComponent';
import { background, border, cn, color, text } from '../../styles/theme';
import { FALLBACK_DEFAULT_MAX_SLIPPAGE } from '../constants';
import type { SwapReact } from '../types';
Expand All @@ -14,12 +12,43 @@ import { SwapSettings } from './SwapSettings';
import { SwapToast } from './SwapToast';
import { SwapToggleButton } from './SwapToggleButton';

function SwapDefaultContent({
swappableTokens,
toToken,
fromToken,
}: Pick<SwapReact, 'swappableTokens' | 'toToken' | 'fromToken'>) {
return (
<>
<SwapSettings />
<SwapAmountInput
label="Sell"
swappableTokens={swappableTokens}
token={fromToken}
type="from"
/>
<SwapToggleButton />
<SwapAmountInput
label="Buy"
swappableTokens={swappableTokens}
token={toToken}
type="to"
/>
<SwapButton />
<SwapMessage />
<SwapToast />
</>
);
}

export function Swap({
children,
config = {
maxSlippage: FALLBACK_DEFAULT_MAX_SLIPPAGE,
},
className,
swappableTokens,
toToken,
fromToken,
experimental = { useAggregator: false },
isSponsored = false,
onError,
Expand All @@ -30,37 +59,21 @@ export function Swap({
}: SwapReact) {
const componentTheme = useTheme();

const {
inputs,
toggleButton,
swapButton,
swapMessage,
swapSettings,
swapToast,
} = useMemo(() => {
const childrenArray = Children.toArray(children);

return {
inputs: childrenArray.filter(findComponent(SwapAmountInput)),
toggleButton: childrenArray.find(findComponent(SwapToggleButton)),
swapButton: childrenArray.find(findComponent(SwapButton)),
swapMessage: childrenArray.find(findComponent(SwapMessage)),
swapSettings: childrenArray.find(findComponent(SwapSettings)),
swapToast: childrenArray.find(findComponent(SwapToast)),
};
}, [children]);

const isMounted = useIsMounted();

// prevents SSR hydration issue
if (!isMounted) {
return null;
}

return (
<SwapProvider
config={config}
experimental={experimental}
isSponsored={isSponsored}
swappableTokens={swappableTokens}
toToken={toToken}
fromToken={fromToken}
onError={onError}
onStatus={onStatus}
onSuccess={onSuccess}
Expand All @@ -71,24 +84,27 @@ export function Swap({
background.default,
border.radius,
color.foreground,
'flex w-[500px] flex-col px-6 pt-6 pb-4',
'relative flex w-full max-w-[500px] flex-col px-6 pt-6 pb-4',
className,
)}
data-testid="ockSwap_Container"
>
<div className="mb-4 flex items-center justify-between">
<div className="absolute flex w-1/2 items-center justify-between">
{headerLeftContent}
<h3 className={cn(text.title3)} data-testid="ockSwap_Title">
<h3
className={cn(text.title3, 'text-center')}
data-testid="ockSwap_Title"
>
{title}
</h3>
{swapSettings}
</div>
{inputs[0]}
<div className="relative h-1">{toggleButton}</div>
{inputs[1]}
{swapButton}
{swapToast}
<div className="flex">{swapMessage}</div>
{children ?? (
<SwapDefaultContent
swappableTokens={swappableTokens}
toToken={toToken}
fromToken={fromToken}
/>
)}
</div>
</SwapProvider>
);
Expand Down
46 changes: 34 additions & 12 deletions src/swap/components/SwapAmountInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,29 @@ export function SwapAmountInput({
type,
swappableTokens,
}: SwapAmountInputReact) {
const { address, to, from, handleAmountChange } = useSwapContext();
const {
address,
to,
from,
handleAmountChange,
swappableTokens: containerSwappableTokens,
toToken: containerToToken,
fromToken: containerFromToken,
} = useSwapContext();
const { sendAnalytics } = useAnalytics();

const source = useValue(type === 'from' ? from : to);
const destination = useValue(type === 'from' ? to : from);
const containerToken = useValue(
type === 'from' ? containerFromToken : containerToToken,
);
useEffect(() => {
if (token) {
source.setToken?.(token);
} else if (containerToken) {
source.setToken?.(containerToken);
}
}, [token, source.setToken]);
}, [token, source.setToken, containerToken]);

const handleMaxButtonClick = useCallback(() => {
if (!source.balance) {
Expand Down Expand Up @@ -82,11 +95,11 @@ export function SwapAmountInput({
// to test this since the components aren't actually rendering
const sourceTokenOptions = useMemo(() => {
return (
swappableTokens?.filter(
(swappableTokens ?? containerSwappableTokens)?.filter(
({ symbol }: Token) => symbol !== destination.token?.symbol,
) ?? []
);
}, [swappableTokens, destination.token]);
}, [swappableTokens, destination.token, containerSwappableTokens]);

const hasInsufficientBalance =
type === 'from' && Number(source.balance) < Number(source.amount);
Expand All @@ -104,13 +117,19 @@ export function SwapAmountInput({
className={cn(
background.secondary,
border.radius,
'box-border flex h-[148px] w-full flex-col items-start p-4',
'my-0.5 box-border flex h-[148px] w-full flex-col items-start p-4',
className,
)}
data-testid="ockSwapAmountInput_Container"
>
<div className="flex w-full items-center justify-between">
<span className={cn(text.label2, color.foregroundMuted)}>{label}</span>
<div
className={cn(
text.label2,
color.foregroundMuted,
'flex w-full items-center justify-between',
)}
>
{label}
</div>
<div className="flex w-full items-center justify-between">
<TextInput
Expand Down Expand Up @@ -140,12 +159,11 @@ export function SwapAmountInput({
)}
</div>
<div className="mt-4 flex w-full justify-between">
<div className="flex items-center">
<span className={cn(text.label2, color.foregroundMuted)}>
<div className={cn('mt-4 flex w-full items-center justify-between')}>
<span className={cn(text.label2, color.foregroundMuted, 'grow')}>
{formatUSD(source.amountUSD)}
</span>
</div>
<span className={cn(text.label2, color.foregroundMuted)}>{''}</span>
<div className="flex items-center">
{source.balance && (
<span
Expand All @@ -155,11 +173,15 @@ export function SwapAmountInput({
{type === 'from' && address && (
<button
type="button"
className="flex cursor-pointer items-center justify-center px-2 py-1"
className={cn(
text.label1,
color.primary,
'flex cursor-pointer items-center justify-center px-2 py-1',
)}
data-testid="ockSwapAmountInput_MaxButton"
onClick={handleMaxButtonClick}
>
<span className={cn(text.label1, color.primary)}>Max</span>
Max
</button>
)}
</div>
Expand Down
10 changes: 7 additions & 3 deletions src/swap/components/SwapButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import { ConnectWallet } from '@/wallet/components/ConnectWallet';
import type { SwapButtonReact } from '../types';
import { useSwapContext } from './SwapProvider';

export function SwapButton({ className, disabled = false }: SwapButtonReact) {
export function SwapButton({
className,
label = 'Swap',
disabled = false,
}: SwapButtonReact) {
const {
address,
to,
Expand Down Expand Up @@ -33,7 +37,7 @@ export function SwapButton({ className, disabled = false }: SwapButtonReact) {

// prompt user to connect wallet
if (!isDisabled && !address) {
return <ConnectWallet className="mt-4 w-full" />;
return <ConnectWallet className={cn('mt-4 w-full', className)} />;
}

return (
Expand All @@ -55,7 +59,7 @@ export function SwapButton({ className, disabled = false }: SwapButtonReact) {
{isLoading ? (
<Spinner />
) : (
<span className={cn(text.headline, color.inverse)}>Swap</span>
<span className={cn(text.headline, color.inverse)}>{label}</span>
)}
</button>
);
Expand Down
6 changes: 6 additions & 0 deletions src/swap/components/SwapProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ export function SwapProvider({
},
experimental,
isSponsored,
swappableTokens,
toToken,
fromToken,
onError,
onStatus,
onSuccess,
Expand Down Expand Up @@ -416,6 +419,9 @@ export function SwapProvider({
setIsToastVisible,
setTransactionHash,
transactionHash,
swappableTokens,
toToken,
fromToken,
});

return <SwapContext.Provider value={value}>{children}</SwapContext.Provider>;
Expand Down
Loading

0 comments on commit e9360fd

Please sign in to comment.