mars-v2-frontend/src/components/header/ChainSelect.tsx
2024-02-23 15:10:03 +01:00

170 lines
5.5 KiB
TypeScript

import classNames from 'classnames'
import React, { useCallback, useMemo } from 'react'
import { useNavigate, useSearchParams } from 'react-router-dom'
import { useSWRConfig } from 'swr'
import Button from 'components/common/Button'
import { ExternalLink } from 'components/common/Icons'
import Overlay from 'components/common/Overlay'
import Radio from 'components/common/Radio'
import Text from 'components/common/Text'
import ChainLogo from 'components/common/chain/ChainLogo'
import chains from 'configs/chains'
import useCurrentChainId from 'hooks/localStorage/useCurrentChainId'
import useChainConfig from 'hooks/useChainConfig'
import useToggle from 'hooks/useToggle'
import useStore from 'store'
import { NETWORK } from 'types/enums/network'
import { ChainInfoID } from 'types/enums/wallet'
import { getRoute } from 'utils/route'
interface V1Outpost {
chainId: ChainInfoID
name: string
url: string
target: '_blank' | '_self'
}
interface ChainOptionProps {
chainConfig?: ChainConfig
onSelect?: (chain: ChainConfig) => void
active: boolean
outpost?: V1Outpost
}
const v1Outposts: V1Outpost[] = [
{
chainId: ChainInfoID.Neutron1,
name: 'Neutron',
url: 'https://neutron.marsprotocol.io',
target: '_blank',
},
{
chainId: ChainInfoID.Pion1,
name: 'Neutron Testnet',
url: '/v1',
target: '_self',
},
{ chainId: ChainInfoID.Osmosis1, name: 'Osmosis', url: '/v1', target: '_self' },
{ chainId: ChainInfoID.OsmosisDevnet, name: 'Osmosis Devnet', url: '/v1', target: '_self' },
]
export default function ChainSelect() {
const [showMenu, setShowMenu] = useToggle()
const chainConfig = useChainConfig()
const { mutate } = useSWRConfig()
const navigate = useNavigate()
const [searchParams] = useSearchParams()
const isV1 = useStore((s) => s.isV1)
const [_, setCurrentChainId] = useCurrentChainId()
const selectChain = useCallback(
async (chainConfig: ChainConfig) => {
setShowMenu(false)
setCurrentChainId(chainConfig.id)
mutate(() => true)
useStore.setState({
chainConfig,
isV1: false,
client: undefined,
address: undefined,
userDomain: undefined,
balances: [],
})
navigate(getRoute('trade', searchParams))
},
[setCurrentChainId, setShowMenu, mutate, navigate, searchParams],
)
const ChainOption = (props: ChainOptionProps) => {
const { onSelect, active, outpost, chainConfig } = props
return (
<div
className={classNames(
'w-full px-4 py-3 flex gap-3 group/chain text-white items-center',
active ? 'pointer-events-none' : 'opacity-60 hover:opacity-100',
)}
role='button'
onClick={
onSelect && chainConfig
? () => onSelect(chainConfig)
: () => {
if (chainConfig) {
setCurrentChainId(chainConfig.id)
useStore.setState({
chainConfig,
})
}
window.open(outpost?.url, outpost?.target)
}
}
>
<Radio active={active} className='group-hover/account:opacity-100' />
<Text size='sm'>{outpost ? 'v1' : 'v2'} Outpost</Text>
{outpost && outpost.target !== '_self' && <ExternalLink className='inline w-4 -mb-0.5' />}
</div>
)
}
const availableChains = useMemo(() => {
const currentNetworkType = process.env.NEXT_PUBLIC_NETWORK ?? NETWORK.TESTNET
const availableChains: { chainId: ChainInfoID; name: string }[] = []
Object.entries(chains).forEach(([chainId, chainConfig]) => {
if (chainConfig.network !== currentNetworkType) return
availableChains.push({ chainId: chainId as ChainInfoID, name: chainConfig.name })
})
if (currentNetworkType === NETWORK.TESTNET) return availableChains
v1Outposts.forEach((v1Outpost) => {
if (!availableChains.find((chain) => chain.chainId === v1Outpost.chainId))
availableChains.push({ chainId: v1Outpost.chainId, name: v1Outpost.name })
})
return availableChains
}, [])
return (
<div className='relative'>
<Button
leftIcon={<ChainLogo chainID={chainConfig.id} className='w-4' />}
iconClassName='w-5 h-5'
color='secondary'
onClick={() => setShowMenu()}
className={classNames('!p-0 w-8 flex items-center justify-center')}
/>
<Overlay
show={showMenu}
setShow={setShowMenu}
className='right-0 w-[200px] mt-2 overflow-hidden'
>
{availableChains.map((chain, index) => (
<React.Fragment key={chain.chainId}>
<div
className={classNames(
'flex items-center gap-2 px-4 py-3 border-b bg-white/10 border-white/20',
index > 0 && 'border-t',
)}
>
<ChainLogo chainID={chain.chainId} className='w-5' />
<Text>{chain.name}</Text>
</div>
{!!chains[chain.chainId] && (
<ChainOption
chainConfig={chains[chain.chainId]}
onSelect={() => selectChain(chains[chain.chainId])}
active={chainConfig.name === chain.name && !isV1}
/>
)}
<ChainOption
chainConfig={chains[chain.chainId]}
outpost={v1Outposts.find((outpost) => outpost.chainId === chain.chainId)}
active={chainConfig.name === chain.name && isV1}
/>
</React.Fragment>
))}
</Overlay>
</div>
)
}