mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2025-05-02 14:56:10 -04:00
feat(gui): Clickable addresses to copy to clipboard (#38)
This commit is contained in:
parent
1b1fe0add5
commit
ff1ded55ba
14 changed files with 405 additions and 37 deletions
|
@ -15,6 +15,7 @@
|
|||
"@material-ui/lab": "^4.0.0-alpha.61",
|
||||
"@reduxjs/toolkit": "^2.2.6",
|
||||
"@tauri-apps/api": "2.0.0-rc.1",
|
||||
"@tauri-apps/plugin-clipboard-manager": "^2.0.0-rc.0",
|
||||
"humanize-duration": "^3.32.1",
|
||||
"lodash": "^4.17.21",
|
||||
"multiaddr": "^10.0.1",
|
||||
|
|
|
@ -34,6 +34,11 @@ const theme = createTheme({
|
|||
disableRipple: true,
|
||||
},
|
||||
},
|
||||
typography: {
|
||||
overline: {
|
||||
textTransform: 'none', // This prevents the text from being all caps
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
function InnerContent() {
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { Box, Typography } from "@material-ui/core";
|
||||
import FileCopyOutlinedIcon from "@material-ui/icons/FileCopyOutlined";
|
||||
import { Box } from "@material-ui/core";
|
||||
import { ReactNode } from "react";
|
||||
import CopyableMonospaceTextBox from "renderer/components/other/CopyableMonospaceTextBox";
|
||||
import BitcoinQrCode from "./BitcoinQrCode";
|
||||
import ClipboardIconButton from "./ClipbiardIconButton";
|
||||
import InfoBox from "./InfoBox";
|
||||
|
||||
type Props = {
|
||||
|
@ -21,29 +20,18 @@ export default function DepositAddressInfoBox({
|
|||
return (
|
||||
<InfoBox
|
||||
title={title}
|
||||
mainContent={<Typography variant="h5">{address}</Typography>}
|
||||
mainContent={<CopyableMonospaceTextBox address={address} />}
|
||||
additionalContent={
|
||||
<Box>
|
||||
<Box>
|
||||
<ClipboardIconButton
|
||||
text={address}
|
||||
endIcon={<FileCopyOutlinedIcon />}
|
||||
color="primary"
|
||||
variant="contained"
|
||||
size="medium"
|
||||
/>
|
||||
<Box
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
gap: "0.5rem",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Box>{additionalContent}</Box>
|
||||
<BitcoinQrCode address={address} />
|
||||
</Box>
|
||||
</Box>
|
||||
<Box
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
gap: "0.5rem",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Box>{additionalContent}</Box>
|
||||
<BitcoinQrCode address={address} />
|
||||
</Box>
|
||||
}
|
||||
icon={icon}
|
||||
|
|
|
@ -26,7 +26,7 @@ const useStyles = makeStyles((theme) => ({
|
|||
upperContent: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: theme.spacing(0.5),
|
||||
gap: theme.spacing(1),
|
||||
},
|
||||
}));
|
||||
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
import { Box, Tooltip } from "@material-ui/core";
|
||||
import { FileCopyOutlined } from "@material-ui/icons";
|
||||
import { writeText } from "@tauri-apps/plugin-clipboard-manager";
|
||||
import { useState } from "react";
|
||||
import MonospaceTextBox from "./MonospaceTextBox";
|
||||
|
||||
type Props = {
|
||||
address: string;
|
||||
noIcon?: boolean;
|
||||
};
|
||||
|
||||
/** Display addresses monospaced and clickable such that a click copies the address to the clipboard. */
|
||||
export default function CopyableMonospaceTextBox({
|
||||
address,
|
||||
noIcon = false,
|
||||
}: Props) {
|
||||
// Signal that the address was copied
|
||||
const [copied, setCopied] = useState(false);
|
||||
const tooltip = copied ? "Copied to clipboard" : "Click to copy";
|
||||
|
||||
// Copy address to clipboard on-click
|
||||
const handleClick = async () => {
|
||||
// Copy to clipboard
|
||||
await writeText(address);
|
||||
// Change tooltip to show that we copied the address
|
||||
setCopied(true);
|
||||
// After a delay, show default tooltip again (2sec)
|
||||
setTimeout(() => setCopied(false), 2_000);
|
||||
};
|
||||
|
||||
// Apply icon unless specified otherwise
|
||||
const icon = noIcon ? null : <FileCopyOutlined />;
|
||||
|
||||
return (
|
||||
<Tooltip title={tooltip} arrow>
|
||||
{/* Div is necessary to make the tooltip work */}
|
||||
<Box style={{ cursor: "pointer" }}>
|
||||
<MonospaceTextBox
|
||||
content={address}
|
||||
endIcon={icon}
|
||||
onClick={handleClick}
|
||||
/>
|
||||
</Box>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
44
src-gui/src/renderer/components/other/MonospaceTextBox.tsx
Normal file
44
src-gui/src/renderer/components/other/MonospaceTextBox.tsx
Normal file
|
@ -0,0 +1,44 @@
|
|||
import { Box, Typography, makeStyles } from "@material-ui/core";
|
||||
import { ReactNode } from "react";
|
||||
|
||||
type Props = {
|
||||
content: string;
|
||||
onClick?: (content: string) => void;
|
||||
endIcon?: ReactNode;
|
||||
};
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
backgroundColor: theme.palette.grey[900],
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
padding: theme.spacing(1),
|
||||
gap: theme.spacing(1),
|
||||
},
|
||||
content: {
|
||||
wordBreak: "break-word",
|
||||
whiteSpace: "pre-wrap",
|
||||
fontFamily: "monospace",
|
||||
lineHeight: "1.5em",
|
||||
},
|
||||
}));
|
||||
|
||||
export default function MonospaceTextBox({ content, endIcon, onClick }: Props) {
|
||||
const classes = useStyles();
|
||||
|
||||
const handleClick = () => onClick?.(content);
|
||||
|
||||
return (
|
||||
<Box className={classes.root} onClick={handleClick}>
|
||||
<Typography
|
||||
component="span"
|
||||
variant="overline"
|
||||
className={classes.content}
|
||||
>
|
||||
{content}
|
||||
</Typography>
|
||||
{endIcon}
|
||||
</Box>
|
||||
);
|
||||
}
|
|
@ -61,7 +61,7 @@ export function SwapCancelRefundButton({
|
|||
export default function HistoryRowActions(swap: GetSwapInfoResponse) {
|
||||
if (swap.state_name === BobStateName.XmrRedeemed) {
|
||||
return (
|
||||
<Tooltip title="The swap is completed because you have redeemed the XMR">
|
||||
<Tooltip title="This swap is completed. You have redeemed the Monero.">
|
||||
<DoneIcon style={{ color: green[500] }} />
|
||||
</Tooltip>
|
||||
);
|
||||
|
@ -69,7 +69,7 @@ export default function HistoryRowActions(swap: GetSwapInfoResponse) {
|
|||
|
||||
if (swap.state_name === BobStateName.BtcRefunded) {
|
||||
return (
|
||||
<Tooltip title="The swap is completed because your BTC have been refunded">
|
||||
<Tooltip title="This swap is completed. Your Bitcoin has been refunded.">
|
||||
<DoneIcon style={{ color: green[500] }} />
|
||||
</Tooltip>
|
||||
);
|
||||
|
@ -79,7 +79,7 @@ export default function HistoryRowActions(swap: GetSwapInfoResponse) {
|
|||
// See this PR: https://github.com/UnstoppableSwap/unstoppableswap-gui/pull/212
|
||||
if (swap.state_name === BobStateName.BtcPunished) {
|
||||
return (
|
||||
<Tooltip title="The swap is completed because you have been punished">
|
||||
<Tooltip title="This swap is completed. You have been punished.">
|
||||
<ErrorIcon style={{ color: red[500] }} />
|
||||
</Tooltip>
|
||||
);
|
||||
|
|
|
@ -8,7 +8,10 @@ import {
|
|||
TableContainer,
|
||||
TableRow,
|
||||
} from "@material-ui/core";
|
||||
import { OpenInNew } from "@material-ui/icons";
|
||||
import { GetSwapInfoResponse } from "models/tauriModel";
|
||||
import CopyableMonospaceTextBox from "renderer/components/other/CopyableAddress";
|
||||
import MonospaceTextBox from "renderer/components/other/InlineCode";
|
||||
import {
|
||||
MoneroBitcoinExchangeRate,
|
||||
PiconeroAmount,
|
||||
|
@ -85,7 +88,11 @@ export default function HistoryRowExpanded({
|
|||
<TableRow>
|
||||
<TableCell>Provider Address</TableCell>
|
||||
<TableCell>
|
||||
<Box>{swap.seller.addresses.join(", ")}</Box>
|
||||
<Box>
|
||||
{swap.seller.addresses.map((addr) => (
|
||||
<CopyableMonospaceTextBox key={addr} address={addr} />
|
||||
))}
|
||||
</Box>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
|
@ -95,7 +102,10 @@ export default function HistoryRowExpanded({
|
|||
href={getBitcoinTxExplorerUrl(swap.tx_lock_id, isTestnet())}
|
||||
target="_blank"
|
||||
>
|
||||
{swap.tx_lock_id}
|
||||
<MonospaceTextBox
|
||||
content={swap.tx_lock_id}
|
||||
endIcon={<OpenInNew />}
|
||||
/>
|
||||
</Link>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
|
|
@ -630,6 +630,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@tauri-apps/api/-/api-2.0.0-rc.1.tgz#ec858f239e34792625e311f687fcaca0581e0904"
|
||||
integrity sha512-qubAWjM9sqofUh7fe+7UAbBY3wlkfCyxm+PNRYpq9mnNng7lvSQq3sYsFUEB12AYvgGARZSb54VMVUvRuVLi7w==
|
||||
|
||||
"@tauri-apps/api@^2.0.0-rc.0":
|
||||
version "2.0.0-rc.3"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/api/-/api-2.0.0-rc.3.tgz#1dd17530de9cafd854f77d3feeca1732a985a81e"
|
||||
integrity sha512-k1erUfnoOFJwL5VNFZz0BQZ2agNstG7CNOjwpdWMl1vOaVuSn4DhJtXB0Deh9lZaaDlfrykKOyZs9c3XXpMi5Q==
|
||||
|
||||
"@tauri-apps/cli-darwin-arm64@2.0.0-beta.21":
|
||||
version "2.0.0-beta.21"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.0.0-beta.21.tgz#9dc6f306b14d58b0b4fbf218ffbb31831e28cf4d"
|
||||
|
@ -696,6 +701,13 @@
|
|||
"@tauri-apps/cli-win32-ia32-msvc" "2.0.0-beta.21"
|
||||
"@tauri-apps/cli-win32-x64-msvc" "2.0.0-beta.21"
|
||||
|
||||
"@tauri-apps/plugin-clipboard-manager@^2.0.0-rc.0":
|
||||
version "2.0.0-rc.0"
|
||||
resolved "https://registry.yarnpkg.com/@tauri-apps/plugin-clipboard-manager/-/plugin-clipboard-manager-2.0.0-rc.0.tgz#8371fa2a2092c67d0cfd9322698c14115735459e"
|
||||
integrity sha512-2fS3wbRQEtorkk3Np2msJUeKCXRqLQ9sSo2FzlFdUPYNzThsu43uWCF55McGLAfltNOvXQIcQLUBf05jbBL/5w==
|
||||
dependencies:
|
||||
"@tauri-apps/api" "^2.0.0-rc.0"
|
||||
|
||||
"@types/babel__core@^7.20.5":
|
||||
version "7.20.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue