Skip to content

Commit

Permalink
UX: Display total fiat balance on home screen
Browse files Browse the repository at this point in the history
  • Loading branch information
darkwing committed Sep 20, 2023
1 parent 9f7ccfc commit 460c194
Show file tree
Hide file tree
Showing 5 changed files with 363 additions and 330 deletions.
10 changes: 10 additions & 0 deletions shared/modules/conversion.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,16 @@ export function sumHexes(first: string, ...args: string[]) {
return total.toPrefixedHexString();
}

export function sumDecimals(first: string, ...args: string[]) {
const firstValue = new Numeric(first, 10);
const total = args.reduce(
(acc, hexAmount) => acc.add(new Numeric(hexAmount, 10)),
firstValue,
);

return total;
}

export function hexWEIToDecGWEI(value: number | string | BigNumber | BN) {
return new Numeric(value, 16, EtherDenomination.WEI)
.toBase(10)
Expand Down
78 changes: 75 additions & 3 deletions ui/components/app/asset-list/asset-list.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useContext, useState } from 'react';
import PropTypes from 'prop-types';
import { useSelector } from 'react-redux';
import { shallowEqual, useSelector } from 'react-redux';
import { isEqual } from 'lodash';
import TokenList from '../token-list';
import { PRIMARY, SECONDARY } from '../../../helpers/constants/common';
import { useUserPreferencedCurrency } from '../../../hooks/useUserPreferencedCurrency';
Expand All @@ -10,8 +11,15 @@ import {
getNativeCurrencyImage,
getDetectedTokensInCurrentNetwork,
getIstokenDetectionInactiveOnNonMainnetSupportedNetwork,
getShouldHideZeroBalanceTokens,
getTokenExchangeRates,
getCurrentCurrency,
} from '../../../selectors';
import { getNativeCurrency } from '../../../ducks/metamask/metamask';
import {
getConversionRate,
getNativeCurrency,
getTokens,
} from '../../../ducks/metamask/metamask';
import { useCurrencyDisplay } from '../../../hooks/useCurrencyDisplay';
import Box from '../../ui/box/box';
import { MetaMetricsContext } from '../../../contexts/metametrics';
Expand All @@ -26,6 +34,14 @@ import {
ImportTokenLink,
BalanceOverview,
} from '../../multichain';
import { isEqualCaseInsensitive } from '../../../../shared/modules/string-utils';
import { getTokenFiatAmount } from '../../../helpers/utils/token-util';
import { formatCurrency } from '../../../helpers/utils/confirm-tx.util';
import {
getValueFromWeiHex,
sumDecimals,
} from '../../../../shared/modules/conversion.utils';
import { useTokenTracker } from '../../../hooks/useTokenTracker';

const AssetList = ({ onClickAsset }) => {
const [showDetectedTokens, setShowDetectedTokens] = useState(false);
Expand Down Expand Up @@ -66,9 +82,63 @@ const AssetList = ({ onClickAsset }) => {
getIstokenDetectionInactiveOnNonMainnetSupportedNetwork,
);

const contractExchangeRates = useSelector(
getTokenExchangeRates,
shallowEqual,
);
const conversionRate = useSelector(getConversionRate);
const currentCurrency = useSelector(getCurrentCurrency);

const nativeFiat = getValueFromWeiHex({
value: balance,
toCurrency: currentCurrency,
conversionRate,
numberOfDecimals: 2,
});

const shouldHideZeroBalanceTokens = useSelector(
getShouldHideZeroBalanceTokens,
);
// use `isEqual` comparison function because the token array is serialized
// from the background so it has a new reference with each background update,
// even if the tokens haven't changed
const tokens = useSelector(getTokens, isEqual);
const { loading, tokensWithBalances } = useTokenTracker(
tokens,
true,
shouldHideZeroBalanceTokens,
);

const dollarBalances = tokensWithBalances.map((token) => {
const contractExchangeTokenKey = Object.keys(contractExchangeRates).find(
(key) => isEqualCaseInsensitive(key, token.address),
);
const tokenExchangeRate =
(contractExchangeTokenKey &&
contractExchangeRates[contractExchangeTokenKey]) ??
0;

const fiat = getTokenFiatAmount(
tokenExchangeRate,
conversionRate,
currentCurrency,
token.string,
token.symbol,
false,
false,
);

return fiat;
});

const totalFiat = formatCurrency(
sumDecimals(nativeFiat, ...dollarBalances).toString(10),
currentCurrency,
);

return (
<>
{process.env.MULTICHAIN ? <BalanceOverview /> : null}
{process.env.MULTICHAIN ? <BalanceOverview balance={totalFiat} /> : null}
{detectedTokens.length > 0 &&
!isTokenDetectionInactiveOnNonMainnetSupportedNetwork && (
<DetectedTokensBanner
Expand All @@ -87,6 +157,8 @@ const AssetList = ({ onClickAsset }) => {
tokenImage={balanceIsLoading ? null : primaryTokenImage}
/>
<TokenList
tokens={tokensWithBalances}
loading={loading}
onTokenClick={(tokenAddress) => {
onClickAsset(tokenAddress);
trackEvent({
Expand Down
29 changes: 7 additions & 22 deletions ui/components/app/token-list/token-list.js
Original file line number Diff line number Diff line change
@@ -1,34 +1,17 @@
import React from 'react';
import PropTypes from 'prop-types';
import { isEqual } from 'lodash';

import { useSelector } from 'react-redux';
import TokenCell from '../token-cell';
import { useI18nContext } from '../../../hooks/useI18nContext';
import { useTokenTracker } from '../../../hooks/useTokenTracker';
import { getShouldHideZeroBalanceTokens } from '../../../selectors';
import { getTokens } from '../../../ducks/metamask/metamask';
import { Box } from '../../component-library';
import {
AlignItems,
Display,
JustifyContent,
} from '../../../helpers/constants/design-system';

export default function TokenList({ onTokenClick }) {
export default function TokenList({ onTokenClick, tokens, loading = false }) {
const t = useI18nContext();
const shouldHideZeroBalanceTokens = useSelector(
getShouldHideZeroBalanceTokens,
);
// use `isEqual` comparison function because the token array is serialized
// from the background so it has a new reference with each background update,
// even if the tokens haven't changed
const tokens = useSelector(getTokens, isEqual);
const { loading, tokensWithBalances } = useTokenTracker(
tokens,
true,
shouldHideZeroBalanceTokens,
);

if (loading) {
return (
<Box
Expand All @@ -45,13 +28,15 @@ export default function TokenList({ onTokenClick }) {

return (
<div>
{tokensWithBalances.map((tokenData, index) => {
return <TokenCell key={index} {...tokenData} onClick={onTokenClick} />;
})}
{tokens.map((tokenData, index) => (
<TokenCell key={index} {...tokenData} onClick={onTokenClick} />
))}
</div>
);
}

TokenList.propTypes = {
onTokenClick: PropTypes.func.isRequired,
tokens: PropTypes.array.isRequired,
loading: PropTypes.bool,
};
Loading

0 comments on commit 460c194

Please sign in to comment.