Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Chain favourite #2300

Merged
merged 17 commits into from
Oct 19, 2023
2 changes: 1 addition & 1 deletion .github/workflows/pr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ jobs:
VITE_TAG: ${{ needs.prepare.outputs.image_tag }}
CURRENT_BRANCH: ${{ needs.prepare.outputs.current_branch }}
NODE_OPTIONS: '--max_old_space_size=4096'
run: yarn build
run: yarn build-dev
XiaoYhun marked this conversation as resolved.
Show resolved Hide resolved

- name: Docker build and push
uses: docker/build-push-action@v2
Expand Down
316 changes: 316 additions & 0 deletions src/components/Header/web3/NetworkModal/DraggableNetworkButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,316 @@
import { ChainId, getChainType } from '@kyberswap/ks-sdk-core'
import { Trans, t } from '@lingui/macro'
import { motion, useAnimationControls, useDragControls } from 'framer-motion'
import { rgba } from 'polished'
import { stringify } from 'querystring'
import { MutableRefObject, RefObject, useState } from 'react'
import { isMobile } from 'react-device-detect'
import { Minus, Plus } from 'react-feather'
import { useNavigate } from 'react-router-dom'
import { Text } from 'rebass'
import styled, { css } from 'styled-components'

import Icon from 'components/Icons/Icon'
import Row from 'components/Row'
import { MouseoverTooltip } from 'components/Tooltip'
import { NetworkInfo } from 'constants/networks/type'
import { Z_INDEXS } from 'constants/styles'
import { SUPPORTED_WALLETS } from 'constants/wallets'
import { useActiveWeb3React } from 'hooks'
import { ChainState } from 'hooks/useChainsConfig'
import useParsedQueryString from 'hooks/useParsedQueryString'
import useTheme from 'hooks/useTheme'
import { useChangeNetwork } from 'hooks/web3/useChangeNetwork'

const NewLabel = styled.span`
font-size: 12px;
color: ${({ theme }) => theme.red};
margin-left: 2px;
margin-top: -10px;
`

const MaintainLabel = styled.span`
font-size: 8px;
color: ${({ theme }) => theme.red};
margin-left: 2px;
margin-top: -10px;
`

const ListItem = styled(motion.div)<{ selected?: boolean; $disabled?: boolean; $dragging?: boolean }>`
width: 100%;
display: flex;
justify-content: center;
align-items: center;
padding: 10px 8px;
border-radius: 999px;
overflow: hidden;
white-space: nowrap;
font-size: 14px;
height: 36px;
color: ${({ theme }) => theme.subText};
background-color: ${({ theme }) => theme.tableHeader};
user-select: none;
cursor: pointer;
gap: 6px;
transition: background-color 0.2s ease;
.drag-button {
opacity: 0;
display: none;
}
:hover .drag-button {
opacity: 1;
display: block;
}

${({ theme, selected }) =>
selected
? css`
background-color: ${theme.buttonBlack};
& > div {
color: ${theme.text};
}
`
: css`
:hover {
background-color: ${theme.background};
}
`}

${({ $disabled }) =>
$disabled &&
css`
cursor: not-allowed;
color: ${({ theme }) => theme.subText + '72'};
`}

${({ theme }) => theme.mediaWidth.upToSmall`
font-size: 12px;
`};
`

const CircleGreen = styled.div`
height: 16px;
width: 16px;
background-color: ${({ theme }) => theme.primary};
background-clip: content-box;
border: solid 2px ${({ theme }) => rgba(theme.primary, 0.3)};
border-radius: 8px;
margin-left: auto;
`
const WalletWrapper = styled.div`
height: 18px;
width: 18px;
margin-left: auto;
margin-right: 4px;
> img {
height: 18px;
width: 18px;
}
`

