rebuild vesting timeline
This commit is contained in:
@@ -22,6 +22,7 @@
|
||||
"@types/react-dom": "^17.0.9",
|
||||
"bs58": "^4.0.1",
|
||||
"clsx": "^1.1.1",
|
||||
"date-fns": "^2.28.0",
|
||||
"notistack": "^2.0.3",
|
||||
"qrcode.react": "^1.0.1",
|
||||
"react": "^17.0.2",
|
||||
|
||||
@@ -8,7 +8,9 @@ import {
|
||||
getSpendableCoins,
|
||||
getOriginalVesting,
|
||||
getCurrentVestingPeriod,
|
||||
getVestingAccountInfo,
|
||||
} from '../requests'
|
||||
import { VestingAccountInfo } from 'src/types/rust/vestingaccountinfo'
|
||||
|
||||
type TTokenAllocation = {
|
||||
[key in 'vesting' | 'vested' | 'locked' | 'spendable']: Coin['amount']
|
||||
@@ -20,6 +22,7 @@ export type TUseuserBalance = {
|
||||
tokenAllocation?: TTokenAllocation
|
||||
originalVesting?: OriginalVestingResponse
|
||||
currentVestingPeriod?: Period
|
||||
vestingAccountInfo?: VestingAccountInfo
|
||||
isLoading: boolean
|
||||
fetchBalance: () => void
|
||||
clearBalance: () => void
|
||||
@@ -33,6 +36,7 @@ export const useGetBalance = (address?: string): TUseuserBalance => {
|
||||
const [tokenAllocation, setTokenAllocation] = useState<TTokenAllocation>()
|
||||
const [originalVesting, setOriginalVesting] = useState<OriginalVestingResponse>()
|
||||
const [currentVestingPeriod, setCurrentVestingPeriod] = useState<Period>()
|
||||
const [vestingAccountInfo, setVestingAccountInfo] = useState<VestingAccountInfo>()
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
|
||||
const fetchBalance = useCallback(async () => {
|
||||
@@ -54,15 +58,23 @@ export const useGetBalance = (address?: string): TUseuserBalance => {
|
||||
setIsLoading(true)
|
||||
if (address) {
|
||||
try {
|
||||
const [originalVestingValue, vestingCoins, vestedCoins, lockedCoins, spendableCoins, currentVestingPeriod] =
|
||||
await Promise.all([
|
||||
getOriginalVesting(address),
|
||||
getVestingCoins(address),
|
||||
getVestedCoins(address),
|
||||
getLockedCoins(address),
|
||||
getSpendableCoins(address),
|
||||
getCurrentVestingPeriod(address),
|
||||
])
|
||||
const [
|
||||
originalVestingValue,
|
||||
vestingCoins,
|
||||
vestedCoins,
|
||||
lockedCoins,
|
||||
spendableCoins,
|
||||
currentVestingPeriod,
|
||||
vestingAccountInfo,
|
||||
] = await Promise.all([
|
||||
getOriginalVesting(address),
|
||||
getVestingCoins(address),
|
||||
getVestedCoins(address),
|
||||
getLockedCoins(address),
|
||||
getSpendableCoins(address),
|
||||
getCurrentVestingPeriod(address),
|
||||
getVestingAccountInfo(address),
|
||||
])
|
||||
setOriginalVesting(originalVestingValue)
|
||||
setCurrentVestingPeriod(currentVestingPeriod)
|
||||
setTokenAllocation({
|
||||
@@ -71,6 +83,7 @@ export const useGetBalance = (address?: string): TUseuserBalance => {
|
||||
locked: lockedCoins.amount,
|
||||
spendable: spendableCoins.amount,
|
||||
})
|
||||
setVestingAccountInfo(vestingAccountInfo)
|
||||
} catch (e) {
|
||||
clearTokenAllocation()
|
||||
clearOriginalVesting()
|
||||
@@ -110,6 +123,7 @@ export const useGetBalance = (address?: string): TUseuserBalance => {
|
||||
tokenAllocation,
|
||||
originalVesting,
|
||||
currentVestingPeriod,
|
||||
vestingAccountInfo,
|
||||
fetchBalance,
|
||||
clearBalance,
|
||||
clearAll,
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
import React, { useContext } from 'react'
|
||||
import { Box, Tooltip, Typography } from '@mui/material'
|
||||
import { format } from 'date-fns'
|
||||
import { ClientContext } from '../../../context/main'
|
||||
|
||||
const calculateNubPosition = (arrLength: number, index: number) => (1 / arrLength) * 100 * (index + 1)
|
||||
|
||||
export const VestingTimeline: React.FC<{ percentageComplete: number }> = ({ percentageComplete }) => {
|
||||
const {
|
||||
userBalance: { currentVestingPeriod, vestingAccountInfo },
|
||||
} = useContext(ClientContext)
|
||||
|
||||
const nextPeriod =
|
||||
typeof currentVestingPeriod === 'object' && !!vestingAccountInfo?.periods
|
||||
? Number(vestingAccountInfo?.periods[currentVestingPeriod.In]?.start_time)
|
||||
: undefined
|
||||
|
||||
return (
|
||||
<Box display="flex" flexDirection="column" gap={1} position="relative" width="100%">
|
||||
<svg width="100%" height="12">
|
||||
<rect y="2" width="100%" height="6" rx="0" fill="#E6E6E6" />
|
||||
<rect y="2" width={`${percentageComplete}%`} height="6" rx="0" fill="#121726" />
|
||||
<rect width="4" height="12" rx="1" fill="#121726" />
|
||||
{vestingAccountInfo?.periods.map((period, i, arr) => (
|
||||
<Tooltip title={format(new Date(Number(period.start_time) * 1000), 'HH:mm do MMM yyyy')} key={i}>
|
||||
<rect
|
||||
x={`${calculateNubPosition(arr.length, i)}%`}
|
||||
width="4"
|
||||
height="12"
|
||||
rx="1"
|
||||
fill={+percentageComplete.toFixed(2) >= calculateNubPosition(arr.length, i) ? '#121726' : '#B9B9B9'}
|
||||
style={{ cursor: 'pointer' }}
|
||||
/>
|
||||
</Tooltip>
|
||||
))}
|
||||
</svg>
|
||||
{nextPeriod && (
|
||||
<Typography variant="caption" sx={{ color: 'grey.500', position: 'absolute', top: 15, left: 0 }}>
|
||||
Next vesting period: {format(new Date(nextPeriod * 1000), 'HH:mm do MMM yyyy')}
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import { NymCard, InfoTooltip, Title, Fee } from '../../components'
|
||||
import { ClientContext } from '../../context/main'
|
||||
import { withdrawVestedCoins } from '../../requests'
|
||||
import { Period } from '../../types'
|
||||
import { VestingTimeline } from './components/vesting-timeline'
|
||||
|
||||
export const VestingCard = () => {
|
||||
const { userBalance } = useContext(ClientContext)
|
||||
@@ -55,7 +56,7 @@ export const VestingCard = () => {
|
||||
<VestingSchedule />
|
||||
<TokenTransfer />
|
||||
<Box display="flex" justifyContent="space-between" alignItems="center">
|
||||
<Fee feeType="Send" />
|
||||
{userBalance.tokenAllocation?.spendable !== '0' ? <Fee feeType="Send" /> : <div />}
|
||||
<Button
|
||||
size="large"
|
||||
variant="contained"
|
||||
@@ -79,7 +80,7 @@ export const VestingCard = () => {
|
||||
}
|
||||
}}
|
||||
endIcon={isLoading && <CircularProgress size={16} color="inherit" />}
|
||||
disabled={isLoading}
|
||||
disabled={isLoading || userBalance.tokenAllocation?.spendable === '0'}
|
||||
disableElevation
|
||||
>
|
||||
Transfer
|
||||
@@ -103,8 +104,9 @@ const VestingSchedule = () => {
|
||||
const calculatePercentage = () => {
|
||||
const { tokenAllocation, originalVesting } = userBalance
|
||||
if (tokenAllocation?.vesting && tokenAllocation.vested && tokenAllocation.vested !== '0' && originalVesting) {
|
||||
const percentage = Math.round((+tokenAllocation.vested / +originalVesting?.amount.amount) * 100)
|
||||
setVestedPercentage(percentage)
|
||||
const percentage = (+tokenAllocation.vested / +originalVesting?.amount.amount) * 100
|
||||
const rounded = percentage.toFixed(2)
|
||||
setVestedPercentage(+rounded)
|
||||
} else {
|
||||
setVestedPercentage(0)
|
||||
}
|
||||
@@ -135,13 +137,8 @@ const VestingSchedule = () => {
|
||||
</TableCell>
|
||||
<TableCell sx={{ borderBottom: 'none' }}>
|
||||
<Box display="flex" alignItems="center" gap={1}>
|
||||
<Typography variant="caption">{`${vestedPercentage}%`}</Typography>
|
||||
<LinearProgress
|
||||
sx={{ flexBasis: '99%' }}
|
||||
variant="determinate"
|
||||
value={vestedPercentage}
|
||||
color="inherit"
|
||||
/>
|
||||
<Typography variant="body2">{`${vestedPercentage}%`}</Typography>
|
||||
<VestingTimeline percentageComplete={vestedPercentage} />
|
||||
</Box>
|
||||
</TableCell>
|
||||
<TableCell sx={{ borderBottom: 'none' }} align="right">
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { invoke } from '@tauri-apps/api'
|
||||
import { VestingAccountInfo } from 'src/types/rust/vestingaccountinfo'
|
||||
import { majorToMinor, minorToMajor } from '.'
|
||||
import { Coin, OriginalVestingResponse, Period } from '../types'
|
||||
import { Coin, DelegationResult, OriginalVestingResponse, Period } from '../types'
|
||||
|
||||
export const getLockedCoins = async (address: string): Promise<Coin> => {
|
||||
const res: Coin = await invoke('locked_coins', { address })
|
||||
@@ -23,8 +24,8 @@ export const getVestedCoins = async (vestingAccountAddress: string): Promise<Coi
|
||||
|
||||
export const getOriginalVesting = async (vestingAccountAddress: string): Promise<OriginalVestingResponse> => {
|
||||
const res: OriginalVestingResponse = await invoke('original_vesting', { vestingAccountAddress })
|
||||
const majorValue = await minorToMajor(res.amount.amount)
|
||||
return {...res, amount: majorValue}
|
||||
const majorValue = await minorToMajor(res.amount.amount)
|
||||
return { ...res, amount: majorValue }
|
||||
}
|
||||
|
||||
export const withdrawVestedCoins = async (amount: string) => {
|
||||
@@ -32,4 +33,19 @@ export const withdrawVestedCoins = async (amount: string) => {
|
||||
await invoke('withdraw_vested_coins', { amount: { amount: minor.amount, denom: 'Minor' } })
|
||||
}
|
||||
|
||||
export const getCurrentVestingPeriod = async (address: string): Promise<Period> => await invoke('get_current_vesting_period', {address})
|
||||
export const getCurrentVestingPeriod = async (address: string): Promise<Period> =>
|
||||
await invoke('get_current_vesting_period', { address })
|
||||
|
||||
export const vestingDelegateToMixnode = async ({
|
||||
identity,
|
||||
amount,
|
||||
}: {
|
||||
identity: string
|
||||
amount: Coin
|
||||
}): Promise<DelegationResult> => await invoke('vesting_delegate_to_mixnode', { identity, amount })
|
||||
|
||||
export const vestingUnelegateFromMixnode = async (identity: string): Promise<DelegationResult> =>
|
||||
await invoke('vesting_delegate_to_mixnode', { identity })
|
||||
|
||||
export const getVestingAccountInfo = async (address: string): Promise<VestingAccountInfo> =>
|
||||
await invoke('get_account_info', { address })
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
import React, { useContext } from 'react'
|
||||
import { ClientContext } from '../context/main'
|
||||
|
||||
export const VestingTimeline: React.FC = () => {
|
||||
const { userBalance } = useContext(ClientContext)
|
||||
|
||||
const arr = new Array(userBalance.originalVesting?.number_of_periods).fill(undefined)
|
||||
return (
|
||||
<svg width="250" height="20" viewBox="0 0 250 8">
|
||||
<rect y="2" width="242px" height="8" rx="0" fill="#E6E6E6" />
|
||||
<rect y="2" width={25} height="8" rx="0" fill="#121726" />
|
||||
<rect width="4" height="12" rx="1" fill="#121726" />
|
||||
{arr.map((e, i) => (
|
||||
<rect x={(i + 1) * 30} width="4" height="12" rx="1" fill="#B9B9B9" />
|
||||
))}
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
@@ -18,3 +18,5 @@ export * from './mixnodestatusresponse'
|
||||
export * from './inclusionprobabilityresponse'
|
||||
export * from './network'
|
||||
export * from './originalvestingresponse'
|
||||
export * from './vestingperiod'
|
||||
export * from './vestingaccountinfo'
|
||||
|
||||
@@ -3116,6 +3116,11 @@ dashdash@^1.12.0:
|
||||
dependencies:
|
||||
assert-plus "^1.0.0"
|
||||
|
||||
date-fns@^2.28.0:
|
||||
version "2.28.0"
|
||||
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.28.0.tgz#9570d656f5fc13143e50c975a3b6bbeb46cd08b2"
|
||||
integrity sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw==
|
||||
|
||||
debug@2.6.9:
|
||||
version "2.6.9"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
|
||||
|
||||
Reference in New Issue
Block a user