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: animation for numbers (current balance) #636

Merged
merged 8 commits into from
Dec 31, 2024
10 changes: 6 additions & 4 deletions frontend/components/MainPage/sections/OlasBalanceSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { sum } from 'lodash';
import { useMemo } from 'react';
import styled from 'styled-components';

import { AnimateNumber } from '@/components/ui/animations/AnimateNumber';
import { UNICODE_SYMBOLS } from '@/constants/symbols';
import { Pages } from '@/enums/Pages';
import { TokenSymbol } from '@/enums/Token';
Expand All @@ -14,7 +15,6 @@ import {
import { useFeatureFlag } from '@/hooks/useFeatureFlag';
import { usePageState } from '@/hooks/usePageState';
import { useServices } from '@/hooks/useServices';
import { balanceFormat } from '@/utils/numberFormatters';

import { CardSection } from '../../styled/CardSection';

Expand All @@ -36,7 +36,7 @@ export const MainOlasBalance = () => {
const isBalanceBreakdownEnabled = useFeatureFlag('manage-wallet');

const displayedBalance = useMemo(() => {
// olas across master wallet -- safes and eoa -- on relevant chains for agent
// olas across master wallet (safes and eoa) on relevant chains for agent
const masterWalletOlasBalance = masterWalletBalances?.reduce(
(acc, { symbol, balance, evmChainId }) => {
if (
Expand Down Expand Up @@ -81,7 +81,7 @@ export const MainOlasBalance = () => {
serviceStakedOlasBalance,
]);

return balanceFormat(totalOlasBalance, 2);
return totalOlasBalance;
}, [
masterWalletBalances,
selectedAgentConfig.requiresAgentSafesOn,
Expand Down Expand Up @@ -114,7 +114,9 @@ export const MainOlasBalance = () => {

<Flex align="end">
<span className="balance-symbol">{UNICODE_SYMBOLS.OLAS}</span>
<Balance className="balance">{displayedBalance}</Balance>
<Balance className="balance">
<AnimateNumber value={displayedBalance} />
</Balance>
<span className="balance-currency">OLAS</span>
</Flex>
</Flex>
Expand Down
51 changes: 51 additions & 0 deletions frontend/components/ui/animations/AnimateNumber.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { motion, useSpring } from 'framer-motion';
import { isNil } from 'lodash';
import React, { useEffect, useState } from 'react';

import { Nullable } from '@/types/Util';
import { balanceFormat } from '@/utils/numberFormatters';

type AnimatedNumberProps = {
value: Nullable<number>;
formatter?: (value: number) => string;
};

/**
* Animate the number from 0 to the given value.
*/
export const AnimateNumber = ({
value,
formatter = balanceFormat,
}: AnimatedNumberProps) => {
const [displayValue, setDisplayValue] = useState(0);

const springValue = useSpring(0, { stiffness: 150, damping: 25 });

useEffect(() => {
if (!isNil(value)) {
springValue.set(value);
}
}, [value, springValue]);

useEffect(() => {
let lastUpdate = Date.now();

const unsubscribe = springValue.on('change', (latest) => {
const now = Date.now();

// Only update the state at most every 100ms
if (now - lastUpdate > 100) {
lastUpdate = now;
setDisplayValue(parseFloat(latest.toFixed(2)));
}
});

return () => unsubscribe();
}, [springValue]);

return (
<motion.span>
{formatter ? formatter(displayValue) : displayValue}
</motion.span>
);
};
Loading