export default function DraggableNetworkButton({
droppableRefs,
networkInfo,
activeChainIds,
isSelected,
disabledMsg,
isEdittingMobile,
isAddButton,
dragConstraints,
customToggleModal,
customOnSelectNetwork,
onChangedNetwork,
onDrop,
onFavoriteClick,
}: {
droppableRefs: MutableRefObject<HTMLDivElement[]>
networkInfo: NetworkInfo
activeChainIds?: ChainId[]
isSelected?: boolean
disabledMsg?: string
isEdittingMobile?: boolean
isAddButton?: boolean
dragConstraints?: RefObject<Element>
customToggleModal?: () => void
customOnSelectNetwork?: (chainId: ChainId) => void
onChangedNetwork?: () => void
onDrop?: (dropIndex: string) => void
onFavoriteClick?: () => void
}) {
const theme = useTheme()
const { isWrongNetwork, walletSolana, walletEVM } = useActiveWeb3React()
const { changeNetwork } = useChangeNetwork()
const qs = useParsedQueryString()
const navigate = useNavigate()
const [isDragging, setIsDragging] = useState(false)
const dragControls = useDragControls()
const animateControls = useAnimationControls()
const { state, icon, chainId, name } = networkInfo
const isMaintenance = state === ChainState.MAINTENANCE
const disabled = (activeChainIds ? !activeChainIds?.includes(chainId) : false) || isMaintenance
const selected = isSelected && !isWrongNetwork
const walletKey =
chainId === ChainId.SOLANA ? walletSolana.walletKey : walletEVM.chainId === chainId ? walletEVM.walletKey : null

const handleChainSelect = () => {
if (disabled) return
customToggleModal?.()
if (customOnSelectNetwork) {
customOnSelectNetwork(chainId)
} else if (getChainType(chainId) === getChainType(chainId)) {
changeNetwork(chainId, () => {
const { inputCurrency, outputCurrency, ...rest } = qs
navigate(
{
search: stringify(rest),
},
{ replace: true },
)
onChangedNetwork?.()
})
} else {
changeNetwork(chainId, () => {
navigate(
{
search: '',
},
{ replace: true },
)
onChangedNetwork?.()
})
}
}

const variants = {
dragging: {
boxShadow: '0 5px 10px #00000060',
filter: 'brightness(0.8)',
scale: 1.05,
zIndex: 2,
},
normal: {
boxShadow: '0 0px 0px #00000060',
filter: 'brightness(1)',
scale: 1,
zIndex: 'unset',
x: 0,
y: 0,
},
}

const handleDragStart = () => {
animateControls.start('dragging')
setIsDragging(true)
}
const handleDragEnd = (e: any) => {
animateControls.start('normal')

const eventTarget = e.target as HTMLDivElement

if (!eventTarget) return

const droppedElPosition = [
eventTarget.getBoundingClientRect().left + eventTarget.getBoundingClientRect().width / 2,
eventTarget.getBoundingClientRect().top + eventTarget.getBoundingClientRect().height / 2,
]

const currentEl: HTMLDivElement | undefined = droppableRefs.current?.find((el: HTMLDivElement) => {
const { left, top, right, bottom } = el.getBoundingClientRect()
return (
left < droppedElPosition[0] &&
top < droppedElPosition[1] &&
right > droppedElPosition[0] &&
bottom > droppedElPosition[1]
)
})
if (currentEl) {
onDrop?.(currentEl.id)
}
}

return (
<MouseoverTooltip
style={{ zIndex: Z_INDEXS.MODAL + 1 }}
key={networkInfo.chainId}
text={
disabled && !isDragging
? isMaintenance
? t`Chain under maintenance. We will be back as soon as possible`
: disabledMsg
: ''
}
width="fit-content"
placement="top"
>
<ListItem
drag
dragListener={false}
dragMomentum={false}
dragControls={dragControls}
dragConstraints={dragConstraints}
dragElastic={0}
dragTransition={{ bounceStiffness: 500 }}
onDragStart={handleDragStart}
onDragEnd={handleDragEnd}
onLayoutAnimationStart={() => setIsDragging(true)}
onLayoutAnimationComplete={() => setIsDragging(false)}
layoutId={networkInfo.chainId.toString() + networkInfo.route}
selected={selected}
animate={animateControls}
transition={{ type: 'spring', damping: 50, stiffness: 1000 }}
variants={variants}
style={{ boxShadow: '0 0px 0px #00000060' }}
onClick={() => !selected && !isDragging && handleChainSelect()}
$disabled={disabled}
>
<img src={icon} alt="Switch Network" style={{ height: '20px', width: '20px' }} />
XiaoYhun marked this conversation as resolved.
Show resolved Hide resolved
<Row flexGrow={1} gap="6px">
<Text as="span" textAlign="left">
{name}
</Text>
{state === ChainState.NEW && (
<NewLabel>
<Trans>New</Trans>
</NewLabel>
)}
{isMaintenance && (
<MaintainLabel>
<Trans>Maintainance</Trans>
</MaintainLabel>
)}
{selected && !walletKey && <CircleGreen />}
{walletKey && (
<WalletWrapper>
<img src={SUPPORTED_WALLETS[walletKey].icon} alt={SUPPORTED_WALLETS[walletKey].name + ' icon'} />
</WalletWrapper>
)}
</Row>
{isMobile ? (
isEdittingMobile && (
<div
style={{ height: '16px' }}
onClick={e => {
e.stopPropagation()
onFavoriteClick?.()
}}
>
{isAddButton ? <Plus size={16} color={theme.subText} /> : <Minus size={16} color={theme.subText} />}
</div>
)
) : (
<div
className="drag-button"
style={{ cursor: 'grab' }}
onPointerDown={e => {
dragControls.start(e)
e.stopPropagation()
}}
onClick={e => e.stopPropagation()}
>
<Icon id="drag-indicator" color={theme.border} />
</div>
)}
</ListItem>
</MouseoverTooltip>
)
}
Loading
Loading