Skip to content

Commit dbf490f

Browse files
Show Gas on Dashboard (#4181)
* Add gas price tiles to dashboard * Add slice and logic to calculate shown values * Only show receive tile on mobile * Move membership higher * Add tests * Update copy * Flip small and big text * Fix PR comments
1 parent f7a27be commit dbf490f

File tree

19 files changed

+266
-27
lines changed

19 files changed

+266
-27
lines changed

src/components/Currency.tsx

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { TCurrencySymbol, TTicker, TUuid } from '@types';
2-
import { isFiatTicker } from '@utils';
2+
import { formatCurrency, isFiatTicker } from '@utils';
33

44
import AssetIcon from './AssetIcon';
55
import Box from './Box';
@@ -29,19 +29,11 @@ function Currency({
2929
color,
3030
...props
3131
}: Props) {
32-
const format = (value: string, decimalPlaces: number) => {
33-
return new Intl.NumberFormat(navigator.language, {
34-
minimumFractionDigits: decimalPlaces,
35-
maximumFractionDigits: decimalPlaces,
36-
...(ticker && isFiatTicker(ticker) && { style: 'currency', currency: ticker })
37-
}).format(parseFloat(value));
38-
};
39-
4032
return (
4133
<Box variant="rowAlign" display="inline-flex" style={{ fontSize: fontSize }} {...props}>
4234
{icon && uuid && <AssetIcon uuid={uuid} mr="0.5ch" size="1.2em" />}
4335
<Body as="span" fontWeight={bold ? 'bold' : 'normal'} fontSize={'inherit'} color={color}>
44-
{format(amount, decimals)}
36+
{formatCurrency(amount, decimals, ticker)}
4537
{ticker && !isFiatTicker(ticker) && ` ${symbol || ticker}`}
4638
</Body>
4739
</Box>

src/features/Dashboard/Dashboard.tsx

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { DashboardZapCTA } from '../DeFiZap';
1313
import { NotificationsPanel } from '../NotificationsPanel';
1414
import {
1515
ActionTile,
16+
DashboardGas,
1617
MembershipPanel,
1718
RecentTransactionList,
1819
TokenPanel,
@@ -96,16 +97,17 @@ export default function Dashboard() {
9697
<Heading as="h2" className="Dashboard-desktop-top-left-heading">
9798
{translateRaw('YOUR_DASHBOARD')}
9899
</Heading>
99-
<div className="Dashboard-desktop-top-left-actions">
100-
{relevantActions.map((action) => (
101-
<ActionTile key={action.title} {...action} />
102-
))}
103-
</div>
100+
<DashboardGas />
104101
{featureFlags.MYC_MEMBERSHIP && (
105102
<div className="Dashboard-desktop-top-left-token">
106103
<MembershipPanel />
107104
</div>
108105
)}
106+
<div className="Dashboard-desktop-top-left-actions">
107+
{relevantActions.map((action) => (
108+
<ActionTile key={action.title} {...action} />
109+
))}
110+
</div>
109111
<div className="Dashboard-desktop-top-left-tokens">
110112
<TokenPanel />
111113
</div>

src/features/Dashboard/components/ActionTile.tsx

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,15 +104,24 @@ const SDescription = styled('div')`
104104
}
105105
`;
106106

107-
function ActionTile({ icon, faded, title, description, link }: Action) {
107+
function ActionTile({ icon, faded, title, description, link, inverse }: Action) {
108108
return (
109109
<SContainer>
110110
<LinkApp href={link} isExternal={isUrl(link)}>
111111
<SButton basic={true} faded={faded}>
112-
<Icon type={icon} alt={title} />
112+
{icon && <Icon type={icon} alt={title} />}
113113
<Typography as="div">
114-
<STitle isLonger={title.length > 15}>{title}</STitle>
115-
<SDescription>{description}</SDescription>
114+
{inverse ? (
115+
<>
116+
<SDescription>{description}</SDescription>
117+
<STitle isLonger={title.length > 15}>{title}</STitle>
118+
</>
119+
) : (
120+
<>
121+
<STitle isLonger={title.length > 15}>{title}</STitle>
122+
<SDescription>{description}</SDescription>
123+
</>
124+
)}
116125
</Typography>
117126
</SButton>
118127
</LinkApp>
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { mockStore, simpleRender } from 'test-utils';
2+
3+
import { fAssets, fSettings } from '@fixtures';
4+
import { bigify } from '@utils';
5+
6+
import { DashboardGas } from './DashboardGas';
7+
8+
describe('DashboardGas', () => {
9+
const renderComponent = () => {
10+
return simpleRender(<DashboardGas />, {
11+
initialState: mockStore({
12+
dataStoreState: {
13+
assets: fAssets,
14+
settings: fSettings,
15+
rates: {
16+
'356a192b-7913-504c-9457-4d18c28d46e6': {
17+
usd: 4000
18+
}
19+
}
20+
},
21+
storeSlice: {
22+
gas: {
23+
baseFee: bigify('200000000000')
24+
}
25+
}
26+
})
27+
});
28+
};
29+
30+
it('Can render', () => {
31+
const { getByText } = renderComponent();
32+
expect(getByText('200 Gwei')).toBeInTheDocument();
33+
expect(getByText('$17')).toBeInTheDocument();
34+
});
35+
});
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import React from 'react';
2+
3+
import BigNumber from 'bignumber.js';
4+
5+
import { Box } from '@components';
6+
import { ETHUUID } from '@config';
7+
import { useRates } from '@services/Rates';
8+
import { getBaseFee, getFiatInformation, useSelector } from '@store';
9+
import { getAssetByUUID } from '@store/asset.slice';
10+
import { translateRaw } from '@translations';
11+
import { TUuid } from '@types';
12+
import { bigify, formatCurrency, fromWei } from '@utils';
13+
14+
import ActionTile from './ActionTile';
15+
16+
export const DashboardGas = () => {
17+
const baseFee = useSelector(getBaseFee);
18+
const ethAsset = useSelector(getAssetByUUID(ETHUUID as TUuid))!;
19+
const { getAssetRate } = useRates();
20+
const fiat = useSelector(getFiatInformation);
21+
22+
const baseFeeGwei =
23+
baseFee && bigify(fromWei(baseFee, 'gwei')).integerValue(BigNumber.ROUND_HALF_UP);
24+
const baseFeeEther = baseFee && bigify(fromWei(baseFee, 'ether'));
25+
26+
const baseAssetRate = getAssetRate(ethAsset);
27+
const fiatValue =
28+
baseFeeEther &&
29+
baseFeeEther
30+
.multipliedBy(21000)
31+
.multipliedBy(baseAssetRate)
32+
.integerValue(BigNumber.ROUND_HALF_UP);
33+
34+
return (
35+
<Box variant="rowAlign" justifyContent="space-between">
36+
<Box width="48%">
37+
<ActionTile
38+
link="#"
39+
inverse={true}
40+
title={baseFeeGwei ? `${baseFeeGwei.toString(10)} ${translateRaw('GWEI')}` : '?'}
41+
description={translateRaw('CURRENT_BASE_FEE_TEXT')}
42+
/>
43+
</Box>
44+
<Box width="48%">
45+
<ActionTile
46+
link="#"
47+
inverse={true}
48+
title={fiatValue ? `${formatCurrency(fiatValue.toString(10), 0, fiat?.ticker)}` : '?'}
49+
description={translateRaw('ETH_TRANSFER_FEE')}
50+
/>
51+
</Box>
52+
</Box>
53+
);
54+
};

src/features/Dashboard/components/TokenPanel/TokenList.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ const TokenDashboardPanel = styled(DashboardPanel)`
6565
max-height: 740px;
6666
@media (min-width: ${BREAK_POINTS.SCREEN_SM}) {
6767
min-height: 430px;
68-
height: 652px;
68+
height: 676px;
6969
}
7070
`;
7171

src/features/Dashboard/components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ export { WalletBreakdown } from './WalletBreakdown';
66
export { default as NoTransactions } from './NoTransactions';
77
export { default as MembershipPanel } from './MembershipPanel';
88
export * from './helpers';
9+
export * from './DashboardGas';

src/features/Dashboard/constants.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ export const actions: Action[] = [
2020
icon: 'tx-receive',
2121
title: translateRaw('DASHBOARD_ACTIONS_REQUEST_ASSETS_TITLE'),
2222
link: ROUTE_PATHS.REQUEST_ASSETS.path,
23-
description: translateRaw('DASHBOARD_ACTIONS_REQUEST_ASSETS_SUBTITLE')
23+
description: translateRaw('DASHBOARD_ACTIONS_REQUEST_ASSETS_SUBTITLE'),
24+
filter: (isMobile) => isMobile
2425
},
2526
{
2627
icon: 'buy',

src/features/Dashboard/types.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@ import { TIcon } from '@components';
22
import { ITxType, TURL } from '@types';
33

44
export interface Action {
5-
icon: TIcon;
5+
icon?: TIcon;
66
faded?: boolean;
77
title: string;
88
description: string;
99
link: string | TURL;
10+
inverse?: boolean;
1011
filter?(isMobile: boolean): boolean;
1112
}
1213

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { expectSaga, mockAppState } from 'test-utils';
2+
3+
import { fNetworks } from '@fixtures';
4+
import { ProviderHandler } from '@services/EthService';
5+
import { bigify } from '@utils';
6+
7+
import slice, { fetchGas, initialState, setBaseFee } from './gas.slice';
8+
9+
const reducer = slice.reducer;
10+
11+
describe('Gas slice', () => {
12+
it('has an initial state', () => {
13+
const actual = reducer(undefined, { type: null });
14+
const expected = initialState;
15+
expect(actual).toEqual(expected);
16+
});
17+
18+
it('setBaseFee(): sets base fee', () => {
19+
const baseFee = bigify('20000000000');
20+
const actual = reducer(initialState, setBaseFee(baseFee));
21+
const expected = {
22+
baseFee
23+
};
24+
expect(actual).toEqual(expected);
25+
});
26+
});
27+
28+
describe('fetchGas()', () => {
29+
it('fetches base fee', () => {
30+
const baseFee = bigify('20000000000');
31+
ProviderHandler.prototype.getLatestBlock = jest
32+
.fn()
33+
.mockResolvedValue({ baseFeePerGas: baseFee });
34+
return expectSaga(fetchGas)
35+
.withState(
36+
mockAppState({
37+
networks: fNetworks
38+
})
39+
)
40+
.put(setBaseFee(baseFee))
41+
.silentRun();
42+
});
43+
});

0 commit comments

Comments
 (0)