add wallet draw

This commit is contained in:
saml33 2021-12-27 22:25:30 +11:00
parent 4335fd124b
commit a646861929
2 changed files with 265 additions and 111 deletions

View File

@ -19,10 +19,11 @@ import {
import { sortBy, sum } from 'lodash'
import {
ExclamationCircleIcon,
ExternalLinkIcon,
SwitchVerticalIcon,
} from '@heroicons/react/outline'
import { ChevronDownIcon, ChevronUpIcon } from '@heroicons/react/solid'
import { sleep } from '../utils'
import { abbreviateAddress, sleep } from '../utils'
import SwapTokenSelect from './SwapTokenSelect'
import { notify } from '../utils/notifications'
import { Token } from '../@types/types'
@ -33,6 +34,12 @@ import {
} from '@blockworks-foundation/mango-client'
import Button, { LinkButton } from './Button'
import { usdFormatter } from '../utils'
import { useViewport } from '../hooks/useViewport'
import { breakpoints } from './TradePageGrid'
import useLocalStorageState from '../hooks/useLocalStorageState'
import Modal from './Modal'
import { ElementTitle } from './styles'
import { WalletIcon } from './icons'
type UseJupiterProps = Parameters<typeof useJupiter>[0]
@ -59,6 +66,11 @@ const JupiterForm: FunctionComponent = () => {
outputMint: new PublicKey('MangoCzJ36AjZyKwVj3VnYU4GTonjfVEnJmvvWaxLac'),
slippage: 0.5,
})
const [hasSwapped, setHasSwapped] = useLocalStorageState('hasSwapped', false)
const [showWalletDraw, setShowWalletDraw] = useState(false)
const [walletTokenPrices, setWalletTokenPrices] = useState(null)
const { width } = useViewport()
const isMobile = width ? width < breakpoints.sm : false
const fetchWalletTokens = useCallback(async () => {
const ownedTokens = []
@ -285,6 +297,34 @@ const JupiterForm: FunctionComponent = () => {
return [0, 0]
}, [outputTokenStats])
const [walletTokensWithInfos] = useMemo(() => {
const userTokens = []
tokens.map((item) => {
const found = walletTokens.find(
(token) => token.account.mint.toBase58() === item?.address
)
if (found) {
userTokens.push({ ...found, item })
}
})
return [userTokens]
}, [walletTokens, tokens])
const getWalletTokenPrices = async () => {
const ids = walletTokensWithInfos.map(
(token) => token.item.extensions.coingeckoId
)
const response = await fetch(
`https://api.coingecko.com/api/v3/simple/price?ids=${ids.toString()}&vs_currencies=usd`
)
const data = await response.json()
setWalletTokenPrices(data)
}
useEffect(() => {
getWalletTokenPrices()
}, [walletTokensWithInfos])
const handleSelectRoute = (route) => {
setSelectedRoute(route)
}
@ -324,7 +364,7 @@ const JupiterForm: FunctionComponent = () => {
const tooltipContent = (tooltipProps) => {
if (tooltipProps.payload.length > 0) {
return (
<div className="bg-th-bkg-1 flex p-2 rounded">
<div className="bg-th-bkg-3 flex min-w-[120px] p-2 rounded">
<div>
<div className="text-th-fgd-3 text-xs">Time</div>
<div className="font-bold text-th-fgd-1 text-xs">
@ -345,12 +385,106 @@ const JupiterForm: FunctionComponent = () => {
return (
<div className="max-w-md mx-auto">
{connected ? (
{connected && walletTokenPrices && !isMobile ? (
<div
className={`flex transform top-22 left-0 w-80 fixed overflow-hidden ease-in-out transition-all duration-300 z-30 ${
showWalletDraw ? 'translate-x-0' : 'ml-16 -translate-x-full'
}`}
>
<aside
className={`bg-th-bkg-3 max-h-[500px] overflow-auto pb-4 pt-6 rounded-r-md w-64`}
>
<div className="flex items-center justify-between pb-2 px-4">
<div className="font-bold text-base text-th-fgd-1">Wallet</div>
<a
className="flex items-center text-th-fgd-4 text-xs hover:text-th-fgd-3"
href={`https://explorer.solana.com/address/${wallet?.publicKey}`}
target="_blank"
rel="noopener noreferrer"
>
<div className="bg-th-green h-1.5 mr-1.5 rounded-full w-1.5" />
{abbreviateAddress(wallet.publicKey)}
<ExternalLinkIcon className="h-3.5 ml-0.5 -mt-0.5 w-3.5" />
</a>
</div>
{walletTokensWithInfos
.sort((a, b) => {
const aId = a.item.extensions.coingeckoId
const bId = b.item.extensions.coingeckoId
return (
b.uiBalance * walletTokenPrices[bId]?.usd -
a.uiBalance * walletTokenPrices[aId]?.usd
)
})
.map((token) => {
const geckoId = token.item.extensions.coingeckoId
return (
<div
className="cursor-pointer default-transition flex items-center justify-between px-4 py-2 hover:bg-th-bkg-4"
key={geckoId}
onClick={() =>
setFormValue((val) => ({
...val,
inputMint: new PublicKey(token?.item.address),
}))
}
>
<div className="flex items-center">
{token.item.logoURI ? (
<img
src={token.item.logoURI}
width="24"
height="24"
alt={token.item.symbol}
/>
) : null}
<div>
<div className="ml-2 text-th-fgd-1">
{token.item.symbol}
</div>
{walletTokenPrices ? (
<div className="ml-2 text-th-fgd-4 text-xs">
{walletTokenPrices[geckoId]
? `$${walletTokenPrices[geckoId].usd}`
: 'Unavailable'}
</div>
) : null}
</div>
</div>
<div>
<div className="text-right text-th-fgd-1">
{token.uiBalance.toLocaleString(undefined, {
maximumSignificantDigits: 6,
})}
</div>
<div className="text-th-fgd-4 text-right text-xs">
{walletTokenPrices[geckoId]
? `$${(
token.uiBalance * walletTokenPrices[geckoId].usd
).toLocaleString(undefined, {
maximumFractionDigits: 2,
})}`
: '?'}
</div>
</div>
</div>
)
})}
</aside>
<button
className="absolute bg-th-bkg-4 p-3 left-64 rounded-l-none text-th-fgd-1 hover:text-th-primary top-12"
onClick={() => setShowWalletDraw(!showWalletDraw)}
>
<WalletIcon className="h-5 w-5" />
</button>
</div>
) : null}
{/* {connected ? (
<div className="mt-8 bg-th-bkg-2 rounded-lg p-4 text-th-fgd-4 -mb-2">
Swaps occur in your connected wallet. After swapping, you may then
deposit swapped tokens into your mango account.
</div>
) : null}
) : null} */}
<div className="mt-8 bg-th-bkg-2 rounded-lg px-6 py-8">
<div className="flex justify-between">
<label htmlFor="inputMint" className="block text-sm font-semibold">
@ -642,9 +776,116 @@ const JupiterForm: FunctionComponent = () => {
>
{connected ? (swapping ? 'Swapping...' : 'Swap') : 'Connect Wallet'}
</Button>
{inputTokenStats?.prices?.length && outputTokenStats?.prices?.length ? (
<>
<div className="flex items-center justify-between mt-4">
<div className="flex items-center">
{inputTokenInfo?.logoURI ? (
<img
src={inputTokenInfo?.logoURI}
width="32"
height="32"
alt={inputTokenInfo?.symbol}
/>
) : null}
<div className="ml-2">
<div className="font-semibold">{inputTokenInfo?.symbol}</div>
<div className="text-th-fgd-4 text-xs">
{inputTokenInfo?.name}
</div>
</div>
</div>
<div className="flex items-center space-x-3">
<div className="flex flex-col items-end sm:flex-row sm:space-x-3">
<div>${inputTokenPrice}</div>
<div
className={`text-xs sm:text-sm ${
inputTokenChange <= 0 ? 'text-th-green' : 'text-th-red'
}`}
>
{(inputTokenChange * -1).toFixed(2)}%
</div>
</div>
<AreaChart
width={120}
height={40}
data={inputChartPrices || null}
>
<Area
isAnimationActive={false}
type="monotone"
dataKey="price"
stroke="#FF9C24"
fill="#FF9C24"
fillOpacity={0.1}
/>
<XAxis dataKey="time" hide />
<YAxis
dataKey="price"
type="number"
domain={['dataMin', 'dataMax']}
hide
/>
<Tooltip content={tooltipContent} position={{ x: 0, y: -50 }} />
</AreaChart>
</div>
</div>
<div className="flex items-center justify-between mt-4">
<div className="flex items-center">
{outputTokenInfo?.logoURI ? (
<img
src={outputTokenInfo?.logoURI}
width="32"
height="32"
alt={outputTokenInfo?.symbol}
/>
) : null}
<div className="ml-2">
<div className="font-semibold">{outputTokenInfo?.symbol}</div>
<div className="text-th-fgd-4 text-xs">
{outputTokenInfo?.name}
</div>
</div>
</div>
<div className="flex items-center space-x-3">
<div className="flex flex-col items-end sm:flex-row sm:space-x-3">
<div>${outputTokenPrice}</div>
<div
className={`${
outputTokenChange <= 0 ? 'text-th-green' : 'text-th-red'
}`}
>
{(outputTokenChange * -1).toFixed(2)}%
</div>
</div>
<AreaChart
width={120}
height={40}
data={outputChartPrices || null}
>
<Area
isAnimationActive={false}
type="monotone"
dataKey="price"
stroke="#FF9C24"
fill="#FF9C24"
fillOpacity={0.1}
/>
<XAxis dataKey="time" hide />
<YAxis
dataKey="price"
type="number"
domain={['dataMin', 'dataMax']}
hide
/>
<Tooltip content={tooltipContent} position={{ x: 0, y: -50 }} />
</AreaChart>
</div>
</div>
</>
) : null}
{selectedRoute ? (
<div className="border-b border-th-bkg-4 flex flex-col space-y-2.5 mt-6 pb-6 text-th-fgd-3 text-xs">
<div className="flex flex-col space-y-2.5 mt-6 text-th-fgd-3 text-xs">
<div className="flex justify-between">
<span>Rate</span>
<span className="text-th-fgd-1">
@ -718,110 +959,6 @@ const JupiterForm: FunctionComponent = () => {
) : null}
</div>
) : null}
{inputTokenStats?.prices?.length && outputTokenStats?.prices?.length ? (
<>
<div className="flex items-center justify-between mt-6">
<div className="flex items-center">
{inputTokenInfo?.logoURI ? (
<img
src={inputTokenInfo?.logoURI}
width="32"
height="32"
alt={inputTokenInfo?.symbol}
/>
) : null}
<div className="ml-2">
<div className="font-semibold">{inputTokenInfo?.symbol}</div>
<div className="text-th-fgd-4 text-xs">
{inputTokenInfo?.name}
</div>
</div>
</div>
<div className="flex items-center space-x-3">
<div className="">${inputTokenPrice}</div>
<div
className={`${
inputTokenChange <= 0 ? 'text-th-green' : 'text-th-red'
}`}
>
{(inputTokenChange * -1).toFixed(2)}%
</div>
<AreaChart
width={120}
height={40}
data={inputChartPrices || null}
>
<Area
isAnimationActive={false}
type="monotone"
dataKey="price"
stroke="#FF9C24"
fill="#FF9C24"
fillOpacity={0.1}
/>
<XAxis dataKey="time" hide />
<YAxis
dataKey="price"
type="number"
domain={['dataMin', 'dataMax']}
hide
/>
<Tooltip content={tooltipContent} position={{ x: 0, y: -50 }} />
</AreaChart>
</div>
</div>
<div className="flex items-center justify-between mt-4">
<div className="flex items-center">
{outputTokenInfo?.logoURI ? (
<img
src={outputTokenInfo?.logoURI}
width="32"
height="32"
alt={outputTokenInfo?.symbol}
/>
) : null}
<div className="ml-2">
<div className="font-semibold">{outputTokenInfo?.symbol}</div>
<div className="text-th-fgd-4 text-xs">
{outputTokenInfo?.name}
</div>
</div>
</div>
<div className="flex items-center space-x-3">
<div className="">${outputTokenPrice}</div>
<div
className={`${
outputTokenChange <= 0 ? 'text-th-green' : 'text-th-red'
}`}
>
{(outputTokenChange * -1).toFixed(2)}%
</div>
<AreaChart
width={120}
height={40}
data={outputChartPrices || null}
>
<Area
isAnimationActive={false}
type="monotone"
dataKey="price"
stroke="#FF9C24"
fill="#FF9C24"
fillOpacity={0.1}
/>
<XAxis dataKey="time" hide />
<YAxis
dataKey="price"
type="number"
domain={['dataMin', 'dataMax']}
hide
/>
<Tooltip content={tooltipContent} position={{ x: 0, y: -50 }} />
</AreaChart>
</div>
</div>
</>
) : null}
{showInputTokenSelect ? (
<SwapTokenSelect
isOpen={showInputTokenSelect}
@ -850,6 +987,23 @@ const JupiterForm: FunctionComponent = () => {
}}
/>
) : null}
{connected && !hasSwapped ? (
<Modal isOpen={!hasSwapped} onClose={() => setHasSwapped(true)}>
<ElementTitle>Before you get started...</ElementTitle>
<div className="flex flex-col justify-center">
<div className="text-center text-th-fgd-3">
Swaps interact directly with your connected wallet, not your Mango
Account.
</div>
<Button
className="mt-6 mx-auto"
onClick={() => setHasSwapped(true)}
>
Got It
</Button>
</div>
</Modal>
) : null}
</div>
)
}

View File

@ -31,7 +31,7 @@ export const WalletIcon = ({ className }) => {
width="20"
height="17"
viewBox="0 0 20 17"
fill="none"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
>
<path