mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2025-04-28 11:46:11 -04:00
feat(gui): Button to open modal with QR code of Bitcoin address (#116)
This commit is contained in:
parent
2bffe40a37
commit
15b43bf4a4
@ -216,7 +216,7 @@ export default function SwapStatusAlert({
|
||||
<Alert
|
||||
key={swap.swap_id}
|
||||
severity="warning"
|
||||
action={<SwapResumeButton swap={swap} />}
|
||||
action={<SwapResumeButton swap={swap}>Resume Swap</SwapResumeButton>}
|
||||
variant="filled"
|
||||
>
|
||||
<AlertTitle>
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { Box } from "@material-ui/core";
|
||||
import { ReactNode } from "react";
|
||||
import CopyableMonospaceTextBox from "renderer/components/other/CopyableMonospaceTextBox";
|
||||
import BitcoinQrCode from "./BitcoinQrCode";
|
||||
import ActionableMonospaceTextBox from "renderer/components/other/ActionableMonospaceTextBox";
|
||||
import InfoBox from "./InfoBox";
|
||||
|
||||
type Props = {
|
||||
@ -20,7 +19,7 @@ export default function DepositAddressInfoBox({
|
||||
return (
|
||||
<InfoBox
|
||||
title={title}
|
||||
mainContent={<CopyableMonospaceTextBox address={address} />}
|
||||
mainContent={<ActionableMonospaceTextBox content={address} displayCopyIcon={true} enableQrCode={true} />}
|
||||
additionalContent={
|
||||
<Box
|
||||
style={{
|
||||
@ -31,7 +30,6 @@ export default function DepositAddressInfoBox({
|
||||
}}
|
||||
>
|
||||
<Box>{additionalContent}</Box>
|
||||
<BitcoinQrCode address={address} />
|
||||
</Box>
|
||||
}
|
||||
icon={icon}
|
||||
|
@ -0,0 +1,120 @@
|
||||
import { Box, Button, IconButton, Tooltip, makeStyles } from "@material-ui/core";
|
||||
import { FileCopyOutlined, CropFree as CropFreeIcon } from "@material-ui/icons";
|
||||
import { writeText } from "@tauri-apps/plugin-clipboard-manager";
|
||||
import { useState } from "react";
|
||||
import MonospaceTextBox from "./MonospaceTextBox";
|
||||
import { Modal } from "@material-ui/core";
|
||||
import QRCode from "react-qr-code";
|
||||
|
||||
type ModalProps = {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
content: string;
|
||||
};
|
||||
|
||||
type Props = {
|
||||
content: string;
|
||||
displayCopyIcon?: boolean;
|
||||
enableQrCode?: boolean;
|
||||
};
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
container: {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
cursor: "pointer",
|
||||
},
|
||||
textBoxWrapper: {
|
||||
flexGrow: 1,
|
||||
},
|
||||
iconButton: {
|
||||
marginLeft: theme.spacing(1),
|
||||
},
|
||||
modalContent: {
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: theme.spacing(2),
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
height: "100%",
|
||||
},
|
||||
qrCode: {
|
||||
maxWidth: "90%",
|
||||
maxHeight: "90%",
|
||||
},
|
||||
}));
|
||||
|
||||
function QRCodeModal({ open, onClose, content }: ModalProps) {
|
||||
const classes = useStyles();
|
||||
return (
|
||||
<Modal open={open} onClose={onClose}>
|
||||
<Box className={classes.modalContent}>
|
||||
<QRCode
|
||||
value={content}
|
||||
size={500}
|
||||
className={classes.qrCode}
|
||||
viewBox="0 0 500 500"
|
||||
/>
|
||||
<Button onClick={onClose} size="large" variant="contained" color="primary">
|
||||
Done
|
||||
</Button>
|
||||
</Box>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
export default function ActionableMonospaceTextBox({
|
||||
content,
|
||||
displayCopyIcon = true,
|
||||
enableQrCode = true,
|
||||
}: Props) {
|
||||
const classes = useStyles();
|
||||
const [copied, setCopied] = useState(false);
|
||||
const [qrCodeOpen, setQrCodeOpen] = useState(false);
|
||||
const [isQrCodeButtonHovered, setIsQrCodeButtonHovered] = useState(false);
|
||||
|
||||
const handleCopy = async () => {
|
||||
await writeText(content);
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 2000);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Tooltip title={isQrCodeButtonHovered ? "" : (copied ? "Copied to clipboard" : "Click to copy")} arrow>
|
||||
<Box className={classes.container}>
|
||||
<Box className={classes.textBoxWrapper} onClick={handleCopy}>
|
||||
<MonospaceTextBox>
|
||||
{content}
|
||||
{displayCopyIcon && (
|
||||
<IconButton onClick={handleCopy} size="small" className={classes.iconButton}>
|
||||
<FileCopyOutlined />
|
||||
</IconButton>
|
||||
)}
|
||||
{enableQrCode && (
|
||||
<Tooltip title="Show QR Code" arrow>
|
||||
<IconButton
|
||||
onClick={() => setQrCodeOpen(true)}
|
||||
onMouseEnter={() => setIsQrCodeButtonHovered(true)}
|
||||
onMouseLeave={() => setIsQrCodeButtonHovered(false)}
|
||||
size="small"
|
||||
className={classes.iconButton}
|
||||
>
|
||||
<CropFreeIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
)}
|
||||
</MonospaceTextBox>
|
||||
</Box>
|
||||
</Box>
|
||||
</Tooltip>
|
||||
{enableQrCode && (
|
||||
<QRCodeModal
|
||||
open={qrCodeOpen}
|
||||
onClose={() => setQrCodeOpen(false)}
|
||||
content={content}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
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>
|
||||
);
|
||||
}
|
@ -1,10 +1,7 @@
|
||||
import { Box, Typography, makeStyles } from "@material-ui/core";
|
||||
import { ReactNode } from "react";
|
||||
|
||||
type Props = {
|
||||
content: string;
|
||||
onClick?: (content: string) => void;
|
||||
endIcon?: ReactNode;
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
@ -14,31 +11,25 @@ const useStyles = makeStyles((theme) => ({
|
||||
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",
|
||||
lineHeight: 1.5,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
},
|
||||
}));
|
||||
|
||||
export default function MonospaceTextBox({ content, endIcon, onClick }: Props) {
|
||||
export default function MonospaceTextBox({ children }: Props) {
|
||||
const classes = useStyles();
|
||||
|
||||
const handleClick = () => onClick?.(content);
|
||||
|
||||
return (
|
||||
<Box className={classes.root} onClick={handleClick}>
|
||||
<Typography
|
||||
component="span"
|
||||
variant="overline"
|
||||
className={classes.content}
|
||||
>
|
||||
{content}
|
||||
<Box className={classes.root}>
|
||||
<Typography component="span" variant="overline" className={classes.content}>
|
||||
{children}
|
||||
</Typography>
|
||||
{endIcon}
|
||||
</Box>
|
||||
);
|
||||
}
|
@ -10,7 +10,7 @@ import {
|
||||
} from "@material-ui/core";
|
||||
import { OpenInNew } from "@material-ui/icons";
|
||||
import { GetSwapInfoResponse } from "models/tauriModel";
|
||||
import CopyableMonospaceTextBox from "renderer/components/other/CopyableMonospaceTextBox";
|
||||
import ActionableMonospaceTextBox from "renderer/components/other/ActionableMonospaceTextBox";
|
||||
import MonospaceTextBox from "renderer/components/other/MonospaceTextBox";
|
||||
import {
|
||||
MoneroBitcoinExchangeRate,
|
||||
@ -90,7 +90,7 @@ export default function HistoryRowExpanded({
|
||||
<TableCell>
|
||||
<Box>
|
||||
{swap.seller.addresses.map((addr) => (
|
||||
<CopyableMonospaceTextBox key={addr} address={addr} />
|
||||
<ActionableMonospaceTextBox key={addr} content={addr} displayCopyIcon={false} enableQrCode={false} />
|
||||
))}
|
||||
</Box>
|
||||
</TableCell>
|
||||
|
Loading…
x
Reference in New Issue
Block a user