add halving to price carousel
This commit is contained in:
parent
a0f3e338a8
commit
058f88da49
|
@ -2,13 +2,17 @@ import { createContext, useContext, useMemo } from 'react'
|
||||||
import { useQuery } from '@apollo/client'
|
import { useQuery } from '@apollo/client'
|
||||||
import { NORMAL_POLL_INTERVAL, SSR } from '@/lib/constants'
|
import { NORMAL_POLL_INTERVAL, SSR } from '@/lib/constants'
|
||||||
import { BLOCK_HEIGHT } from '@/fragments/blockHeight'
|
import { BLOCK_HEIGHT } from '@/fragments/blockHeight'
|
||||||
|
import { datePivot } from '@/lib/time'
|
||||||
|
|
||||||
export const BlockHeightContext = createContext({
|
export const BlockHeightContext = createContext({
|
||||||
height: 0
|
height: 0,
|
||||||
|
halving: null
|
||||||
})
|
})
|
||||||
|
|
||||||
export const useBlockHeight = () => useContext(BlockHeightContext)
|
export const useBlockHeight = () => useContext(BlockHeightContext)
|
||||||
|
|
||||||
|
const HALVING_INTERVAL = 210000
|
||||||
|
|
||||||
export const BlockHeightProvider = ({ blockHeight, children }) => {
|
export const BlockHeightProvider = ({ blockHeight, children }) => {
|
||||||
const { data } = useQuery(BLOCK_HEIGHT, {
|
const { data } = useQuery(BLOCK_HEIGHT, {
|
||||||
...(SSR
|
...(SSR
|
||||||
|
@ -18,9 +22,23 @@ export const BlockHeightProvider = ({ blockHeight, children }) => {
|
||||||
nextFetchPolicy: 'cache-and-network'
|
nextFetchPolicy: 'cache-and-network'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
const value = useMemo(() => ({
|
const value = useMemo(() => {
|
||||||
height: data?.blockHeight ?? blockHeight ?? 0
|
if (!data?.blockHeight) {
|
||||||
}), [data?.blockHeight, blockHeight])
|
return {
|
||||||
|
height: blockHeight ?? 0,
|
||||||
|
halving: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const remainingBlocks = HALVING_INTERVAL - (data.blockHeight % HALVING_INTERVAL)
|
||||||
|
const minutesUntilHalving = remainingBlocks * 10
|
||||||
|
const halving = datePivot(new Date(), { minutes: minutesUntilHalving })
|
||||||
|
|
||||||
|
return {
|
||||||
|
height: data.blockHeight,
|
||||||
|
halving
|
||||||
|
}
|
||||||
|
}, [data?.blockHeight, blockHeight])
|
||||||
return (
|
return (
|
||||||
<BlockHeightContext.Provider value={value}>
|
<BlockHeightContext.Provider value={value}>
|
||||||
{children}
|
{children}
|
||||||
|
|
|
@ -1,18 +1,53 @@
|
||||||
import Countdown from 'react-countdown'
|
import Countdown from 'react-countdown'
|
||||||
|
|
||||||
export default function SimpleCountdown ({ className, onComplete, date }) {
|
export default function SimpleCountdown (props) {
|
||||||
return (
|
return (
|
||||||
<span className={className}>
|
<CountdownShared
|
||||||
<Countdown
|
{...props} formatter={props => {
|
||||||
date={date}
|
return (
|
||||||
renderer={props => <span className='text-monospace' suppressHydrationWarning> {props.formatted.minutes}:{props.formatted.seconds}</span>}
|
<>
|
||||||
onComplete={onComplete}
|
{props.formatted.minutes}:{props.formatted.seconds}
|
||||||
/>
|
</>
|
||||||
</span>
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function LongCountdown ({ className, onComplete, date }) {
|
export function LongCountdown (props) {
|
||||||
|
return (
|
||||||
|
<CountdownShared
|
||||||
|
{...props} formatter={props => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{props.formatted.days && `${props.formatted.days} days `}
|
||||||
|
{props.formatted.hours && `${props.formatted.hours} hours `}
|
||||||
|
{props.formatted.minutes && `${props.formatted.minutes} minutes `}
|
||||||
|
{props.formatted.seconds && `${props.formatted.seconds} seconds `}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function CompactLongCountdown (props) {
|
||||||
|
return (
|
||||||
|
<CountdownShared
|
||||||
|
{...props} formatter={props => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{props.formatted.days
|
||||||
|
? ` ${props.formatted.days}d ${props.formatted.hours}h ${props.formatted.minutes}m ${props.formatted.seconds}s`
|
||||||
|
: ` ${props.formatted.hours}:${props.formatted.minutes}:${props.formatted.seconds}`}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function CountdownShared ({ className, onComplete, date, formatter }) {
|
||||||
return (
|
return (
|
||||||
<span className={className}>
|
<span className={className}>
|
||||||
<Countdown
|
<Countdown
|
||||||
|
@ -20,9 +55,7 @@ export function LongCountdown ({ className, onComplete, date }) {
|
||||||
renderer={props => {
|
renderer={props => {
|
||||||
return (
|
return (
|
||||||
<span suppressHydrationWarning>
|
<span suppressHydrationWarning>
|
||||||
{props.formatted.days && `${props.formatted.days} days `}
|
{formatter(props)}
|
||||||
{props.formatted.minutes && `${props.formatted.minutes} minutes `}
|
|
||||||
{props.formatted.seconds && `${props.formatted.seconds} seconds `}
|
|
||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { CURRENCY_SYMBOLS } from '@/lib/currency'
|
||||||
import { NORMAL_POLL_INTERVAL, SSR } from '@/lib/constants'
|
import { NORMAL_POLL_INTERVAL, SSR } from '@/lib/constants'
|
||||||
import { useBlockHeight } from './block-height'
|
import { useBlockHeight } from './block-height'
|
||||||
import { useChainFee } from './chain-fee'
|
import { useChainFee } from './chain-fee'
|
||||||
|
import { CompactLongCountdown } from './countdown'
|
||||||
|
|
||||||
export const PriceContext = React.createContext({
|
export const PriceContext = React.createContext({
|
||||||
price: null,
|
price: null,
|
||||||
|
@ -50,11 +51,9 @@ export default function Price ({ className }) {
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const { price, fiatSymbol } = usePrice()
|
const { price, fiatSymbol } = usePrice()
|
||||||
const { height: blockHeight } = useBlockHeight()
|
const { height: blockHeight, halving } = useBlockHeight()
|
||||||
const { fee: chainFee } = useChainFee()
|
const { fee: chainFee } = useChainFee()
|
||||||
|
|
||||||
if (!price || price < 0 || blockHeight <= 0 || chainFee <= 0) return null
|
|
||||||
|
|
||||||
// Options: yep, 1btc, blockHeight, undefined
|
// Options: yep, 1btc, blockHeight, undefined
|
||||||
// yep -> 1btc -> blockHeight -> chainFee -> undefined -> yep
|
// yep -> 1btc -> blockHeight -> chainFee -> undefined -> yep
|
||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
|
@ -68,6 +67,9 @@ export default function Price ({ className }) {
|
||||||
window.localStorage.setItem('asSats', 'chainFee')
|
window.localStorage.setItem('asSats', 'chainFee')
|
||||||
setAsSats('chainFee')
|
setAsSats('chainFee')
|
||||||
} else if (asSats === 'chainFee') {
|
} else if (asSats === 'chainFee') {
|
||||||
|
window.localStorage.setItem('asSats', 'halving')
|
||||||
|
setAsSats('halving')
|
||||||
|
} else if (asSats === 'halving') {
|
||||||
window.localStorage.removeItem('asSats')
|
window.localStorage.removeItem('asSats')
|
||||||
setAsSats('fiat')
|
setAsSats('fiat')
|
||||||
} else {
|
} else {
|
||||||
|
@ -79,6 +81,7 @@ export default function Price ({ className }) {
|
||||||
const compClassName = (className || '') + ' text-reset pointer'
|
const compClassName = (className || '') + ' text-reset pointer'
|
||||||
|
|
||||||
if (asSats === 'yep') {
|
if (asSats === 'yep') {
|
||||||
|
if (!price || price < 0) return null
|
||||||
return (
|
return (
|
||||||
<div className={compClassName} onClick={handleClick} variant='link'>
|
<div className={compClassName} onClick={handleClick} variant='link'>
|
||||||
{fixedDecimal(100000000 / price, 0) + ` sats/${fiatSymbol}`}
|
{fixedDecimal(100000000 / price, 0) + ` sats/${fiatSymbol}`}
|
||||||
|
@ -95,6 +98,7 @@ export default function Price ({ className }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (asSats === 'blockHeight') {
|
if (asSats === 'blockHeight') {
|
||||||
|
if (blockHeight <= 0) return null
|
||||||
return (
|
return (
|
||||||
<div className={compClassName} onClick={handleClick} variant='link'>
|
<div className={compClassName} onClick={handleClick} variant='link'>
|
||||||
{blockHeight}
|
{blockHeight}
|
||||||
|
@ -102,7 +106,17 @@ export default function Price ({ className }) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (asSats === 'halving') {
|
||||||
|
if (!halving) return null
|
||||||
|
return (
|
||||||
|
<div className={compClassName} onClick={handleClick} variant='link'>
|
||||||
|
<CompactLongCountdown date={halving} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if (asSats === 'chainFee') {
|
if (asSats === 'chainFee') {
|
||||||
|
if (chainFee <= 0) return null
|
||||||
return (
|
return (
|
||||||
<div className={compClassName} onClick={handleClick} variant='link'>
|
<div className={compClassName} onClick={handleClick} variant='link'>
|
||||||
{chainFee} sat/vB
|
{chainFee} sat/vB
|
||||||
|
@ -111,6 +125,7 @@ export default function Price ({ className }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (asSats === 'fiat') {
|
if (asSats === 'fiat') {
|
||||||
|
if (!price || price < 0) return null
|
||||||
return (
|
return (
|
||||||
<div className={compClassName} onClick={handleClick} variant='link'>
|
<div className={compClassName} onClick={handleClick} variant='link'>
|
||||||
{fiatSymbol + fixedDecimal(price, 0)}
|
{fiatSymbol + fixedDecimal(price, 0)}
|
||||||
|
|
|
@ -7,7 +7,6 @@ import Layout from '@/components/layout'
|
||||||
import { useMutation, useQuery } from '@apollo/client'
|
import { useMutation, useQuery } from '@apollo/client'
|
||||||
import Link from 'next/link'
|
import Link from 'next/link'
|
||||||
import { amountSchema } from '@/lib/validate'
|
import { amountSchema } from '@/lib/validate'
|
||||||
import Countdown from 'react-countdown'
|
|
||||||
import { numWithUnits } from '@/lib/format'
|
import { numWithUnits } from '@/lib/format'
|
||||||
import PageLoading from '@/components/page-loading'
|
import PageLoading from '@/components/page-loading'
|
||||||
import { useShowModal } from '@/components/modal'
|
import { useShowModal } from '@/components/modal'
|
||||||
|
@ -21,6 +20,7 @@ import { proportions } from '@/lib/madness'
|
||||||
import { useData } from '@/components/use-data'
|
import { useData } from '@/components/use-data'
|
||||||
import { GrowthPieChartSkeleton } from '@/components/charts-skeletons'
|
import { GrowthPieChartSkeleton } from '@/components/charts-skeletons'
|
||||||
import { useMemo } from 'react'
|
import { useMemo } from 'react'
|
||||||
|
import { CompactLongCountdown } from '@/components/countdown'
|
||||||
|
|
||||||
const GrowthPieChart = dynamic(() => import('@/components/charts').then(mod => mod.GrowthPieChart), {
|
const GrowthPieChart = dynamic(() => import('@/components/charts').then(mod => mod.GrowthPieChart), {
|
||||||
loading: () => <GrowthPieChartSkeleton />
|
loading: () => <GrowthPieChartSkeleton />
|
||||||
|
@ -77,15 +77,12 @@ export function RewardLine ({ total, time }) {
|
||||||
{numWithUnits(total)} in rewards
|
{numWithUnits(total)} in rewards
|
||||||
</span>
|
</span>
|
||||||
{time &&
|
{time &&
|
||||||
<Countdown
|
<small style={{ whiteSpace: 'nowrap' }}>
|
||||||
date={time}
|
<CompactLongCountdown
|
||||||
renderer={props =>
|
className='text-monospace'
|
||||||
<small className='text-monospace' suppressHydrationWarning style={{ whiteSpace: 'nowrap' }}>
|
date={time}
|
||||||
{props.formatted.days
|
/>
|
||||||
? ` ${props.formatted.days}d ${props.formatted.hours}h ${props.formatted.minutes}m ${props.formatted.seconds}s`
|
</small>}
|
||||||
: ` ${props.formatted.hours}:${props.formatted.minutes}:${props.formatted.seconds}`}
|
|
||||||
</small>}
|
|
||||||
/>}
|
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue