mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2025-07-29 17:48:43 -04:00
wip: WithdrawDialog migrated to Tauri IPC
This commit is contained in:
parent
92034a5be8
commit
47821cbe79
14 changed files with 185 additions and 166 deletions
|
@ -6,6 +6,7 @@ import { ReactNode, useEffect, useState } from "react";
|
|||
interface IpcInvokeButtonProps<T> {
|
||||
onSuccess?: (data: T) => void;
|
||||
onClick: () => Promise<T>;
|
||||
onPendingChange?: (bool) => void;
|
||||
isLoadingOverride?: boolean;
|
||||
isIconButton?: boolean;
|
||||
loadIcon?: ReactNode;
|
||||
|
@ -24,26 +25,22 @@ export default function PromiseInvokeButton<T>({
|
|||
isIconButton,
|
||||
displayErrorSnackbar,
|
||||
tooltipTitle,
|
||||
onPendingChange,
|
||||
...rest
|
||||
}: IpcInvokeButtonProps<T> & ButtonProps) {
|
||||
const { enqueueSnackbar } = useSnackbar();
|
||||
|
||||
const [isPending, setIsPending] = useState(false);
|
||||
const [hasMinLoadingTimePassed, setHasMinLoadingTimePassed] = useState(false);
|
||||
|
||||
const isLoading = (isPending && hasMinLoadingTimePassed) || isLoadingOverride;
|
||||
const isLoading = isPending || isLoadingOverride;
|
||||
const actualEndIcon = isLoading
|
||||
? loadIcon || <CircularProgress size="1em" />
|
||||
: endIcon;
|
||||
|
||||
useEffect(() => {
|
||||
setHasMinLoadingTimePassed(false);
|
||||
setTimeout(() => setHasMinLoadingTimePassed(true), 100);
|
||||
}, [isPending]);
|
||||
|
||||
async function handleClick(event: React.MouseEvent<HTMLButtonElement>) {
|
||||
if (!isPending) {
|
||||
try {
|
||||
onPendingChange?.(true);
|
||||
setIsPending(true);
|
||||
let result = await onClick();
|
||||
onSuccess?.(result);
|
||||
|
@ -56,32 +53,23 @@ export default function PromiseInvokeButton<T>({
|
|||
}
|
||||
} finally {
|
||||
setIsPending(false);
|
||||
onPendingChange?.(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const isDisabled = disabled || isLoading;
|
||||
|
||||
return (
|
||||
<Tooltip title={tooltipTitle}>
|
||||
<span>
|
||||
{isIconButton ? (
|
||||
<IconButton
|
||||
onClick={handleClick}
|
||||
disabled={isDisabled}
|
||||
{...(rest as any)}
|
||||
>
|
||||
{actualEndIcon}
|
||||
</IconButton>
|
||||
) : (
|
||||
<Button
|
||||
onClick={handleClick}
|
||||
disabled={isDisabled}
|
||||
endIcon={actualEndIcon}
|
||||
{...rest}
|
||||
/>
|
||||
)}
|
||||
</span>
|
||||
</Tooltip>
|
||||
return isIconButton ? (
|
||||
<IconButton onClick={handleClick} disabled={isDisabled} {...(rest as any)}>
|
||||
{actualEndIcon}
|
||||
</IconButton>
|
||||
) : (
|
||||
<Button
|
||||
onClick={handleClick}
|
||||
disabled={isDisabled}
|
||||
endIcon={actualEndIcon}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,9 +1,14 @@
|
|||
import { Dialog } from '@material-ui/core';
|
||||
import { useAppDispatch, useIsRpcEndpointBusy } from 'store/hooks';
|
||||
import { RpcMethod } from 'models/rpcModel';
|
||||
import { rpcResetWithdrawTxId } from 'store/features/rpcSlice';
|
||||
import WithdrawStatePage from './WithdrawStatePage';
|
||||
import DialogHeader from '../DialogHeader';
|
||||
import { Button, Dialog, DialogActions } from "@material-ui/core";
|
||||
import { useAppDispatch, useIsRpcEndpointBusy } from "store/hooks";
|
||||
import { RpcMethod } from "models/rpcModel";
|
||||
import { rpcResetWithdrawTxId } from "store/features/rpcSlice";
|
||||
import WithdrawStatePage from "./WithdrawStatePage";
|
||||
import DialogHeader from "../DialogHeader";
|
||||
import PromiseInvokeButton from "renderer/components/PromiseInvokeButton";
|
||||
import { useState } from "react";
|
||||
import { withdrawBtc } from "renderer/rpc";
|
||||
import BtcTxInMempoolPageContent from "./pages/BitcoinWithdrawTxInMempoolPage";
|
||||
import AddressInputPage from "./pages/AddressInputPage";
|
||||
|
||||
export default function WithdrawDialog({
|
||||
open,
|
||||
|
@ -12,23 +17,60 @@ export default function WithdrawDialog({
|
|||
open: boolean;
|
||||
onClose: () => void;
|
||||
}) {
|
||||
const isRpcEndpointBusy = useIsRpcEndpointBusy(RpcMethod.WITHDRAW_BTC);
|
||||
const dispatch = useAppDispatch();
|
||||
const [pending, setPending] = useState(false);
|
||||
const [withdrawTxId, setWithdrawTxId] = useState<string | null>(null);
|
||||
const [withdrawAddressValid, setWithdrawAddressValid] = useState(false);
|
||||
const [withdrawAddress, setWithdrawAddress] = useState<string>("");
|
||||
|
||||
function onCancel() {
|
||||
if (!isRpcEndpointBusy) {
|
||||
if (!pending) {
|
||||
setWithdrawTxId(null);
|
||||
setWithdrawAddress("");
|
||||
onClose();
|
||||
dispatch(rpcResetWithdrawTxId());
|
||||
}
|
||||
}
|
||||
|
||||
// This prevents an issue where the Dialog is shown for a split second without a present withdraw state
|
||||
if (!open && !isRpcEndpointBusy) return null;
|
||||
if (!open) return null;
|
||||
|
||||
return (
|
||||
<Dialog open onClose={onCancel} maxWidth="sm" fullWidth>
|
||||
<DialogHeader title="Withdraw Bitcoin" />
|
||||
<WithdrawStatePage onCancel={onCancel} />
|
||||
{withdrawTxId === null ? (
|
||||
<AddressInputPage
|
||||
setWithdrawAddress={setWithdrawAddress}
|
||||
withdrawAddress={withdrawAddress}
|
||||
setWithdrawAddressValid={setWithdrawAddressValid}
|
||||
/>
|
||||
) : (
|
||||
<BtcTxInMempoolPageContent
|
||||
withdrawTxId={withdrawTxId}
|
||||
onCancel={onCancel}
|
||||
/>
|
||||
)}
|
||||
<DialogActions>
|
||||
{withdrawTxId === null ? (
|
||||
<PromiseInvokeButton
|
||||
variant="contained"
|
||||
color="primary"
|
||||
disabled={!withdrawAddressValid}
|
||||
onClick={() => withdrawBtc(withdrawAddress)}
|
||||
onPendingChange={(pending) => {
|
||||
console.log("pending", pending);
|
||||
setPending(pending);
|
||||
}}
|
||||
onSuccess={(txId) => {
|
||||
setWithdrawTxId(txId);
|
||||
}}
|
||||
>
|
||||
Withdraw
|
||||
</PromiseInvokeButton>
|
||||
) : (
|
||||
<Button onClick={onCancel} color="primary" disabled={pending}>
|
||||
Close
|
||||
</Button>
|
||||
)}
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
import { useState } from 'react';
|
||||
import { Button, DialogActions, DialogContentText } from '@material-ui/core';
|
||||
import BitcoinAddressTextField from '../../../inputs/BitcoinAddressTextField';
|
||||
import WithdrawDialogContent from '../WithdrawDialogContent';
|
||||
import IpcInvokeButton from '../../../IpcInvokeButton';
|
||||
import { useState } from "react";
|
||||
import { Button, DialogActions, DialogContentText } from "@material-ui/core";
|
||||
import BitcoinAddressTextField from "../../../inputs/BitcoinAddressTextField";
|
||||
import WithdrawDialogContent from "../WithdrawDialogContent";
|
||||
import IpcInvokeButton from "../../../IpcInvokeButton";
|
||||
|
||||
export default function AddressInputPage({
|
||||
onCancel,
|
||||
withdrawAddress,
|
||||
setWithdrawAddress,
|
||||
setWithdrawAddressValid,
|
||||
}: {
|
||||
onCancel: () => void;
|
||||
withdrawAddress: string;
|
||||
setWithdrawAddress: (address: string) => void;
|
||||
setWithdrawAddressValid: (valid: boolean) => void;
|
||||
}) {
|
||||
const [withdrawAddressValid, setWithdrawAddressValid] = useState(false);
|
||||
const [withdrawAddress, setWithdrawAddress] = useState('');
|
||||
|
||||
return (
|
||||
<>
|
||||
<WithdrawDialogContent>
|
||||
|
@ -28,22 +29,6 @@ export default function AddressInputPage({
|
|||
fullWidth
|
||||
/>
|
||||
</WithdrawDialogContent>
|
||||
|
||||
<DialogActions>
|
||||
<Button onClick={onCancel} variant="text">
|
||||
Cancel
|
||||
</Button>
|
||||
<IpcInvokeButton
|
||||
disabled={!withdrawAddressValid}
|
||||
ipcChannel="spawn-withdraw-btc"
|
||||
ipcArgs={[withdrawAddress]}
|
||||
color="primary"
|
||||
variant="contained"
|
||||
requiresRpc
|
||||
>
|
||||
Withdraw
|
||||
</IpcInvokeButton>
|
||||
</DialogActions>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Button, DialogActions, DialogContentText } from '@material-ui/core';
|
||||
import BitcoinTransactionInfoBox from '../../swap/BitcoinTransactionInfoBox';
|
||||
import WithdrawDialogContent from '../WithdrawDialogContent';
|
||||
import { Button, DialogActions, DialogContentText } from "@material-ui/core";
|
||||
import BitcoinTransactionInfoBox from "../../swap/BitcoinTransactionInfoBox";
|
||||
import WithdrawDialogContent from "../WithdrawDialogContent";
|
||||
|
||||
export default function BtcTxInMempoolPageContent({
|
||||
withdrawTxId,
|
||||
|
@ -23,14 +23,6 @@ export default function BtcTxInMempoolPageContent({
|
|||
additionalContent={null}
|
||||
/>
|
||||
</WithdrawDialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={onCancel} variant="text">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button onClick={onCancel} color="primary" variant="contained">
|
||||
Done
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
import { Button, DialogActions } from '@material-ui/core';
|
||||
import CircularProgressWithSubtitle from '../../swap/CircularProgressWithSubtitle';
|
||||
import WithdrawDialogContent from '../WithdrawDialogContent';
|
||||
|
||||
export default function InitiatedPage({ onCancel }: { onCancel: () => void }) {
|
||||
return (
|
||||
<>
|
||||
<WithdrawDialogContent>
|
||||
<CircularProgressWithSubtitle description="Withdrawing Bitcoin" />
|
||||
</WithdrawDialogContent>
|
||||
<DialogActions>
|
||||
<Button onClick={onCancel} variant="text">
|
||||
Cancel
|
||||
</Button>
|
||||
<Button disabled color="primary" variant="contained">
|
||||
Done
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -3,7 +3,6 @@ import { store } from "./store/storeRenderer";
|
|||
import { rpcSetBalance, rpcSetSwapInfo } from "store/features/rpcSlice";
|
||||
|
||||
export async function checkBitcoinBalance() {
|
||||
// TODO: use tauri-bindgen here
|
||||
const response = (await invoke("get_balance")) as {
|
||||
balance: number;
|
||||
};
|
||||
|
@ -16,3 +15,17 @@ export async function getRawSwapInfos() {
|
|||
|
||||
(response as any[]).forEach((info) => store.dispatch(rpcSetSwapInfo(info)));
|
||||
}
|
||||
|
||||
export async function withdrawBtc(address: string): Promise<string> {
|
||||
const response = (await invoke("withdraw_btc", {
|
||||
args: {
|
||||
address,
|
||||
amount: null,
|
||||
},
|
||||
})) as {
|
||||
txid: string;
|
||||
amount: number;
|
||||
};
|
||||
|
||||
return response.txid;
|
||||
}
|
||||
|
|
|
@ -1,18 +1,15 @@
|
|||
import { ExtendedProviderStatus } from 'models/apiModel';
|
||||
import { ExtendedProviderStatus } from "models/apiModel";
|
||||
|
||||
export const isTestnet = () =>
|
||||
false
|
||||
export const isTestnet = () => true;
|
||||
|
||||
export const isExternalRpc = () =>
|
||||
true
|
||||
export const isExternalRpc = () => true;
|
||||
|
||||
export const isDevelopment =
|
||||
true
|
||||
export const isDevelopment = true;
|
||||
|
||||
export function getStubTestnetProvider(): ExtendedProviderStatus | null {
|
||||
return null;
|
||||
}
|
||||
|
||||
export const getPlatform = () => {
|
||||
return 'mac';
|
||||
return "mac";
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue