wip: WithdrawDialog migrated to Tauri IPC

This commit is contained in:
binarybaron 2024-08-09 19:03:28 +02:00
parent 92034a5be8
commit 47821cbe79
No known key found for this signature in database
GPG key ID: 99B75D3E1476A26E
14 changed files with 185 additions and 166 deletions

View file

@ -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}
/>
);
}

View file

@ -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>
);
}

View file

@ -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>
</>
);
}

View file

@ -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>
</>
);
}

View file

@ -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>
</>
);
}

View file

@ -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;
}

View file

@ -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";
};