Skip to content

Commit

Permalink
Merge pull request #45 from OriginProtocol/feat/TAS-238-polish-top-nav
Browse files Browse the repository at this point in the history
feat: top nav account details
  • Loading branch information
toniocodo authored Sep 20, 2023
2 parents 2bdd1a9 + 2b9b59d commit 471d718
Show file tree
Hide file tree
Showing 19 changed files with 400 additions and 82 deletions.
14 changes: 14 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,20 @@
}
],

// react import
"no-restricted-imports": [
"error",
{
"paths": [
{
"name": "react",
"importNames": ["default"]
// "message": "some messages for who import 'React' accidentally"
}
]
}
],

// import types rules
"@typescript-eslint/consistent-type-imports": "error",
"import/consistent-type-specifier-style": ["error", "prefer-top-level"],
Expand Down
27 changes: 23 additions & 4 deletions apps/oeth/src/components/Topnav.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { useState } from 'react';

import {
alpha,
Box,
Expand All @@ -8,20 +10,25 @@ import {
useMediaQuery,
useTheme,
} from '@mui/material';
import { AccountPopover } from '@origin/oeth/shared';
import { OpenAccountModalButton } from '@origin/shared/providers';
import { useIntl } from 'react-intl';
import { Link, useLocation, useNavigate } from 'react-router-dom';
import { useAccount } from 'wagmi';

import { routes } from '../routes';

import type { BoxProps } from '@mui/material';

export function Topnav(props: BoxProps) {
const theme = useTheme();
const isSmall = useMediaQuery(theme.breakpoints.down('md'));
const isMd = useMediaQuery(theme.breakpoints.down('lg'));
const intl = useIntl();
const navigate = useNavigate();
const location = useLocation();
const { isConnected } = useAccount();
const [accountModalAnchor, setAccountModalAnchor] =
useState<HTMLButtonElement | null>(null);

return (
<Box
Expand Down Expand Up @@ -144,7 +151,7 @@ export function Topnav(props: BoxProps) {
sx={{
display: 'flex',
justifyContent: 'flex-end',
alignItems: 'stretch',
alignItems: 'center',
gap: { xs: 1, md: 2 },
'& > a, & > *': {
fontSize: {
Expand All @@ -159,6 +166,7 @@ export function Topnav(props: BoxProps) {
<MuiLink
href="https://oeth.on.fleek.co/"
target="_blank"
noWrap
sx={{
borderRadius: 25,
paddingBlock: 0.75,
Expand All @@ -171,6 +179,7 @@ export function Topnav(props: BoxProps) {
fontFamily: 'Inter',
fontStyle: 'normal',
fontWeight: 500,
minHeight: 36,
background: ` linear-gradient(0deg, ${alpha(
theme.palette.common.white,
0.05,
Expand All @@ -186,11 +195,21 @@ export function Topnav(props: BoxProps) {
lineHeight: '1rem',
}}
>
{isSmall
{isMd
? intl.formatMessage({ defaultMessage: 'IPFS' })
: intl.formatMessage({ defaultMessage: 'View on IPFS' })}
</MuiLink>
<OpenAccountModalButton />
<OpenAccountModalButton
onClick={(e) => {
if (isConnected) {
setAccountModalAnchor(e.currentTarget);
}
}}
/>
<AccountPopover
anchor={accountModalAnchor}
setAnchor={setAccountModalAnchor}
/>
</Box>
<Divider
sx={{
Expand Down
2 changes: 0 additions & 2 deletions libs/oeth/history/src/components/ChartCard.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import React from 'react';

import { Stack, Typography, useTheme } from '@mui/material';
import { Card } from '@origin/shared/components';
import { Line } from 'react-chartjs-2';
Expand Down
2 changes: 0 additions & 2 deletions libs/oeth/history/src/components/HistoryCell.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import React from 'react';

import { Stack, Typography } from '@mui/material';
import { useIntl } from 'react-intl';
import { useTransaction } from 'wagmi';
Expand Down
2 changes: 0 additions & 2 deletions libs/oeth/history/src/components/TransactionIcon.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import React from 'react';

import { Box } from '@mui/material';

type Props =
Expand Down
191 changes: 191 additions & 0 deletions libs/oeth/shared/src/components/AccountPopover.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
import {
alpha,
Box,
Button,
Divider,
Popover,
Skeleton,
Stack,
Typography,
useTheme,
} from '@mui/material';
import { Icon, LinkIcon, MiddleTruncated } from '@origin/shared/components';
import { tokens } from '@origin/shared/contracts';
import { formatAmount } from '@origin/shared/utils';
import { map, prop } from 'ramda';
import { useIntl } from 'react-intl';
import { useAccount, useBalance, useContractReads, useDisconnect } from 'wagmi';

import type { StackProps } from '@mui/material';
import type { Token } from '@origin/shared/contracts';

const balanceTokens = [
tokens.mainnet.WETH,
tokens.mainnet.rETH,
tokens.mainnet.frxETH,
tokens.mainnet.sfrxETH,
tokens.mainnet.stETH,
];

const padding = { paddingInline: 2, paddingBlock: 3 };

interface Props {
anchor: HTMLElement | null;
setAnchor: (value: HTMLButtonElement | null) => void;
}

export function AccountPopover({ anchor, setAnchor }: Props) {
const intl = useIntl();
const theme = useTheme();
const { address, isConnected, connector } = useAccount();
const { disconnect } = useDisconnect();

const { data: eth, isLoading: ethLoading } = useBalance({
address,
token: tokens.mainnet.ETH.address,
enabled: isConnected,
});
const { data: balances, isLoading: balancesLoading } = useContractReads({
contracts: balanceTokens.map((t) => ({
address: t.address,
abi: t.abi,
functionName: 'balanceOf',
args: [address],
})),
select: map(prop('result')),
});

function close() {
setAnchor(null);
}

if (!isConnected) return null;

return (
<Popover
open={!!anchor}
anchorEl={anchor}
onClose={close}
anchorOrigin={{
vertical: 50,
horizontal: 'right',
}}
transformOrigin={{
vertical: 'top',
horizontal: 'right',
}}
sx={{
'& .MuiPopover-paper': {
borderRadius: 1,
width: (theme) => ({
xs: '90vw',
md: `min(${theme.typography.pxToRem(300)}, 90vw)`,
}),
[theme.breakpoints.down('md')]: {
left: '0 !important',
right: 0,
marginInline: 'auto',
},
},
}}
>
<Stack>
<Stack
justifyContent="space-between"
alignItems="center"
direction="row"
sx={padding}
>
<Typography color="primary.contrastText">
{intl.formatMessage({ defaultMessage: 'Account' })}
</Typography>
<Button
variant="contained"
sx={{
borderRadius: 7,
paddingInline: 2.375,
paddingBlock: 1.25,
fontSize: '0.75rem',
lineHeight: '0.75rem',
'&:hover': {
background: (theme) => alpha(theme.palette.common.white, 0.05),
},
}}
color="secondary"
disableElevation
onClick={() => {
disconnect();
close();
}}
>
{intl.formatMessage({ defaultMessage: 'Disconnect' })}
</Button>
</Stack>
<Divider />
<Stack
alignItems="center"
gap={1.5}
sx={padding}
direction="row"
color="primary.contrastText"
>
<Icon src={`/images/${connector?.id.toLowerCase()}-icon.svg`} />
<MiddleTruncated>{address}</MiddleTruncated>
<LinkIcon
url={`https://etherscan.io/address/${address}`}
sx={{ transform: 'translateY(5%)' }}
/>
</Stack>
<Divider />
<Stack sx={padding} gap={2}>
<BalanceRow
token={tokens.mainnet.ETH}
balance={eth.value}
isBalanceLoading={ethLoading}
/>
{balanceTokens.map((tok, i) => (
<BalanceRow
key={tok.name}
token={tok}
balance={balances?.[i] ?? 0n}
isBalanceLoading={balancesLoading}
/>
))}
</Stack>
</Stack>
</Popover>
);
}

type BalanceRowProps = {
token: Token;
balance: bigint;
isBalanceLoading: boolean;
} & StackProps;

function BalanceRow({
token,
balance,
isBalanceLoading,
...rest
}: BalanceRowProps) {
return (
<Stack
direction="row"
alignItems="center"
color="primary.contrastText"
gap={1}
{...rest}
>
<Box component="img" src={token.icon} sx={{ width: 20 }} />
<Typography minWidth={60}>
{isBalanceLoading ? (
<Skeleton width={60} />
) : (
formatAmount(balance, token.decimals)
)}
</Typography>
<Typography color="text.secondary">{token.symbol}</Typography>
</Stack>
);
}
4 changes: 2 additions & 2 deletions libs/oeth/shared/src/components/ApyHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useState } from 'react';
import { useState } from 'react';

import {
alpha,
Expand All @@ -20,7 +20,7 @@ const days = [7, 30];
export function ApyHeader() {
const intl = useIntl();
const [selectedPeriod, setSelectedPeriod] = useState(30);
const [anchorEl, setAnchorEl] = React.useState<null | HTMLElement>(null);
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const { address } = useAccount();
const { data: oethBalance } = useBalance({
address,
Expand Down
3 changes: 3 additions & 0 deletions libs/oeth/shared/src/components/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './ApyHeader';
export * from './AccountPopover';
export * from './GasPopover';
3 changes: 1 addition & 2 deletions libs/oeth/shared/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
export * from './components/ApyHeader';
export * from './components/GasPopover';
export * from './components';
export * from './clients';
export * from './generated/graphql';
2 changes: 0 additions & 2 deletions libs/shared/components/src/Checkbox/EmptyCheckbox.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import React from 'react';

import { SvgIcon } from '@mui/material';

export function EmptyCheckbox() {
Expand Down
Loading

0 comments on commit 471d718

Please sign in to comment.