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

Feat: dynamic states for boost breakdown and boost meter #85

Merged
merged 3 commits into from
Mar 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions public/images/empty-breakdown.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 0 additions & 4 deletions src/components/ClaimCard/index.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import { ShieldOutlined } from '@mui/icons-material'
import { Grid, Paper, Typography, Tooltip, Badge } from '@mui/material'
import { useTheme } from '@mui/material/styles'
import { formatEther } from 'ethers/lib/utils'
import InfoOutlined from '@mui/icons-material/InfoOutlined'
import type { ReactElement } from 'react'

import SingleGreenTile from '@/public/images/single-green-tile.svg'
import DoubleGreenTile from '@/public/images/double-green-tile.svg'
import { formatAmount } from '@/utils/formatters'
import { Odometer } from '@/components/Odometer'

Expand All @@ -27,7 +24,6 @@ export const ClaimCard = ({
}): ReactElement => {
const ecosystemAmountInEth = formatEther(ecosystemAmount)
const totalAmountInEth = formatEther(totalAmount)
const numericalTotalAmountInEth = Number(totalAmountInEth)

const isClaimable = variant === 'claimable'

Expand Down
62 changes: 41 additions & 21 deletions src/components/TokenLocking/BoostBreakdown.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { floorNumber } from '@/utils/boost'
import { SignalCellularAlt, SignalCellularAlt1Bar, SignalCellularAlt2Bar } from '@mui/icons-material'
import { Stack, Typography, Box } from '@mui/material'
import { Stack, Typography, Box, SvgIcon } from '@mui/material'
import BoostCounter from '../BoostCounter'
import { BoostMeter } from './BoostMeter'

import EmptyBreakdown from '@/public/images/empty-breakdown.svg'

import css from './styles.module.css'

const BoostStrengthSignal = ({ boost, color }: { boost: number; color: 'primary' | 'warning' | undefined }) => {
Expand Down Expand Up @@ -35,47 +37,65 @@ export const BoostBreakdown = ({
realizedBoost,
currentFinalBoost,
newFinalBoost,
boostPrediction,
isLock,
}: {
realizedBoost: number
currentFinalBoost: number
newFinalBoost: number
boostPrediction?: number
isLock: boolean
}) => {
const isVisibleDifference = Math.abs(floorNumber(currentFinalBoost, 2) - floorNumber(newFinalBoost, 2))
const isVisibleDifference = Math.abs(floorNumber(currentFinalBoost, 2) - floorNumber(newFinalBoost, 2)) > 0

// If everything is 1.0 there are no relevant locks / no amount is put in
const isInitialState = realizedBoost === 1 && currentFinalBoost === 1 && newFinalBoost === 1

return (
<Stack gap={2} height="100%">
<Box className={`${css.boostInfoBox} ${css.bordered}`} p={3} gap={4} height="100%" display="flex">
<Stack direction="row" justifyContent="space-between" width="100%" height="100%" alignItems="start">
<Stack direction="row" justifyContent="space-between" width="100%" alignItems="start">
<span>
<SignalCellularAlt color="border" fontSize="large" />
<BoostStrengthSignal
boost={newFinalBoost}
color={isVisibleDifference ? (isLock ? 'primary' : 'warning') : undefined}
/>
{!isInitialState && (
<>
<SignalCellularAlt color="border" fontSize="large" />
<BoostStrengthSignal
boost={newFinalBoost}
color={isVisibleDifference ? (isLock ? 'primary' : 'warning') : undefined}
/>
</>
)}
</span>
<Typography variant="body2" color="text.secondary">
Realized boost {floorNumber(realizedBoost, 2)}x
</Typography>
</Stack>

<Stack direction="column" width="100%" alignItems="start" mt={8} spacing={1}>
<BoostCounter
value={newFinalBoost}
variant="h2"
fontWeight={700}
color={isVisibleDifference ? (isLock ? 'primary' : 'warning.main') : undefined}
direction={isVisibleDifference ? (isLock ? 'north' : 'south') : undefined}
/>
<Typography variant="body2" color="text.secondary">
Expected final point boost
</Typography>
</Stack>
{isInitialState ? (
<Stack mt={4} spacing={4}>
<EmptyBreakdown />
<Typography variant="body2" color="text.secondary">
Start locking tokens to build your miles boost.
</Typography>
</Stack>
) : (
<Stack direction="column" width="100%" alignItems="start" mt="auto" spacing={1}>
<BoostCounter
value={newFinalBoost}
variant="h2"
fontWeight={700}
color={isVisibleDifference ? (isLock ? 'primary' : 'warning.main') : undefined}
direction={isVisibleDifference ? (isLock ? 'north' : 'south') : undefined}
/>
<Typography variant="body2" color="text.secondary">
Expected final point boost
</Typography>
</Stack>
)}
</Box>
<Box className={`${css.boostInfoBox} ${css.bordered}`} p={2} gap={4} display="flex" width="100%">
<Box height="88px">
<BoostMeter />
<BoostMeter isLock={isLock} isVisibleDifference={isVisibleDifference} prediction={boostPrediction} />
</Box>
</Box>
</Stack>
Expand Down
56 changes: 49 additions & 7 deletions src/components/TokenLocking/BoostMeter.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,59 @@
import { CHAIN_START_TIMESTAMPS } from '@/config/constants'
import { useChainId } from '@/hooks/useChainId'
import { floorNumber } from '@/utils/boost'
import { getCurrentDays } from '@/utils/date'
import { AccessTime } from '@mui/icons-material'
import { Box, LinearProgress, Stack, Typography } from '@mui/material'
import { useMemo } from 'react'
import { Box, LinearProgress, Stack, Tooltip, Typography } from '@mui/material'
import { ReactElement, useMemo } from 'react'
import { SEASON2_START } from './BoostGraph/graphConstants'

export const BoostMeter = () => {
import { useTheme } from '@mui/material/styles'

export const BoostMeter = ({
isLock,
isVisibleDifference,
prediction,
}: {
isLock: boolean
isVisibleDifference: boolean
prediction?: number
}) => {
const chainId = useChainId()
const startTime = CHAIN_START_TIMESTAMPS[chainId]
const now = useMemo(() => getCurrentDays(startTime), [startTime])

const value = ((SEASON2_START - now) / SEASON2_START) * 100

let boostMeterInfo: ReactElement | null = null

const theme = useTheme()

if (isLock) {
if (isVisibleDifference && prediction) {
boostMeterInfo = (
<Typography>
If you lock in 10 days your boost will only be{' '}
<b style={{ color: theme.palette.warning.main }}>{floorNumber(prediction, 2)}x</b>
</Typography>
)
} else {
boostMeterInfo =
value > 25 ? (
<Typography>The earlier you lock the higher the boost.</Typography>
) : (
<Typography>Your last chance to get the early boost.</Typography>
)
}
} else {
boostMeterInfo = <Typography>You will stop accruing boost for unlocked SAFE.</Typography>
}

return (
<Stack direction="row" spacing={2} height="100%" alignItems="flex-end">
<LinearProgress
variant="determinate"
value={value}
color={value > 50 ? 'primary' : 'warning'}
sx={{
height: '100%',
border: ({ palette }) => `4px solid ${palette.border.light}`,
Expand All @@ -35,13 +71,19 @@ export const BoostMeter = () => {
}}
/>
<Box>
<AccessTime fontSize="small" />
<Tooltip
title={
value < 25
? 'Receive a bonus for locking early. Realise your last chance to get an early boost.'
: 'Receive a bonus for locking early. The boost meter is going down over time.'
}
>
<AccessTime fontSize="small" />
</Tooltip>
<Typography variant="subtitle2" fontWeight={700}>
Early boost meter
</Typography>
<Typography variant="body2" color="text.secondary">
The boost decreases over time.
</Typography>
{boostMeterInfo}
</Box>
</Stack>
)
Expand Down
6 changes: 6 additions & 0 deletions src/components/TokenLocking/LockTokenWidget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ export const LockTokenWidget = ({ safeBalance }: { safeBalance: BigNumberish | u
[cleanedAmount, relativeLockHistory, todayInDays],
)

const boostIn10DaysFunction = useMemo(
() => getBoostFunction(todayInDays + 10, Number(cleanedAmount), relativeLockHistory),
[cleanedAmount, relativeLockHistory, todayInDays],
)

const validateAmount = useCallback(
(newAmount: string) => {
const parsed = parseUnits(newAmount, 18)
Expand Down Expand Up @@ -173,6 +178,7 @@ export const LockTokenWidget = ({ safeBalance }: { safeBalance: BigNumberish | u
currentFinalBoost={currentBoostFunction({ x: SEASON2_START })}
newFinalBoost={newBoostFunction({ x: SEASON2_START })}
isLock
boostPrediction={boostIn10DaysFunction({ x: SEASON2_START })}
/>
</Grid>
</Grid>
Expand Down
26 changes: 13 additions & 13 deletions src/components/TokenLocking/styles.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
flex-direction: column;
gap: 16px;
align-items: center;
justify-content: center;
}

.bordered {
Expand Down Expand Up @@ -88,7 +87,7 @@
}

.gradientText {
background: linear-gradient(225deg, #5FDDFF 12.5%, #12FF80 88.07%);
background: linear-gradient(225deg, #5fddff 12.5%, #12ff80 88.07%);
background-clip: text;
color: transparent;
}
Expand Down Expand Up @@ -122,7 +121,7 @@
}

.step:after {
content: "";
content: '';
left: 12px;
top: 36px;
position: absolute;
Expand Down Expand Up @@ -161,8 +160,8 @@
}

.rewardsBackground:before {
content: "";
background: radial-gradient(41.34% 41.34% at 50% 50%, #29B6F6 0%, transparent 82.5%);
content: '';
background: radial-gradient(41.34% 41.34% at 50% 50%, #29b6f6 0%, transparent 82.5%);
position: absolute;
width: 500px;
height: 500px;
Expand All @@ -172,8 +171,8 @@
}

.rewardsBackground:after {
content: "";
background: radial-gradient(41.34% 41.34% at 50% 50%, rgba(18, 255, 128, 0.60) 0%, rgba(18, 255, 128, 0.00) 82.5%);
content: '';
background: radial-gradient(41.34% 41.34% at 50% 50%, rgba(18, 255, 128, 0.6) 0%, rgba(18, 255, 128, 0) 82.5%);
position: absolute;
width: 700px;
height: 700px;
Expand All @@ -183,7 +182,7 @@
}

.rewards:after {
content: "";
content: '';
left: 12px;
top: -4px;
position: absolute;
Expand All @@ -207,7 +206,8 @@
overflow: hidden;
}

.leftReceipt, .rightReceipt {
.leftReceipt,
.rightReceipt {
background-color: #121312;
border-radius: 20px;
padding: 40px;
Expand All @@ -219,7 +219,7 @@
}

.leftReceipt:after {
content: "";
content: '';
border-right: 1px dashed white;
position: absolute;
right: 0;
Expand Down Expand Up @@ -252,10 +252,10 @@
z-index: 1;
top: -200px;
left: -100px;
background: radial-gradient(41.34% 41.34% at 50% 50%, rgba(18, 255, 128, 0.60) 0%, rgba(18, 255, 128, 0.00) 82.5%);
background: radial-gradient(41.34% 41.34% at 50% 50%, rgba(18, 255, 128, 0.6) 0%, rgba(18, 255, 128, 0) 82.5%);
pointer-events: none;
}

.unlockGradient {
background: radial-gradient(41.34% 41.34% at 50% 50%, #FF8061 0%, rgba(255, 128, 97, 0.00) 82.5%);
}
background: radial-gradient(41.34% 41.34% at 50% 50%, #ff8061 0%, rgba(255, 128, 97, 0) 82.5%);
}
Loading