Skip to content

Commit 0b0c22c

Browse files
committed
feat(ledger-browser): refactor eth tokens page into accounts page
- This concludes refactoring of ethereum app, it should be fully usable with mostly same features as before. - Refactor completely ETH app token page into accounts page using MUI components. - Update materialized view in peristence plugin ethereum schema. - Use hyperledger favicon from local files and not from the URL. - Remove files and components that are not used anymore. - Remove unnecessary package dependencies. Depends on #3237 Signed-off-by: Michal Bajer <[email protected]>
1 parent 740c061 commit 0b0c22c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+1210
-1241
lines changed

packages/cacti-ledger-browser/index.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
<head>
55
<meta charset="UTF-8" />
6-
<link rel="icon" href="https://www.hyperledger.org/hubfs/hyperledgerfavicon.png" />
6+
<link rel="icon" href="/hyperledgerfavicon.webp" />
77
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
88
<title>Cacti Ledger Browser</title>
99
</head>

packages/cacti-ledger-browser/package.json

+7-5
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@
2929
"email": "[email protected]",
3030
"url": "https://example.com"
3131
},
32+
{
33+
"name": "Michal Bajer",
34+
"email": "[email protected]",
35+
"url": "https://www.fujitsu.com/global/"
36+
},
3237
{
3338
"name": "Tomasz Awramski",
3439
"email": "[email protected]",
@@ -56,26 +61,23 @@
5661
"@emotion/react": "11.11.4",
5762
"@emotion/styled": "11.11.5",
5863
"@mui/icons-material": "5.15.10",
64+
"@mui/lab": "5.0.0-alpha.170",
5965
"@mui/material": "5.15.15",
6066
"@supabase/supabase-js": "1.35.6",
6167
"@tanstack/react-query": "5.29.2",
6268
"apexcharts": "3.45.2",
63-
"localforage": "1.10.0",
64-
"match-sorter": "6.3.3",
65-
"moment": "2.30.1",
69+
"ethers": "6.12.1",
6670
"react": "18.2.0",
6771
"react-apexcharts": "1.4.1",
6872
"react-dom": "18.2.0",
6973
"react-router-dom": "6.21.3",
70-
"sort-by": "1.2.0",
7174
"web3": "4.1.1"
7275
},
7376
"devDependencies": {
7477
"@tanstack/eslint-plugin-query": "5.28.11",
7578
"@tanstack/react-query-devtools": "5.29.2",
7679
"@types/react": "18.2.43",
7780
"@types/react-dom": "18.2.17",
78-
"@types/sort-by": "1",
7981
"@vitejs/plugin-react": "4.2.1",
8082
"typescript": "5.2.2",
8183
"vite": "5.1.7"
Binary file not shown.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import React from "react";
2+
import Box from "@mui/material/Box";
3+
import Typography from "@mui/material/Typography";
4+
import Divider from "@mui/material/Divider";
5+
6+
import { TokenERC20 } from "../../../../common/supabase-types";
7+
import ERC20TokenList from "./ERC20TokenList";
8+
import ERC20TokenDetails from "./ERC20TokenDetails";
9+
10+
export type AccountERC20ViewProps = {
11+
accountAddress: string;
12+
};
13+
14+
export default function AccountERC20View({
15+
accountAddress,
16+
}: AccountERC20ViewProps) {
17+
const [selectedToken, setSelectedToken] = React.useState<
18+
TokenERC20 | undefined
19+
>(undefined);
20+
21+
const tokenDetailsId = selectedToken
22+
? `${selectedToken.token_address}-${selectedToken.account_address}`
23+
: "token-not-selected";
24+
25+
return (
26+
<Box>
27+
<Typography variant="h5" color="secondary">
28+
ERC20
29+
</Typography>
30+
<Box
31+
display="flex"
32+
flexDirection="row"
33+
justifyContent="space-around"
34+
alignContent="center"
35+
gap={5}
36+
>
37+
<Box flex={5}>
38+
<ERC20TokenList
39+
accountAddress={accountAddress}
40+
onTokenSelected={(token) => setSelectedToken(token)}
41+
/>
42+
</Box>
43+
44+
<Divider orientation="vertical" flexItem />
45+
46+
<Box flex={7}>
47+
<ERC20TokenDetails key={tokenDetailsId} token={selectedToken} />
48+
</Box>
49+
</Box>
50+
</Box>
51+
);
52+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import Chart from "react-apexcharts";
2+
import { useTheme } from "@mui/material";
3+
4+
import { BalanceHistoryListData } from "./balanceHistory";
5+
6+
export type ERC20BalanceHistoryChartProps = {
7+
data: BalanceHistoryListData[];
8+
height?: string | number;
9+
};
10+
11+
export default function ERC20BalanceHistoryChart({
12+
data,
13+
height,
14+
}: ERC20BalanceHistoryChartProps) {
15+
if (!data) {
16+
return;
17+
}
18+
19+
const theme = useTheme();
20+
21+
return (
22+
<>
23+
{/* Style overwrite for Apex Charts to use colors from the theme for toolbar buttons (right-top of the chart) */}
24+
<style>
25+
{`
26+
.apexcharts-zoom-icon.apexcharts-selected svg {
27+
fill: ${theme.palette.secondary.main} !important;
28+
}
29+
30+
.apexcharts-pan-icon.apexcharts-selected svg {
31+
stroke: ${theme.palette.secondary.main} !important;
32+
}
33+
`}
34+
</style>
35+
36+
{/* Chart component */}
37+
<Chart
38+
options={{
39+
chart: {
40+
type: "line",
41+
zoom: {
42+
enabled: true,
43+
type: "x",
44+
autoScaleYaxis: true,
45+
},
46+
toolbar: {
47+
autoSelected: "zoom",
48+
tools: {
49+
zoom: true,
50+
zoomin: true,
51+
zoomout: true,
52+
pan: true,
53+
reset: true,
54+
},
55+
},
56+
},
57+
colors: [theme.palette.primary.main],
58+
xaxis: {
59+
type: "datetime",
60+
categories: data?.map((txn) => new Date(txn.created_at).getTime()),
61+
labels: {
62+
format: "dd-MM-yyyy h:mm",
63+
},
64+
},
65+
yaxis: {
66+
title: {
67+
text: "Balance",
68+
},
69+
},
70+
stroke: {
71+
curve: "stepline",
72+
},
73+
markers: {
74+
size: 6,
75+
},
76+
tooltip: {
77+
x: {
78+
format: "dd-MM-yyyy h:mm:ss",
79+
},
80+
},
81+
}}
82+
series={[
83+
{
84+
name: "Balance",
85+
data: data.map((txn) => txn.balance),
86+
},
87+
]}
88+
type="line"
89+
height={height}
90+
/>
91+
</>
92+
);
93+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
import * as React from "react";
2+
import { styled } from "@mui/material/styles";
3+
import Box from "@mui/material/Box";
4+
import Table from "@mui/material/Table";
5+
import TableBody from "@mui/material/TableBody";
6+
import TableCell from "@mui/material/TableCell";
7+
import TableContainer from "@mui/material/TableContainer";
8+
import TableFooter from "@mui/material/TableFooter";
9+
import TablePagination from "@mui/material/TablePagination";
10+
import TableRow from "@mui/material/TableRow";
11+
import Paper from "@mui/material/Paper";
12+
import TableHead from "@mui/material/TableHead";
13+
import Typography from "@mui/material/Typography";
14+
import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown";
15+
import ArrowDropUpIcon from "@mui/icons-material/ArrowDropUp";
16+
17+
import { TokenHistoryItem20 } from "../../../../common/supabase-types";
18+
import ShortenedTypography from "../../../../components/ui/ShortenedTypography";
19+
20+
const StyledHeaderCell = styled(TableCell)(({ theme }) => ({
21+
color: theme.palette.primary.main,
22+
fontWeight: "bold",
23+
}));
24+
25+
export type BalanceAmountTextProps = {
26+
children: React.ReactNode;
27+
};
28+
29+
function PositiveBalanceAmountText({ children }: BalanceAmountTextProps) {
30+
return (
31+
<Box
32+
display="flex"
33+
flexDirection="row"
34+
justifyContent="right"
35+
alignItems="center"
36+
>
37+
<Typography color="green" fontWeight="bold">
38+
{children}
39+
</Typography>
40+
<ArrowDropUpIcon color="success" />
41+
</Box>
42+
);
43+
}
44+
45+
function NegativeBalanceAmountText({ children }: BalanceAmountTextProps) {
46+
return (
47+
<Box
48+
display="flex"
49+
flexDirection="row"
50+
justifyContent="right"
51+
alignItems="center"
52+
>
53+
<Typography color="red" fontWeight="bold">
54+
{children}
55+
</Typography>
56+
<ArrowDropDownIcon color="error" />
57+
</Box>
58+
);
59+
}
60+
61+
export type ERC20BalanceHistoryTableProps = {
62+
data: TokenHistoryItem20[];
63+
ownerAddress: string;
64+
};
65+
66+
function formatCreatedAtDate(createdAt: string) {
67+
const date = new Date(createdAt);
68+
return date.toUTCString();
69+
}
70+
71+
export default function ERC20BalanceHistoryTable({
72+
data,
73+
ownerAddress,
74+
}: ERC20BalanceHistoryTableProps) {
75+
const [page, setPage] = React.useState(0);
76+
const [rowsPerPage, setRowsPerPage] = React.useState(5);
77+
const sortedData = [...data].sort((a, b) =>
78+
b.created_at.localeCompare(a.created_at),
79+
);
80+
81+
// Avoid a layout jump when reaching the last page with empty rows.
82+
const emptyRows =
83+
page > 0 ? Math.max(0, (1 + page) * rowsPerPage - sortedData.length) : 0;
84+
85+
return (
86+
<TableContainer
87+
component={Paper}
88+
sx={{
89+
minWidth: 500,
90+
}}
91+
>
92+
<Table aria-label="erc20 token history for account">
93+
<TableHead>
94+
<TableRow>
95+
<StyledHeaderCell>Time</StyledHeaderCell>
96+
<StyledHeaderCell>Hash</StyledHeaderCell>
97+
<StyledHeaderCell>From/To</StyledHeaderCell>
98+
<StyledHeaderCell align="right">Amount</StyledHeaderCell>
99+
</TableRow>
100+
</TableHead>
101+
<TableBody>
102+
{(rowsPerPage > 0
103+
? sortedData.slice(
104+
page * rowsPerPage,
105+
page * rowsPerPage + rowsPerPage,
106+
)
107+
: sortedData
108+
).map((row) => {
109+
const isReceiving = row.recipient === ownerAddress;
110+
111+
return (
112+
<TableRow key={row.transaction_hash}>
113+
<TableCell>{formatCreatedAtDate(row.created_at)}</TableCell>
114+
<TableCell>
115+
<ShortenedTypography
116+
text={row.transaction_hash}
117+
minWidth={300}
118+
/>
119+
</TableCell>
120+
<TableCell>
121+
{isReceiving ? (
122+
<ShortenedTypography text={row.sender} minWidth={200} />
123+
) : (
124+
<ShortenedTypography text={row.recipient} minWidth={200} />
125+
)}
126+
</TableCell>
127+
<TableCell align="right">
128+
{isReceiving ? (
129+
<PositiveBalanceAmountText>
130+
{row.value}
131+
</PositiveBalanceAmountText>
132+
) : (
133+
<NegativeBalanceAmountText>
134+
-{row.value}
135+
</NegativeBalanceAmountText>
136+
)}
137+
</TableCell>
138+
</TableRow>
139+
);
140+
})}
141+
{emptyRows > 0 && (
142+
<TableRow style={{ height: 53 * emptyRows }}>
143+
<TableCell colSpan={6} />
144+
</TableRow>
145+
)}
146+
</TableBody>
147+
<TableFooter>
148+
<TableRow>
149+
<TablePagination
150+
rowsPerPageOptions={[5, 10, 25, { label: "All", value: -1 }]}
151+
colSpan={3}
152+
count={sortedData.length}
153+
rowsPerPage={rowsPerPage}
154+
page={page}
155+
onPageChange={(_event, newPage) => {
156+
setPage(newPage);
157+
}}
158+
onRowsPerPageChange={(event) => {
159+
setRowsPerPage(parseInt(event.target.value, 10));
160+
setPage(0);
161+
}}
162+
/>
163+
</TableRow>
164+
</TableFooter>
165+
</Table>
166+
</TableContainer>
167+
);
168+
}

0 commit comments

Comments
 (0)