170 lines
5.5 KiB
TypeScript
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>
|
|
)
|
|
}
|