From 1ad4bcadf58dbef338a29dcb6d6ac6c14ed4bfde Mon Sep 17 00:00:00 2001 From: Mohan <86064887+binarybaron@users.noreply.github.com> Date: Fri, 18 Jul 2025 15:33:59 +0200 Subject: [PATCH] feat(gui): Rework transaction history ui (#470) Co-authored-by: b-enedict --- monero-sys/monero | 2 +- .../src/renderer/components/other/Units.tsx | 47 +++++- .../monero/components/ConfirmationsBadge.tsx | 50 ++++++ .../monero/components/TransactionHistory.tsx | 125 +++++--------- .../monero/components/TransactionItem.tsx | 155 ++++++++++++++++++ .../monero/components/WalletOverview.tsx | 1 + src-gui/src/renderer/components/theme.tsx | 40 +++++ 7 files changed, 331 insertions(+), 89 deletions(-) create mode 100644 src-gui/src/renderer/components/pages/monero/components/ConfirmationsBadge.tsx create mode 100644 src-gui/src/renderer/components/pages/monero/components/TransactionItem.tsx diff --git a/monero-sys/monero b/monero-sys/monero index dbbccecc..5f714f14 160000 --- a/monero-sys/monero +++ b/monero-sys/monero @@ -1 +1 @@ -Subproject commit dbbccecc89e1121762a4ad6b531638ece82aa0c7 +Subproject commit 5f714f147fd29228698070e6bd80e41ce2f86fb0 diff --git a/src-gui/src/renderer/components/other/Units.tsx b/src-gui/src/renderer/components/other/Units.tsx index 32fb7a00..78ab3df7 100644 --- a/src-gui/src/renderer/components/other/Units.tsx +++ b/src-gui/src/renderer/components/other/Units.tsx @@ -1,4 +1,4 @@ -import { Tooltip } from "@mui/material"; +import { Box, SxProps, Tooltip, Typography } from "@mui/material"; import { useAppSelector, useSettings } from "store/hooks"; import { getMarkup, piconerosToXmr, satsToBtc } from "utils/conversionUtils"; @@ -10,12 +10,18 @@ export function AmountWithUnit({ fixedPrecision, exchangeRate, parenthesisText = null, + labelStyles, + amountStyles, + disableTooltip = false, }: { amount: Amount; unit: string; fixedPrecision: number; exchangeRate?: Amount; parenthesisText?: string; + labelStyles?: SxProps; + amountStyles?: SxProps; + disableTooltip?: boolean; }) { const [fetchFiatPrices, fiatCurrency] = useSettings((settings) => [ settings.fetchFiatPrices, @@ -29,12 +35,25 @@ export function AmountWithUnit({ ? `≈ ${(exchangeRate * amount).toFixed(2)} ${fiatCurrency}` : ""; + const content = ( + + + {amount != null ? amount.toFixed(fixedPrecision) : "?"} + {" "} + + {unit} + {parenthesisText != null ? ` (${parenthesisText})` : null} + + + ); + + if (disableTooltip) { + return content; + } + return ( - - {amount != null ? amount.toFixed(fixedPrecision) : "?"} {unit} - {parenthesisText != null ? ` (${parenthesisText})` : null} - + {content} ); } @@ -89,9 +108,15 @@ export function BitcoinAmount({ amount }: { amount: Amount }) { export function MoneroAmount({ amount, fixedPrecision = 4, + labelStyles, + amountStyles, + disableTooltip = false, }: { amount: Amount; fixedPrecision?: number; + labelStyles?: SxProps; + amountStyles?: SxProps; + disableTooltip?: boolean; }) { const xmrRate = useAppSelector((state) => state.rates.xmrPrice); @@ -101,6 +126,9 @@ export function MoneroAmount({ unit="XMR" fixedPrecision={fixedPrecision} exchangeRate={xmrRate} + labelStyles={labelStyles} + amountStyles={amountStyles} + disableTooltip={disableTooltip} /> ); } @@ -164,14 +192,23 @@ export function SatsAmount({ amount }: { amount: Amount }) { export function PiconeroAmount({ amount, fixedPrecision = 8, + labelStyles, + amountStyles, + disableTooltip = false, }: { amount: Amount; fixedPrecision?: number; + labelStyles?: SxProps; + amountStyles?: SxProps; + disableTooltip?: boolean; }) { return ( ); } diff --git a/src-gui/src/renderer/components/pages/monero/components/ConfirmationsBadge.tsx b/src-gui/src/renderer/components/pages/monero/components/ConfirmationsBadge.tsx new file mode 100644 index 00000000..cc9f27c2 --- /dev/null +++ b/src-gui/src/renderer/components/pages/monero/components/ConfirmationsBadge.tsx @@ -0,0 +1,50 @@ +import { Box, Chip, Tooltip, Typography } from "@mui/material"; +import { + AutoAwesome as AutoAwesomeIcon, + CheckCircleOutline as CheckCircleOutlineIcon, +} from "@mui/icons-material"; + +export default function ConfirmationsBadge({ + confirmations, +}: { + confirmations: number; +}) { + if (confirmations === 0) { + return ( + } + label="Published" + color="secondary" + size="small" + /> + ); + } else if (confirmations < 10) { + const label = ( + <> + + + {confirmations} + + /10 + + + ); + return ; + } else { + return ( + + + + ); + } +} diff --git a/src-gui/src/renderer/components/pages/monero/components/TransactionHistory.tsx b/src-gui/src/renderer/components/pages/monero/components/TransactionHistory.tsx index 4950e077..1075b693 100644 --- a/src-gui/src/renderer/components/pages/monero/components/TransactionHistory.tsx +++ b/src-gui/src/renderer/components/pages/monero/components/TransactionHistory.tsx @@ -1,25 +1,8 @@ -import { - Typography, - Card, - CardContent, - Table, - TableBody, - TableCell, - TableContainer, - TableHead, - TableRow, - Paper, - Chip, - IconButton, - Tooltip, - Stack, -} from "@mui/material"; -import { OpenInNew as OpenInNewIcon } from "@mui/icons-material"; -import { open } from "@tauri-apps/plugin-shell"; -import { PiconeroAmount } from "../../../other/Units"; -import { getMoneroTxExplorerUrl } from "../../../../../utils/conversionUtils"; -import { isTestnet } from "store/config"; +import { Typography, Box, Paper } from "@mui/material"; import { TransactionInfo } from "models/tauriModel"; +import _ from "lodash"; +import dayjs from "dayjs"; +import TransactionItem from "./TransactionItem"; interface TransactionHistoryProps { history?: { @@ -27,76 +10,52 @@ interface TransactionHistoryProps { }; } +interface TransactionGroup { + date: string; + displayDate: string; + transactions: TransactionInfo[]; +} + // Component for displaying transaction history export default function TransactionHistory({ history, }: TransactionHistoryProps) { if (!history || !history.transactions || history.transactions.length === 0) { - return Transaction History; + return Transactions; } - return ( - <> - Transaction History + const transactions = history.transactions; - - - - - Amount - Fee - Confirmations - Explorer - - - - {[...history.transactions] - .sort((a, b) => a.confirmations - b.confirmations) - .map((tx, index) => ( - - - - - - - - - - - - = 10 ? "success" : "warning"} - size="small" - /> - - - {tx.tx_hash && ( - - { - const url = getMoneroTxExplorerUrl( - tx.tx_hash, - isTestnet(), - ); - open(url); - }} - > - - - - )} - - + // Group transactions by date using dayjs and lodash + const transactionGroups: TransactionGroup[] = _(transactions) + .groupBy((tx) => dayjs(tx.timestamp * 1000).format("YYYY-MM-DD")) // Convert Unix timestamp to date string + .map((txs, dateKey) => ({ + date: dateKey, + displayDate: dayjs(dateKey).format("MMMM D, YYYY"), // Human-readable format + transactions: _.orderBy(txs, ["timestamp"], ["desc"]), // Sort transactions within group by newest first + })) + .orderBy(["date"], ["desc"]) // Sort groups by newest date first + .value(); + + return ( + + + Transactions + + + {transactionGroups.map((group) => ( + + + {group.displayDate} + + + {group.transactions.map((tx) => ( + ))} - -
-
- + + + ))} + + ); } diff --git a/src-gui/src/renderer/components/pages/monero/components/TransactionItem.tsx b/src-gui/src/renderer/components/pages/monero/components/TransactionItem.tsx new file mode 100644 index 00000000..0d3faf7c --- /dev/null +++ b/src-gui/src/renderer/components/pages/monero/components/TransactionItem.tsx @@ -0,0 +1,155 @@ +import { + Box, + Chip, + IconButton, + Menu, + MenuItem, + Typography, +} from "@mui/material"; +import { TransactionDirection, TransactionInfo } from "models/tauriModel"; +import { + CallReceived as IncomingIcon, + MoreVert as MoreVertIcon, +} from "@mui/icons-material"; +import { CallMade as OutgoingIcon } from "@mui/icons-material"; +import { + FiatPiconeroAmount, + PiconeroAmount, +} from "renderer/components/other/Units"; +import ConfirmationsBadge from "./ConfirmationsBadge"; +import { getMoneroTxExplorerUrl } from "utils/conversionUtils"; +import { isTestnet } from "store/config"; +import { open } from "@tauri-apps/plugin-shell"; +import dayjs from "dayjs"; +import { useState } from "react"; + +interface TransactionItemProps { + transaction: TransactionInfo; +} + +export default function TransactionItem({ transaction }: TransactionItemProps) { + const isIncoming = transaction.direction === TransactionDirection.In; + const displayDate = dayjs(transaction.timestamp * 1000).format( + "MMM DD YYYY, HH:mm", + ); + + const amountStyles = isIncoming + ? { color: "success.tint" } + : { color: "error.tint" }; + + const [menuAnchorEl, setMenuAnchorEl] = useState(null); + const menuOpen = Boolean(menuAnchorEl); + + return ( + + + + {isIncoming ? : } + + + + ‐ + + + + + + + + + + + + {displayDate} + + + { + setMenuAnchorEl(event.currentTarget); + }} + > + + + setMenuAnchorEl(null)} + > + { + navigator.clipboard.writeText(transaction.tx_hash); + setMenuAnchorEl(null); + }} + > + Copy Transaction ID + + { + open(getMoneroTxExplorerUrl(transaction.tx_hash, isTestnet())); + setMenuAnchorEl(null); + }} + > + View on Explorer + + + + + ); +} diff --git a/src-gui/src/renderer/components/pages/monero/components/WalletOverview.tsx b/src-gui/src/renderer/components/pages/monero/components/WalletOverview.tsx index 5a0262d3..1f385b9e 100644 --- a/src-gui/src/renderer/components/pages/monero/components/WalletOverview.tsx +++ b/src-gui/src/renderer/components/pages/monero/components/WalletOverview.tsx @@ -72,6 +72,7 @@ export default function WalletOverview({