mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2025-12-17 09:34:16 -05:00
refactor (#656)
This commit is contained in:
parent
6db365bf23
commit
c3f3623b6e
21 changed files with 375 additions and 221 deletions
|
|
@ -3,11 +3,11 @@ import { TauriEvent } from "models/tauriModel";
|
|||
import {
|
||||
contextStatusEventReceived,
|
||||
contextInitializationFailed,
|
||||
rpcSetBalance,
|
||||
timelockChangeEventReceived,
|
||||
approvalEventReceived,
|
||||
backgroundProgressEventReceived,
|
||||
} from "store/features/rpcSlice";
|
||||
import { setBitcoinBalance } from "store/features/bitcoinWalletSlice";
|
||||
import { receivedCliLog } from "store/features/logsSlice";
|
||||
import { poolStatusReceived } from "store/features/poolSlice";
|
||||
import { swapProgressEventReceived } from "store/features/swapSlice";
|
||||
|
|
@ -118,7 +118,7 @@ listen<TauriEvent>(TAURI_UNIFIED_EVENT_CHANNEL_NAME, (event) => {
|
|||
break;
|
||||
|
||||
case "BalanceChange":
|
||||
store.dispatch(rpcSetBalance(eventData.balance));
|
||||
store.dispatch(setBitcoinBalance(eventData.balance));
|
||||
break;
|
||||
|
||||
case "SwapDatabaseStateUpdate":
|
||||
|
|
|
|||
|
|
@ -1,30 +0,0 @@
|
|||
import { Button } from "@mui/material";
|
||||
import Alert from "@mui/material/Alert";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useAppSelector } from "store/hooks";
|
||||
|
||||
export default function FundsLeftInWalletAlert() {
|
||||
const fundsLeft = useAppSelector((state) => state.rpc.state.balance);
|
||||
const navigate = useNavigate();
|
||||
|
||||
if (fundsLeft != null && fundsLeft > 0) {
|
||||
return (
|
||||
<Alert
|
||||
variant="filled"
|
||||
severity="info"
|
||||
action={
|
||||
<Button
|
||||
variant="outlined"
|
||||
size="small"
|
||||
onClick={() => navigate("/bitcoin-wallet")}
|
||||
>
|
||||
View
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
There are some Bitcoin left in your wallet
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
@ -14,7 +14,7 @@ export default function AddressInputPage({
|
|||
<>
|
||||
<DialogContentText>
|
||||
To withdraw the Bitcoin inside the internal wallet, please enter an
|
||||
address. All funds will be sent to that address.
|
||||
address. All funds (the entire balance) will be sent to that address.
|
||||
</DialogContentText>
|
||||
|
||||
<BitcoinAddressTextField
|
||||
|
|
@ -22,6 +22,7 @@ export default function AddressInputPage({
|
|||
onAddressChange={setWithdrawAddress}
|
||||
onAddressValidityChange={setWithdrawAddressValid}
|
||||
helperText="All Bitcoin of the internal wallet will be transferred to this address"
|
||||
allowEmpty={false}
|
||||
fullWidth
|
||||
/>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { Box, Tooltip } from "@mui/material";
|
||||
import { BackgroundProgressAlerts } from "../alert/DaemonStatusAlert";
|
||||
import FundsLeftInWalletAlert from "../alert/FundsLeftInWalletAlert";
|
||||
import UnfinishedSwapsAlert from "../alert/UnfinishedSwapsAlert";
|
||||
import BackgroundRefundAlert from "../alert/BackgroundRefundAlert";
|
||||
import ContactInfoBox from "../other/ContactInfoBox";
|
||||
|
|
@ -15,7 +14,6 @@ export default function NavigationFooter() {
|
|||
gap: 1,
|
||||
}}
|
||||
>
|
||||
<FundsLeftInWalletAlert />
|
||||
<UnfinishedSwapsAlert />
|
||||
<BackgroundRefundAlert />
|
||||
<BackgroundProgressAlerts />
|
||||
|
|
|
|||
|
|
@ -92,40 +92,43 @@ export default function ActionableMonospaceTextBox({
|
|||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
cursor: "pointer",
|
||||
filter: spoilerText && !isRevealed ? "blur(8px)" : "none",
|
||||
transition: "filter 0.3s ease",
|
||||
}}
|
||||
onClick={handleCopy}
|
||||
>
|
||||
<Box sx={{ flexGrow: 1 }} onClick={handleCopy}>
|
||||
<MonospaceTextBox light={light}>
|
||||
{content}
|
||||
{displayCopyIcon && (
|
||||
<IconButton
|
||||
onClick={handleCopy}
|
||||
size="small"
|
||||
sx={{ marginLeft: 1 }}
|
||||
>
|
||||
<FileCopyOutlined />
|
||||
</IconButton>
|
||||
)}
|
||||
{enableQrCode && (
|
||||
<Tooltip title="Show QR Code" arrow>
|
||||
<IconButton
|
||||
onClick={() => setQrCodeOpen(true)}
|
||||
onMouseEnter={() => setIsQrCodeButtonHovered(true)}
|
||||
onMouseLeave={() => setIsQrCodeButtonHovered(false)}
|
||||
size="small"
|
||||
sx={{ marginLeft: 1 }}
|
||||
>
|
||||
<QrCodeIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
)}
|
||||
</MonospaceTextBox>
|
||||
</Box>
|
||||
<MonospaceTextBox
|
||||
light={light}
|
||||
actions={
|
||||
<>
|
||||
{displayCopyIcon && (
|
||||
<Tooltip title="Copy to clipboard" arrow>
|
||||
<IconButton onClick={handleCopy} size="small">
|
||||
<FileCopyOutlined />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
)}
|
||||
{enableQrCode && (
|
||||
<Tooltip title="Show QR Code" arrow>
|
||||
<IconButton
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setQrCodeOpen(true);
|
||||
}}
|
||||
onMouseEnter={() => setIsQrCodeButtonHovered(true)}
|
||||
onMouseLeave={() => setIsQrCodeButtonHovered(false)}
|
||||
size="small"
|
||||
>
|
||||
<QrCodeIcon />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
>
|
||||
{content}
|
||||
</MonospaceTextBox>
|
||||
</Box>
|
||||
</Tooltip>
|
||||
|
||||
|
|
|
|||
|
|
@ -3,18 +3,25 @@ import { Box, Typography } from "@mui/material";
|
|||
type Props = {
|
||||
children: React.ReactNode;
|
||||
light?: boolean;
|
||||
actions?: React.ReactNode;
|
||||
};
|
||||
|
||||
export default function MonospaceTextBox({ children, light = false }: Props) {
|
||||
export default function MonospaceTextBox({
|
||||
children,
|
||||
light = false,
|
||||
actions,
|
||||
}: Props) {
|
||||
return (
|
||||
<Box
|
||||
sx={(theme) => ({
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
backgroundColor: light ? "transparent" : theme.palette.grey[900],
|
||||
borderRadius: 2,
|
||||
border: light ? `1px solid ${theme.palette.grey[800]}` : "none",
|
||||
padding: theme.spacing(1),
|
||||
gap: 1,
|
||||
})}
|
||||
>
|
||||
<Typography
|
||||
|
|
@ -25,12 +32,14 @@ export default function MonospaceTextBox({ children, light = false }: Props) {
|
|||
whiteSpace: "pre-wrap",
|
||||
fontFamily: "monospace",
|
||||
lineHeight: 1.5,
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
flex: 1,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Typography>
|
||||
{actions && (
|
||||
<Box sx={{ display: "flex", gap: 0.5, flexShrink: 0 }}>{actions}</Box>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,126 +0,0 @@
|
|||
import {
|
||||
Box,
|
||||
Typography,
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
DialogActions,
|
||||
Button,
|
||||
Link,
|
||||
DialogContentText,
|
||||
} from "@mui/material";
|
||||
import InfoBox from "renderer/components/pages/swap/swap/components/InfoBox";
|
||||
import { useState } from "react";
|
||||
import { getWalletDescriptor } from "renderer/rpc";
|
||||
import { ExportBitcoinWalletResponse } from "models/tauriModel";
|
||||
import PromiseInvokeButton from "renderer/components/PromiseInvokeButton";
|
||||
import ActionableMonospaceTextBox from "renderer/components/other/ActionableMonospaceTextBox";
|
||||
import { isContextWithBitcoinWallet } from "models/tauriModelExt";
|
||||
|
||||
export default function ExportDataBox() {
|
||||
const [walletDescriptor, setWalletDescriptor] =
|
||||
useState<ExportBitcoinWalletResponse | null>(null);
|
||||
|
||||
const handleCloseDialog = () => {
|
||||
setWalletDescriptor(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<InfoBox
|
||||
title="Export Bitcoin Wallet"
|
||||
icon={null}
|
||||
loading={false}
|
||||
mainContent={
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "flex-start",
|
||||
gap: 2,
|
||||
}}
|
||||
>
|
||||
<Typography variant="subtitle2">
|
||||
You can export the wallet descriptor of the interal Bitcoin wallet
|
||||
for backup or recovery purposes. Please make sure to store it
|
||||
securely.
|
||||
</Typography>
|
||||
</Box>
|
||||
}
|
||||
additionalContent={
|
||||
<>
|
||||
<PromiseInvokeButton
|
||||
variant="outlined"
|
||||
onInvoke={getWalletDescriptor}
|
||||
onSuccess={setWalletDescriptor}
|
||||
displayErrorSnackbar={true}
|
||||
contextRequirement={isContextWithBitcoinWallet}
|
||||
>
|
||||
Reveal Bitcoin Wallet Private Key
|
||||
</PromiseInvokeButton>
|
||||
{walletDescriptor !== null && (
|
||||
<WalletDescriptorModal
|
||||
open={walletDescriptor !== null}
|
||||
onClose={handleCloseDialog}
|
||||
walletDescriptor={walletDescriptor}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
function WalletDescriptorModal({
|
||||
open,
|
||||
onClose,
|
||||
walletDescriptor,
|
||||
}: {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
walletDescriptor: ExportBitcoinWalletResponse;
|
||||
}) {
|
||||
const parsedDescriptor = JSON.parse(
|
||||
walletDescriptor.wallet_descriptor["descriptor"],
|
||||
);
|
||||
const stringifiedDescriptor = JSON.stringify(parsedDescriptor, null, 4);
|
||||
|
||||
return (
|
||||
<Dialog open={open} onClose={onClose} maxWidth="md" fullWidth>
|
||||
<DialogTitle>Bitcoin Wallet Descriptor</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>
|
||||
<ul style={{ marginTop: 0 }}>
|
||||
<li>
|
||||
The text below contains the wallet descriptor of the internal
|
||||
Bitcoin wallet. It contains your private key and can be used to
|
||||
derive your wallet. It should thus be stored securely.
|
||||
</li>
|
||||
<li>
|
||||
It can be imported into other Bitcoin wallets or services that
|
||||
support the descriptor format.
|
||||
</li>
|
||||
<li>
|
||||
For more information on what to do with the descriptor, see our{" "}
|
||||
<Link
|
||||
href="https://github.com/eigenwallet/core/blob/master/dev-docs/asb/README.md#exporting-the-bitcoin-wallet-descriptor"
|
||||
target="_blank"
|
||||
>
|
||||
documentation
|
||||
</Link>
|
||||
</li>
|
||||
</ul>
|
||||
</DialogContentText>
|
||||
<ActionableMonospaceTextBox
|
||||
content={stringifiedDescriptor}
|
||||
displayCopyIcon={true}
|
||||
enableQrCode={false}
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={onClose} color="primary" variant="contained">
|
||||
Done
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
|
@ -32,7 +32,6 @@ export default function SettingsPage() {
|
|||
<SettingsBox />
|
||||
<DaemonControlBox />
|
||||
<MoneroPoolHealthBox />
|
||||
<ExportDataBox />
|
||||
<DiscoveryBox />
|
||||
</Box>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ export default function ConfirmationsBadge({
|
|||
return (
|
||||
<Chip
|
||||
icon={<AutoAwesomeIcon />}
|
||||
label="Published"
|
||||
label="Unconfirmed"
|
||||
color="secondary"
|
||||
size="small"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,21 +1,32 @@
|
|||
import { Box, Typography } from "@mui/material";
|
||||
import { Alert } from "@mui/material";
|
||||
import WithdrawWidget from "./WithdrawWidget";
|
||||
import { Box } from "@mui/material";
|
||||
import { useAppSelector } from "store/hooks";
|
||||
import WalletOverview from "./components/WalletOverview";
|
||||
import WalletActionButtons from "./components/WalletActionButtons";
|
||||
import ActionableMonospaceTextBox from "renderer/components/other/ActionableMonospaceTextBox";
|
||||
|
||||
export default function WalletPage() {
|
||||
const walletBalance = useAppSelector((state) => state.bitcoinWallet.balance);
|
||||
const bitcoinAddress = useAppSelector((state) => state.bitcoinWallet.address);
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
maxWidth: 800,
|
||||
mx: "auto",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "1rem",
|
||||
gap: 2,
|
||||
pb: 2,
|
||||
}}
|
||||
>
|
||||
<Alert severity="info">
|
||||
You do not have to deposit money before starting a swap. Instead, you
|
||||
will be greeted with a deposit address after you initiate one.
|
||||
</Alert>
|
||||
<WithdrawWidget />
|
||||
<WalletOverview balance={walletBalance} />
|
||||
{bitcoinAddress && (
|
||||
<ActionableMonospaceTextBox
|
||||
content={bitcoinAddress}
|
||||
displayCopyIcon={true}
|
||||
/>
|
||||
)}
|
||||
<WalletActionButtons />
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import WithdrawDialog from "../../modal/wallet/WithdrawDialog";
|
|||
import WalletRefreshButton from "./WalletRefreshButton";
|
||||
|
||||
export default function WithdrawWidget() {
|
||||
const walletBalance = useAppSelector((state) => state.rpc.state.balance);
|
||||
const walletBalance = useAppSelector((state) => state.bitcoinWallet.balance);
|
||||
const [showDialog, setShowDialog] = useState(false);
|
||||
|
||||
function onShowDialog() {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
import { Box, Chip } from "@mui/material";
|
||||
import { Send as SendIcon } from "@mui/icons-material";
|
||||
import { useState } from "react";
|
||||
import { useAppSelector } from "store/hooks";
|
||||
import WithdrawDialog from "../../../modal/wallet/WithdrawDialog";
|
||||
import WalletDescriptorButton from "./WalletDescriptorButton";
|
||||
|
||||
export default function WalletActionButtons() {
|
||||
const [showDialog, setShowDialog] = useState(false);
|
||||
const balance = useAppSelector((state) => state.bitcoinWallet.balance);
|
||||
|
||||
return (
|
||||
<>
|
||||
<WithdrawDialog open={showDialog} onClose={() => setShowDialog(false)} />
|
||||
<Box sx={{ display: "flex", justifyContent: "space-between" }}>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexWrap: "wrap",
|
||||
gap: 1,
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<Chip
|
||||
icon={<SendIcon />}
|
||||
label="Sweep"
|
||||
variant="button"
|
||||
clickable
|
||||
onClick={() => setShowDialog(true)}
|
||||
disabled={balance === null || balance <= 0}
|
||||
/>
|
||||
<WalletDescriptorButton />
|
||||
</Box>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
DialogContent,
|
||||
DialogActions,
|
||||
DialogContentText,
|
||||
Link,
|
||||
} from "@mui/material";
|
||||
import { Key as KeyIcon } from "@mui/icons-material";
|
||||
import { useState } from "react";
|
||||
import ActionableMonospaceTextBox from "renderer/components/other/ActionableMonospaceTextBox";
|
||||
import { getWalletDescriptor } from "renderer/rpc";
|
||||
import { ExportBitcoinWalletResponse } from "models/tauriModel";
|
||||
import PromiseInvokeButton from "renderer/components/PromiseInvokeButton";
|
||||
import { isContextWithBitcoinWallet } from "models/tauriModelExt";
|
||||
|
||||
const WALLET_DESCRIPTOR_DOCS_URL =
|
||||
"https://github.com/eigenwallet/core/blob/master/dev-docs/asb/README.md#exporting-the-bitcoin-wallet-descriptor";
|
||||
|
||||
export default function WalletDescriptorButton() {
|
||||
const [walletDescriptor, setWalletDescriptor] =
|
||||
useState<ExportBitcoinWalletResponse | null>(null);
|
||||
|
||||
const handleCloseDialog = () => {
|
||||
setWalletDescriptor(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<PromiseInvokeButton
|
||||
isChipButton={true}
|
||||
startIcon={<KeyIcon />}
|
||||
onInvoke={getWalletDescriptor}
|
||||
onSuccess={setWalletDescriptor}
|
||||
displayErrorSnackbar={true}
|
||||
contextRequirement={isContextWithBitcoinWallet}
|
||||
>
|
||||
Reveal Private Key
|
||||
</PromiseInvokeButton>
|
||||
{walletDescriptor !== null && (
|
||||
<WalletDescriptorModal
|
||||
open={walletDescriptor !== null}
|
||||
onClose={handleCloseDialog}
|
||||
walletDescriptor={walletDescriptor}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function WalletDescriptorModal({
|
||||
open,
|
||||
onClose,
|
||||
walletDescriptor,
|
||||
}: {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
walletDescriptor: ExportBitcoinWalletResponse;
|
||||
}) {
|
||||
const parsedDescriptor = JSON.parse(
|
||||
walletDescriptor.wallet_descriptor["descriptor"],
|
||||
);
|
||||
const stringifiedDescriptor = JSON.stringify(parsedDescriptor, null, 4);
|
||||
|
||||
return (
|
||||
<Dialog open={open} onClose={onClose} maxWidth="md" fullWidth>
|
||||
<DialogTitle>Bitcoin Wallet Descriptor</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText>
|
||||
The Bitcoin wallet is derived from your Monero wallet. Opening the
|
||||
same Monero wallet in another Eigenwallet will yield the same Bitcoin
|
||||
wallet.
|
||||
<br />
|
||||
<br />
|
||||
It contains your private key. Anyone who has it can spend your funds.
|
||||
It should thus be stored securely.
|
||||
<br />
|
||||
<br />
|
||||
It can be imported into other Bitcoin wallets or services that support
|
||||
the descriptor format. For more information on what to do with the
|
||||
descriptor, see our{" "}
|
||||
<Link href={WALLET_DESCRIPTOR_DOCS_URL} target="_blank">
|
||||
documentation
|
||||
</Link>
|
||||
</DialogContentText>
|
||||
<ActionableMonospaceTextBox
|
||||
content={stringifiedDescriptor}
|
||||
displayCopyIcon={true}
|
||||
enableQrCode={false}
|
||||
spoilerText="Press to Reveal Private Key"
|
||||
/>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={onClose} color="primary" variant="contained">
|
||||
Done
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
import { Box, Typography, Card } from "@mui/material";
|
||||
import { BitcoinAmount } from "renderer/components/other/Units";
|
||||
import { useAppSelector, useSettings } from "store/hooks";
|
||||
import { satsToBtc } from "utils/conversionUtils";
|
||||
import WalletRefreshButton from "../WalletRefreshButton";
|
||||
|
||||
interface WalletOverviewProps {
|
||||
balance: number | null;
|
||||
}
|
||||
|
||||
function FiatBitcoinAmount({ amount }: { amount: number | null }) {
|
||||
const btcPrice = useAppSelector((state) => state.rates.btcPrice);
|
||||
const [fetchFiatPrices, fiatCurrency] = useSettings((settings) => [
|
||||
settings.fetchFiatPrices,
|
||||
settings.fiatCurrency,
|
||||
]);
|
||||
|
||||
if (
|
||||
!fetchFiatPrices ||
|
||||
fiatCurrency == null ||
|
||||
amount == null ||
|
||||
btcPrice == null
|
||||
) {
|
||||
return <span />;
|
||||
}
|
||||
|
||||
return (
|
||||
<span>
|
||||
{(amount * btcPrice).toFixed(2)} {fiatCurrency}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
export default function WalletOverview({ balance }: WalletOverviewProps) {
|
||||
const btcBalance = balance == null ? null : satsToBtc(balance);
|
||||
|
||||
return (
|
||||
<Card sx={{ p: 2, position: "relative", borderRadius: 2 }} elevation={4}>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "stretch",
|
||||
mb: 1,
|
||||
}}
|
||||
>
|
||||
{/* Left side content */}
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: 0.5,
|
||||
}}
|
||||
>
|
||||
<Typography variant="body2" color="text.secondary" sx={{ mb: 1 }}>
|
||||
Available Funds
|
||||
</Typography>
|
||||
<Typography variant="h4">
|
||||
<BitcoinAmount amount={btcBalance} />
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
<FiatBitcoinAmount amount={btcBalance} />
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
{/* Right side - Refresh button */}
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "flex-end",
|
||||
justifyContent: "flex-start",
|
||||
}}
|
||||
>
|
||||
<WalletRefreshButton />
|
||||
</Box>
|
||||
</Box>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@ import {
|
|||
WithdrawBtcResponse,
|
||||
GetSwapInfoArgs,
|
||||
ExportBitcoinWalletResponse,
|
||||
GetBitcoinAddressResponse,
|
||||
CheckMoneroNodeArgs,
|
||||
CheckSeedArgs,
|
||||
CheckSeedResponse,
|
||||
|
|
@ -49,11 +50,11 @@ import {
|
|||
ContextStatus,
|
||||
} from "models/tauriModel";
|
||||
import {
|
||||
rpcSetBalance,
|
||||
rpcSetSwapInfo,
|
||||
approvalRequestsReplaced,
|
||||
contextInitializationFailed,
|
||||
} from "store/features/rpcSlice";
|
||||
import { setBitcoinBalance } from "store/features/bitcoinWalletSlice";
|
||||
import {
|
||||
setMainAddress,
|
||||
setBalance,
|
||||
|
|
@ -166,7 +167,7 @@ export async function checkBitcoinBalance() {
|
|||
force_refresh: true,
|
||||
});
|
||||
|
||||
store.dispatch(rpcSetBalance(response.balance));
|
||||
store.dispatch(setBitcoinBalance(response.balance));
|
||||
}
|
||||
|
||||
export async function cheapCheckBitcoinBalance() {
|
||||
|
|
@ -174,7 +175,15 @@ export async function cheapCheckBitcoinBalance() {
|
|||
force_refresh: false,
|
||||
});
|
||||
|
||||
store.dispatch(rpcSetBalance(response.balance));
|
||||
store.dispatch(setBitcoinBalance(response.balance));
|
||||
}
|
||||
|
||||
export async function getBitcoinAddress() {
|
||||
const response = await invokeNoArgs<GetBitcoinAddressResponse>(
|
||||
"get_bitcoin_address",
|
||||
);
|
||||
|
||||
return response.address;
|
||||
}
|
||||
|
||||
export async function getAllSwapInfos() {
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import nodesSlice from "./features/nodesSlice";
|
|||
import conversationsSlice from "./features/conversationsSlice";
|
||||
import poolSlice from "./features/poolSlice";
|
||||
import walletSlice from "./features/walletSlice";
|
||||
import bitcoinWalletSlice from "./features/bitcoinWalletSlice";
|
||||
import logsSlice from "./features/logsSlice";
|
||||
|
||||
export const reducers = {
|
||||
|
|
@ -21,5 +22,6 @@ export const reducers = {
|
|||
conversations: conversationsSlice,
|
||||
pool: poolSlice,
|
||||
wallet: walletSlice,
|
||||
bitcoinWallet: bitcoinWalletSlice,
|
||||
logs: logsSlice,
|
||||
};
|
||||
|
|
|
|||
32
src-gui/src/store/features/bitcoinWalletSlice.ts
Normal file
32
src-gui/src/store/features/bitcoinWalletSlice.ts
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||
|
||||
interface BitcoinWalletState {
|
||||
address: string | null;
|
||||
balance: number | null;
|
||||
}
|
||||
|
||||
const initialState: BitcoinWalletState = {
|
||||
address: null,
|
||||
balance: null,
|
||||
};
|
||||
|
||||
export const bitcoinWalletSlice = createSlice({
|
||||
name: "bitcoinWallet",
|
||||
initialState,
|
||||
reducers: {
|
||||
setBitcoinAddress(state, action: PayloadAction<string>) {
|
||||
state.address = action.payload;
|
||||
},
|
||||
setBitcoinBalance(state, action: PayloadAction<number>) {
|
||||
state.balance = action.payload;
|
||||
},
|
||||
resetBitcoinWalletState(state) {
|
||||
return initialState;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const { setBitcoinAddress, setBitcoinBalance, resetBitcoinWalletState } =
|
||||
bitcoinWalletSlice.actions;
|
||||
|
||||
export default bitcoinWalletSlice.reducer;
|
||||
|
|
@ -14,7 +14,6 @@ import { GetSwapInfoResponseExt } from "models/tauriModelExt";
|
|||
import logger from "utils/logger";
|
||||
|
||||
interface State {
|
||||
balance: number | null;
|
||||
withdrawTxId: string | null;
|
||||
rendezvousDiscoveredSellers: (ExtendedMakerStatus | MakerStatus)[];
|
||||
swapInfos: {
|
||||
|
|
@ -54,7 +53,6 @@ export interface RPCSlice {
|
|||
const initialState: RPCSlice = {
|
||||
status: null,
|
||||
state: {
|
||||
balance: null,
|
||||
withdrawTxId: null,
|
||||
rendezvousDiscoveredSellers: [],
|
||||
swapInfos: {},
|
||||
|
|
@ -95,9 +93,6 @@ export const rpcSlice = createSlice({
|
|||
);
|
||||
}
|
||||
},
|
||||
rpcSetBalance(slice, action: PayloadAction<number>) {
|
||||
slice.state.balance = action.payload;
|
||||
},
|
||||
rpcSetWithdrawTxId(slice, action: PayloadAction<string>) {
|
||||
slice.state.withdrawTxId = action.payload;
|
||||
},
|
||||
|
|
@ -177,7 +172,6 @@ export const rpcSlice = createSlice({
|
|||
export const {
|
||||
contextStatusEventReceived,
|
||||
contextInitializationFailed,
|
||||
rpcSetBalance,
|
||||
rpcSetWithdrawTxId,
|
||||
rpcResetWithdrawTxId,
|
||||
rpcSetRendezvousDiscoveredMakers,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { throttle, debounce } from "lodash";
|
|||
import {
|
||||
getAllSwapInfos,
|
||||
checkBitcoinBalance,
|
||||
getBitcoinAddress,
|
||||
updateAllNodeStatuses,
|
||||
fetchSellersAtPresetRendezvousPoints,
|
||||
getSwapInfo,
|
||||
|
|
@ -30,6 +31,7 @@ import {
|
|||
addFeedbackId,
|
||||
setConversation,
|
||||
} from "store/features/conversationsSlice";
|
||||
import { setBitcoinAddress } from "store/features/bitcoinWalletSlice";
|
||||
|
||||
// Create a Map to store throttled functions per swap_id
|
||||
const throttledGetSwapInfoFunctions = new Map<
|
||||
|
|
@ -86,15 +88,21 @@ export function createMainListeners() {
|
|||
|
||||
if (!status) return;
|
||||
|
||||
// If the Bitcoin wallet just came available, check the Bitcoin balance
|
||||
// If the Bitcoin wallet just came available, check the Bitcoin balance and get address
|
||||
if (
|
||||
status.bitcoin_wallet_available &&
|
||||
!previousContextStatus?.bitcoin_wallet_available
|
||||
) {
|
||||
logger.info(
|
||||
"Bitcoin wallet just became available, checking balance...",
|
||||
"Bitcoin wallet just became available, checking balance and getting address...",
|
||||
);
|
||||
await checkBitcoinBalance();
|
||||
try {
|
||||
const address = await getBitcoinAddress();
|
||||
store.dispatch(setBitcoinAddress(address));
|
||||
} catch (error) {
|
||||
logger.error("Failed to fetch Bitcoin address", error);
|
||||
}
|
||||
}
|
||||
|
||||
// If the Monero wallet just came available, initialize the Monero wallet
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@ use swap::cli::{
|
|||
BalanceArgs, BuyXmrArgs, CancelAndRefundArgs, ChangeMoneroNodeArgs,
|
||||
CheckElectrumNodeArgs, CheckElectrumNodeResponse, CheckMoneroNodeArgs,
|
||||
CheckMoneroNodeResponse, CheckSeedArgs, CheckSeedResponse, DfxAuthenticateResponse,
|
||||
ExportBitcoinWalletArgs, GetCurrentSwapArgs, GetDataDirArgs, GetHistoryArgs,
|
||||
GetLogsArgs, GetMoneroAddressesArgs, GetMoneroBalanceArgs, GetMoneroHistoryArgs,
|
||||
GetMoneroMainAddressArgs, GetMoneroSeedArgs, GetMoneroSyncProgressArgs,
|
||||
GetPendingApprovalsResponse, GetRestoreHeightArgs, GetSwapInfoArgs,
|
||||
GetSwapInfosAllArgs, ListSellersArgs, MoneroRecoveryArgs, RedactArgs,
|
||||
ExportBitcoinWalletArgs, GetBitcoinAddressArgs, GetCurrentSwapArgs, GetDataDirArgs,
|
||||
GetHistoryArgs, GetLogsArgs, GetMoneroAddressesArgs, GetMoneroBalanceArgs,
|
||||
GetMoneroHistoryArgs, GetMoneroMainAddressArgs, GetMoneroSeedArgs,
|
||||
GetMoneroSyncProgressArgs, GetPendingApprovalsResponse, GetRestoreHeightArgs,
|
||||
GetSwapInfoArgs, GetSwapInfosAllArgs, ListSellersArgs, MoneroRecoveryArgs, RedactArgs,
|
||||
RejectApprovalArgs, RejectApprovalResponse, ResolveApprovalArgs, ResumeSwapArgs,
|
||||
SendMoneroArgs, SetRestoreHeightArgs, SuspendCurrentSwapArgs, WithdrawBtcArgs,
|
||||
},
|
||||
|
|
@ -35,6 +35,7 @@ macro_rules! generate_command_handlers {
|
|||
() => {
|
||||
tauri::generate_handler![
|
||||
get_balance,
|
||||
get_bitcoin_address,
|
||||
get_monero_addresses,
|
||||
get_swap_info,
|
||||
get_swap_infos_all,
|
||||
|
|
@ -435,6 +436,7 @@ tauri_command!(send_monero, SendMoneroArgs);
|
|||
tauri_command!(change_monero_node, ChangeMoneroNodeArgs);
|
||||
|
||||
// These commands require no arguments
|
||||
tauri_command!(get_bitcoin_address, GetBitcoinAddressArgs, no_args);
|
||||
tauri_command!(get_wallet_descriptor, ExportBitcoinWalletArgs, no_args);
|
||||
tauri_command!(suspend_current_swap, SuspendCurrentSwapArgs, no_args);
|
||||
tauri_command!(get_swap_info, GetSwapInfoArgs);
|
||||
|
|
|
|||
|
|
@ -279,6 +279,30 @@ impl Request for BalanceArgs {
|
|||
}
|
||||
}
|
||||
|
||||
// GetBitcoinAddress
|
||||
#[typeshare]
|
||||
#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct GetBitcoinAddressArgs;
|
||||
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct GetBitcoinAddressResponse {
|
||||
#[typeshare(serialized_as = "string")]
|
||||
#[serde(with = "swap_serde::bitcoin::address_serde")]
|
||||
pub address: bitcoin::Address,
|
||||
}
|
||||
|
||||
impl Request for GetBitcoinAddressArgs {
|
||||
type Response = GetBitcoinAddressResponse;
|
||||
|
||||
async fn request(self, ctx: Arc<Context>) -> Result<Self::Response> {
|
||||
let bitcoin_wallet = ctx.try_get_bitcoin_wallet().await?;
|
||||
let address = bitcoin_wallet.new_address().await?;
|
||||
|
||||
Ok(GetBitcoinAddressResponse { address })
|
||||
}
|
||||
}
|
||||
|
||||
// GetHistory
|
||||
#[typeshare]
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue