-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(tangle-dapp): Bridge UI on Tangle Dapp (#2307)
- Loading branch information
1 parent
21d7a18
commit 66e5fbf
Showing
21 changed files
with
584 additions
and
73 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
'use client'; | ||
|
||
import { DropdownMenuTrigger as DropdownTrigger } from '@radix-ui/react-dropdown-menu'; | ||
import { TokenIcon } from '@webb-tools/icons/TokenIcon'; | ||
import ChainOrTokenButton from '@webb-tools/webb-ui-components/components/buttons/ChainOrTokenButton'; | ||
import { | ||
Dropdown, | ||
DropdownBody, | ||
} from '@webb-tools/webb-ui-components/components/Dropdown'; | ||
import { MenuItem } from '@webb-tools/webb-ui-components/components/MenuItem'; | ||
import { ScrollArea } from '@webb-tools/webb-ui-components/components/ScrollArea'; | ||
import { FC } from 'react'; | ||
|
||
import AmountInput from '../../components/AmountInput/AmountInput'; | ||
import { useBridge } from '../../context/BridgeContext'; | ||
|
||
const AmountAndTokenInput: FC = () => { | ||
const { amount, setAmount, selectedToken, setSelectedToken, tokenOptions } = | ||
useBridge(); | ||
|
||
return ( | ||
<div className="flex items-center gap-2 bg-mono-20 dark:bg-mono-160 rounded-lg pr-4"> | ||
<AmountInput | ||
id="bridge-amount-input" | ||
title="Amount" | ||
amount={amount} | ||
setAmount={setAmount} | ||
baseInputOverrides={{ | ||
isFullWidth: true, | ||
}} | ||
placeholder="" | ||
wrapperClassName="!pr-0" | ||
/> | ||
<Dropdown> | ||
<DropdownTrigger asChild> | ||
<ChainOrTokenButton | ||
value={selectedToken.symbol} | ||
status="success" | ||
className="w-full bg-mono-0 dark:bg-mono-140 border-0 px-3" | ||
iconType="token" | ||
/> | ||
</DropdownTrigger> | ||
<DropdownBody className="border-0 w-[119px] min-w-fit"> | ||
<ScrollArea className="max-h-[300px]"> | ||
<ul> | ||
{tokenOptions.map((token) => { | ||
return ( | ||
<li key={token.id}> | ||
<MenuItem | ||
startIcon={<TokenIcon size="lg" name={token.symbol} />} | ||
onSelect={() => setSelectedToken(token)} | ||
className="px-3" | ||
> | ||
{token.symbol} | ||
</MenuItem> | ||
</li> | ||
); | ||
})} | ||
</ul> | ||
</ScrollArea> | ||
</DropdownBody> | ||
</Dropdown> | ||
</div> | ||
); | ||
}; | ||
|
||
export default AmountAndTokenInput; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
'use client'; | ||
|
||
import Button from '@webb-tools/webb-ui-components/components/buttons/Button'; | ||
import { FC } from 'react'; | ||
import { twMerge } from 'tailwind-merge'; | ||
|
||
import AddressInput, { | ||
AddressType, | ||
} from '../../components/AddressInput/AddressInput'; | ||
import { useBridge } from '../../context/BridgeContext'; | ||
import AmountAndTokenInput from './AmountAndTokenInput'; | ||
import ChainSelectors from './ChainSelectors'; | ||
import useActionButton from './useActionButton'; | ||
|
||
interface BridgeContainerProps { | ||
className?: string; | ||
} | ||
|
||
const BridgeContainer: FC<BridgeContainerProps> = ({ className }) => { | ||
const { destinationAddress, setDestinationAddress } = useBridge(); | ||
const { buttonAction, buttonText, isLoading } = useActionButton(); | ||
|
||
return ( | ||
<div | ||
className={twMerge( | ||
'max-w-[640px] min-h-[580px] bg-mono-0 dark:bg-mono-190 p-5 md:p-8', | ||
'rounded-xl border border-mono-40 dark:border-mono-160', | ||
'shadow-webb-lg dark:shadow-webb-lg-dark', | ||
'flex flex-col', | ||
className | ||
)} | ||
> | ||
<div className="flex-1 w-full flex flex-col justify-between"> | ||
<div className="space-y-8"> | ||
<ChainSelectors /> | ||
|
||
<AmountAndTokenInput /> | ||
|
||
<AddressInput | ||
id="bridge-destination-address-input" | ||
type={AddressType.Both} | ||
title="Receiver Address" | ||
baseInputOverrides={{ isFullWidth: true }} | ||
value={destinationAddress} | ||
setValue={setDestinationAddress} | ||
/> | ||
|
||
{/* TODO: Tx Info (Fees & Estimated Time) */} | ||
</div> | ||
<Button | ||
isFullWidth | ||
isDisabled={isLoading} | ||
isLoading={isLoading} | ||
onClick={buttonAction} | ||
loadingText="Connecting..." | ||
> | ||
{buttonText} | ||
</Button> | ||
</div> | ||
</div> | ||
); | ||
}; | ||
|
||
export default BridgeContainer; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
'use client'; | ||
|
||
import { DropdownMenuTrigger as DropdownTrigger } from '@radix-ui/react-dropdown-menu'; | ||
import { ChainConfig } from '@webb-tools/dapp-config/chains/chain-config.interface'; | ||
import { ArrowRight } from '@webb-tools/icons/ArrowRight'; | ||
import { ChainIcon } from '@webb-tools/icons/ChainIcon'; | ||
import ChainOrTokenButton from '@webb-tools/webb-ui-components/components/buttons/ChainOrTokenButton'; | ||
import { | ||
Dropdown, | ||
DropdownBody, | ||
} from '@webb-tools/webb-ui-components/components/Dropdown'; | ||
import { MenuItem } from '@webb-tools/webb-ui-components/components/MenuItem'; | ||
import { ScrollArea } from '@webb-tools/webb-ui-components/components/ScrollArea'; | ||
import { FC, useCallback } from 'react'; | ||
|
||
import { useBridge } from '../../context/BridgeContext'; | ||
|
||
interface ChainSelectorProps { | ||
selectedChain: ChainConfig; | ||
chainOptions: ChainConfig[]; | ||
onSelectChain: (chain: ChainConfig) => void; | ||
className?: string; | ||
} | ||
|
||
const ChainSelectors: FC = () => { | ||
const { | ||
selectedSourceChain, | ||
setSelectedSourceChain, | ||
selectedDestinationChain, | ||
setSelectedDestinationChain, | ||
sourceChainOptions, | ||
destinationChainOptions, | ||
} = useBridge(); | ||
|
||
const switchChains = useCallback(() => { | ||
const temp = selectedSourceChain; | ||
setSelectedDestinationChain(temp); | ||
setSelectedSourceChain(selectedDestinationChain); | ||
}, [ | ||
setSelectedSourceChain, | ||
setSelectedDestinationChain, | ||
selectedDestinationChain, | ||
selectedSourceChain, | ||
]); | ||
|
||
return ( | ||
<div className="flex flex-col md:flex-row justify-between items-center gap-3"> | ||
<ChainSelector | ||
selectedChain={selectedSourceChain} | ||
chainOptions={sourceChainOptions} | ||
onSelectChain={setSelectedSourceChain} | ||
className="flex-1 w-full md:w-auto" | ||
/> | ||
|
||
<div | ||
className="cursor-pointer p-1 rounded-full hover:bg-mono-20 dark:hover:bg-mono-160" | ||
onClick={switchChains} | ||
> | ||
<ArrowRight size="lg" className="rotate-90 md:rotate-0" /> | ||
</div> | ||
|
||
<ChainSelector | ||
selectedChain={selectedDestinationChain} | ||
chainOptions={destinationChainOptions} | ||
onSelectChain={setSelectedDestinationChain} | ||
className="flex-1 w-full md:w-auto" | ||
/> | ||
</div> | ||
); | ||
}; | ||
|
||
const ChainSelector: FC<ChainSelectorProps> = ({ | ||
selectedChain, | ||
chainOptions, | ||
onSelectChain, | ||
className, | ||
}) => { | ||
return ( | ||
<Dropdown className={className}> | ||
<DropdownTrigger asChild> | ||
<ChainOrTokenButton | ||
value={selectedChain.name} | ||
className="w-full bg-mono-20 dark:bg-mono-160 border-0 hover:bg-mono-20 dark:hover:bg-mono-160" | ||
iconType="chain" | ||
/> | ||
</DropdownTrigger> | ||
<DropdownBody className="border-0"> | ||
<ScrollArea className="max-h-[300px] w-[calc(100vw-74px)] md:w-[259px]"> | ||
<ul> | ||
{chainOptions.map((chain) => { | ||
return ( | ||
<li key={`${chain.chainType}-${chain.id}`}> | ||
<MenuItem | ||
startIcon={<ChainIcon size="lg" name={chain.name} />} | ||
onSelect={() => onSelectChain(chain)} | ||
> | ||
{chain.name} | ||
</MenuItem> | ||
</li> | ||
); | ||
})} | ||
</ul> | ||
</ScrollArea> | ||
</DropdownBody> | ||
</Dropdown> | ||
); | ||
}; | ||
|
||
export default ChainSelectors; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { FC, PropsWithChildren } from 'react'; | ||
|
||
import BridgeProvider from '../../context/BridgeContext'; | ||
|
||
const BridgeLayout: FC<PropsWithChildren> = ({ children }) => { | ||
return <BridgeProvider>{children}</BridgeProvider>; | ||
}; | ||
|
||
export default BridgeLayout; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { Metadata } from 'next'; | ||
import { FC } from 'react'; | ||
|
||
import createPageMetadata from '../../utils/createPageMetadata'; | ||
import BridgeContainer from './BridgeContainer'; | ||
|
||
export const metadata: Metadata = createPageMetadata({ | ||
title: 'Bridge', | ||
}); | ||
|
||
const Bridge: FC = () => { | ||
return ( | ||
<div> | ||
<BridgeContainer className="mx-auto" /> | ||
</div> | ||
); | ||
}; | ||
|
||
export default Bridge; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
'use client'; | ||
|
||
import { | ||
useConnectWallet, | ||
useWebContext, | ||
} from '@webb-tools/api-provider-environment'; | ||
import { useCallback, useMemo } from 'react'; | ||
|
||
export default function useActionButton() { | ||
const { activeAccount, activeWallet, loading, isConnecting } = | ||
useWebContext(); | ||
|
||
const { toggleModal } = useConnectWallet(); | ||
|
||
const noActiveAccountOrWallet = useMemo(() => { | ||
return !activeAccount || !activeWallet; | ||
}, [activeAccount, activeWallet]); | ||
|
||
const openWalletModal = useCallback(() => { | ||
toggleModal(true); | ||
}, [toggleModal]); | ||
|
||
const bridgeTx = useCallback(() => { | ||
// TODO: handle bridge Tx for each case from the source and destination chain | ||
}, []); | ||
|
||
return { | ||
isLoading: loading || isConnecting, | ||
buttonAction: noActiveAccountOrWallet ? openWalletModal : bridgeTx, | ||
buttonText: noActiveAccountOrWallet ? 'Connect' : 'Approve', | ||
}; | ||
} |
Oops, something went wrong.