Skip to content

Commit 5b9a8d3

Browse files
committed
useFetchEpochProgress
- 600ms updates, avoid cached api/websocket updates/ avoid animation glitches on network/refreshRate changes BlockHeightStatsCard - avoid cache issues, blockHeight can only increase EpochProgressRing/EpochHeroPill - use plurals on rounds left
1 parent f4e0a45 commit 5b9a8d3

5 files changed

Lines changed: 114 additions & 76 deletions

File tree

src/components/ProgressRing/ProgressRing.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { memo, useMemo } from 'react';
12
import classNames from 'classnames';
23
import { WithClassnameType } from 'types';
34

@@ -11,7 +12,7 @@ export interface ProgressRingType extends WithClassnameType {
1112
children?: React.ReactNode;
1213
}
1314

14-
export const ProgressRing = ({
15+
const ProgressRingBase = ({
1516
progress = 0,
1617
size = 24,
1718
trackWidth = 3,
@@ -22,10 +23,8 @@ export const ProgressRing = ({
2223
className
2324
}: ProgressRingType) => {
2425
const center = size / 2;
25-
const radius =
26-
center - (trackWidth > indicatorWidth ? trackWidth : indicatorWidth);
27-
28-
const dashArray = 2 * Math.PI * radius;
26+
const radius = center - Math.max(trackWidth, indicatorWidth);
27+
const dashArray = useMemo(() => 2 * Math.PI * radius, [radius]);
2928
const dashOffset = dashArray * ((100 - progress) / 100);
3029

3130
const showLabel = size > 80 && children;
@@ -74,3 +73,5 @@ export const ProgressRing = ({
7473
</div>
7574
);
7675
};
76+
77+
export const ProgressRing = memo(ProgressRingBase);

src/hooks/fetch/useFetchEpochProgress.ts

Lines changed: 81 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useEffect, useMemo, useState } from 'react';
1+
import { useEffect, useMemo, useRef, useState } from 'react';
22
import BigNumber from 'bignumber.js';
33
import { useSelector } from 'react-redux';
44

@@ -21,34 +21,97 @@ export const useFetchEpochProgress = () => {
2121
const { epochPercentage, epochTimeRemaining } = stats;
2222
const { epoch, refreshRate, roundsPerEpoch, roundsPassed } = unprocessed;
2323

24-
const [oldTestnetId, setOldTestnetId] = useState(activeNetworkId);
25-
const [isNewState, setIsNewState] = useState<boolean>(true);
26-
const [hasCallMade, setHasCallMade] = useState<boolean>(false);
27-
const [epochRoundsLeft, setEpochRoundsLeft] = useState<number>(0);
24+
const hasCallMadeRef = useRef<boolean>(false);
2825

29-
const refreshInterval =
26+
const rawRefreshInterval =
3027
refreshRate || initialNetworkRefreshRate || REFRESH_RATE;
31-
const refreshIntervalSec = new BigNumber(refreshInterval).dividedBy(1000);
3228

33-
const stepInterval = getProgressStepInterval(refreshInterval);
34-
const stepProgressSec = stepInterval.dividedBy(1000);
29+
const [epochRoundsLeft, setEpochRoundsLeft] = useState<number>(0);
30+
const [effectiveRefreshInterval, setEffectiveRefreshInterval] =
31+
useState(rawRefreshInterval);
32+
33+
const refreshIntervalSec = useMemo(
34+
() => new BigNumber(effectiveRefreshInterval).dividedBy(1000),
35+
[effectiveRefreshInterval]
36+
);
37+
38+
const stepInterval = useMemo(
39+
() => getProgressStepInterval(effectiveRefreshInterval),
40+
[effectiveRefreshInterval]
41+
);
42+
43+
const stepProgressSec = useMemo(
44+
() => stepInterval.dividedBy(1000),
45+
[stepInterval]
46+
);
3547

3648
const [roundTimeProgress, setRoundTimeProgress] = useState(
3749
new BigNumber(stepProgressSec)
3850
);
3951

40-
const updateStats = () => {
41-
if (!refreshInterval) {
52+
const roundProgress = useMemo(
53+
() => roundTimeProgress.times(100).dividedBy(refreshIntervalSec),
54+
[roundTimeProgress, refreshIntervalSec]
55+
);
56+
57+
const roundsLeft = useMemo(() => {
58+
if (epochRoundsLeft) {
59+
return epochRoundsLeft;
60+
}
61+
62+
// add one in order to take into account the css animation and the api call sync on the first run
63+
return new BigNumber(roundsPerEpoch).minus(roundsPassed).plus(1).toNumber();
64+
}, [epochRoundsLeft, roundsPerEpoch, roundsPassed]);
65+
66+
useEffect(() => {
67+
if (!rawRefreshInterval) {
4268
return;
4369
}
44-
setIsNewState(oldTestnetId !== activeNetworkId);
45-
if (isNewState) {
46-
startRoundTime();
70+
setEffectiveRefreshInterval((prev) =>
71+
rawRefreshInterval < prev ? rawRefreshInterval : prev
72+
);
73+
}, [rawRefreshInterval]);
74+
75+
// Reset on network change
76+
useEffect(() => {
77+
setEffectiveRefreshInterval(rawRefreshInterval);
78+
setRoundTimeProgress(new BigNumber(stepProgressSec));
79+
hasCallMadeRef.current = false;
80+
setEpochRoundsLeft(0);
81+
}, [activeNetworkId]);
82+
83+
useEffect(() => {
84+
if (!effectiveRefreshInterval) {
85+
return;
86+
}
87+
88+
const intervalRoundTime = setInterval(() => {
89+
if (!document.hidden) {
90+
setRoundTimeProgress((prev) =>
91+
prev.isGreaterThanOrEqualTo(refreshIntervalSec)
92+
? new BigNumber(stepProgressSec)
93+
: prev.plus(stepProgressSec)
94+
);
95+
}
96+
}, stepInterval.toNumber());
97+
98+
return () => clearInterval(intervalRoundTime);
99+
}, [effectiveRefreshInterval]);
100+
101+
useEffect(() => {
102+
if (!effectiveRefreshInterval || !roundTimeProgress || !timestamp) {
103+
return;
47104
}
48105

49-
if (roundTimeProgress.isEqualTo(refreshIntervalSec) && !hasCallMade) {
106+
if (
107+
roundTimeProgress.isGreaterThanOrEqualTo(refreshIntervalSec) &&
108+
!hasCallMadeRef.current
109+
) {
110+
hasCallMadeRef.current = true;
111+
50112
fetchStats().then(({ success }) => {
51113
if (!success) {
114+
hasCallMadeRef.current = false;
52115
return;
53116
}
54117

@@ -59,7 +122,6 @@ export const useFetchEpochProgress = () => {
59122
return;
60123
}
61124

62-
setHasCallMade(true);
63125
setEpochRoundsLeft((existingRound) => {
64126
if (!existingRound) {
65127
return roundsLeft;
@@ -75,49 +137,10 @@ export const useFetchEpochProgress = () => {
75137
return existingRound;
76138
});
77139
});
78-
} else {
79-
setHasCallMade(false);
80-
}
81-
};
82-
83-
const startRoundTime = () => {
84-
if (!refreshInterval) {
85-
return;
86-
}
87-
const intervalRoundTime = setInterval(() => {
88-
if (!document.hidden) {
89-
setRoundTimeProgress((roundTimeProgress) =>
90-
roundTimeProgress.isEqualTo(refreshIntervalSec)
91-
? new BigNumber(stepProgressSec)
92-
: roundTimeProgress.plus(stepProgressSec)
93-
);
94-
}
95-
}, stepInterval.toNumber());
96-
return () => clearInterval(intervalRoundTime);
97-
};
98-
99-
useEffect(() => {
100-
setOldTestnetId(activeNetworkId);
101-
}, [activeNetworkId]);
102-
103-
useEffect(() => {
104-
if (refreshInterval && roundTimeProgress && timestamp) {
105-
updateStats();
140+
} else if (roundTimeProgress.isLessThan(refreshIntervalSec)) {
141+
hasCallMadeRef.current = false;
106142
}
107-
}, [timestamp, roundTimeProgress, refreshInterval]);
108-
109-
const roundProgress = roundTimeProgress
110-
.times(100)
111-
.dividedBy(refreshIntervalSec ?? 1);
112-
113-
const roundsLeft = useMemo(() => {
114-
if (epochRoundsLeft) {
115-
return epochRoundsLeft;
116-
}
117-
118-
// add one in order to take into account the css animation and the api call sync on the first run
119-
return new BigNumber(roundsPerEpoch).minus(roundsPassed).plus(1).toNumber();
120-
}, [epochRoundsLeft, roundsPerEpoch, roundsPassed]);
143+
}, [timestamp, roundTimeProgress]);
121144

122145
return {
123146
isReady: isDataReady,

src/widgets/BlockHeightStatsCard/BlockHeightStatsCard.tsx

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useMemo } from 'react';
1+
import { useMemo, useRef } from 'react';
22
import BigNumber from 'bignumber.js';
33
import { useSelector } from 'react-redux';
44

@@ -14,18 +14,26 @@ export const BlockHeightStatsCard = () => {
1414
const { blockHeight } = useSelector(pageHeadersBlocksStatsSelector);
1515
const { blocks: statsBlocks } = unprocessed;
1616

17+
const higherRef = useRef<number>(0);
18+
1719
const displayValue = useMemo(() => {
18-
const bNBlocks = new BigNumber(unprocessed?.blocks ?? 0);
19-
if (bNBlocks.isInteger() && bNBlocks.isGreaterThan(0)) {
20-
return bNBlocks.toNumber();
21-
}
20+
const bNBlocks = new BigNumber(statsBlocks ?? 0);
21+
const bNToolsBlocks = new BigNumber(
22+
blockHeight ? String(blockHeight).replaceAll(',', '') : 0
23+
);
2224

23-
const bNToolsBlocks = new BigNumber(blockHeight ?? 0);
24-
if (bNToolsBlocks.isInteger() && bNToolsBlocks.isGreaterThan(0)) {
25-
return bNToolsBlocks.toNumber();
25+
const highest = bNBlocks.isGreaterThan(bNToolsBlocks)
26+
? bNBlocks
27+
: bNToolsBlocks;
28+
29+
if (highest.isInteger() && highest.isGreaterThan(0)) {
30+
const num = highest.toNumber();
31+
if (num > higherRef.current) {
32+
higherRef.current = num;
33+
}
2634
}
2735

28-
return ELLIPSIS;
36+
return higherRef.current > 0 ? higherRef.current : ELLIPSIS;
2937
}, [blockHeight, statsBlocks]);
3038

3139
const isAnimated = Boolean(

src/widgets/EpochProgressRing/EpochProgressRing.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import classNames from 'classnames';
22

33
import { ProgressRing } from 'components';
4-
import { formatBigNumber } from 'helpers';
4+
import { formatBigNumber, getStringPlural } from 'helpers';
55
import { useFetchEpochProgress } from 'hooks';
66
import { WithClassnameType } from 'types';
77

@@ -29,7 +29,10 @@ export const EpochProgressRing = ({
2929
{...(showTime ? { title: epochTimeRemaining } : {})}
3030
>
3131
{formatBigNumber({ value: roundsLeft, showEllipsisIfZero: !isReady })}{' '}
32-
Rounds Left
32+
{getStringPlural(roundsLeft, {
33+
string: 'Round'
34+
})}{' '}
35+
Left
3336
</div>
3437
</ProgressRing>
3538
</div>

src/widgets/HeroPills/EpochHeroPill.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import classNames from 'classnames';
22

33
import { ProgressRing } from 'components';
4-
import { formatBigNumber } from 'helpers';
4+
import { formatBigNumber, getStringPlural } from 'helpers';
55
import { useFetchEpochProgress } from 'hooks';
66
import { WithClassnameType } from 'types';
77

@@ -23,7 +23,10 @@ export const EpochHeroPill = ({ className }: WithClassnameType) => {
2323
</div>
2424
<div className='description cursor-context' title={epochTimeRemaining}>
2525
{formatBigNumber({ value: roundsLeft, showEllipsisIfZero: !isReady })}{' '}
26-
Rounds Left
26+
{getStringPlural(roundsLeft, {
27+
string: 'Round'
28+
})}{' '}
29+
Left
2730
</div>
2831
</div>
2932
<ProgressRing progress={epochPercentage} size={32} />

0 commit comments

Comments
 (0)