rebuild vesting timeline

This commit is contained in:
fmtabbara
2022-03-02 00:21:52 +00:00
parent a732a676e0
commit 3d2eaeeabb
8 changed files with 103 additions and 42 deletions
+1
View File
@@ -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",
+23 -9
View File
@@ -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>
)
}
+8 -11
View File
@@ -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">
+20 -4
View File
@@ -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>
)
}
+2
View File
@@ -18,3 +18,5 @@ export * from './mixnodestatusresponse'
export * from './inclusionprobabilityresponse'
export * from './network'
export * from './originalvestingresponse'
export * from './vestingperiod'
export * from './vestingaccountinfo'
+5
View File
@@ -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"