feat(gui): Refund swap in the background (#154)

Swaps will now be refunded as soon as the cancel timelock expires if the GUI is running but the swap dialog is not open.
This commit is contained in:
binarybaron 2024-11-14 14:20:22 +01:00 committed by GitHub
parent 4cf5cf719a
commit e46be4a9ff
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 210 additions and 27 deletions

View file

@ -26,6 +26,7 @@
"@tauri-apps/plugin-shell": "^2.0.0",
"@tauri-apps/plugin-store": "^2.1.0",
"@tauri-apps/plugin-updater": "^2.0.0",
"@types/react-redux": "^7.1.34",
"humanize-duration": "^3.32.1",
"lodash": "^4.17.21",
"multiaddr": "^10.0.1",
@ -36,7 +37,7 @@
"react-dom": "^18.2.0",
"react-qr-code": "^2.0.15",
"react-redux": "^9.1.2",
"react-router-dom": "^6.24.1",
"react-router-dom": "^6.28.0",
"redux-persist": "^6.0.0",
"semver": "^7.6.2",
"virtua": "^0.33.2"

View file

@ -0,0 +1,42 @@
import { BackgroundRefundState } from "models/tauriModel";
import { useAppSelector } from "store/hooks";
import { LoadingSpinnerAlert } from "./LoadingSpinnerAlert";
import { AlertTitle } from "@material-ui/lab";
import TruncatedText from "../other/TruncatedText";
import { useSnackbar } from "notistack";
import { useEffect } from "react";
export default function BackgroundRefundAlert() {
const backgroundRefund = useAppSelector(state => state.rpc.state.backgroundRefund);
const notistack = useSnackbar();
useEffect(() => {
// If we failed to refund, show a notification
if (backgroundRefund?.state.type === "Failed") {
notistack.enqueueSnackbar(
<>
Our attempt to refund {backgroundRefund.swapId} in the background failed.
<br />
Error: {backgroundRefund.state.content.error}
</>,
{ variant: "error", autoHideDuration: 60 * 1000 }
);
}
// If we successfully refunded, show a notification as well
if (backgroundRefund?.state.type === "Completed") {
notistack.enqueueSnackbar(`The swap ${backgroundRefund.swapId} has been refunded in the background.`, { variant: "success", persist: true });
}
}, [backgroundRefund]);
if (backgroundRefund?.state.type === "Started") {
return <LoadingSpinnerAlert>
<AlertTitle>
Refund in progress
</AlertTitle>
The swap <TruncatedText>{backgroundRefund.swapId}</TruncatedText> is being refunded in the background.
</LoadingSpinnerAlert>
}
return null;
}

View file

@ -1,13 +1,10 @@
import { Button, CircularProgress } from "@material-ui/core";
import { Alert, AlertProps } from "@material-ui/lab";
import { Button } from "@material-ui/core";
import { Alert } from "@material-ui/lab";
import { TauriContextInitializationProgress } from "models/tauriModel";
import { useNavigate } from "react-router-dom";
import { useAppSelector } from "store/hooks";
import { exhaustiveGuard } from "utils/typescriptUtils";
function LoadingSpinnerAlert({ ...rest }: AlertProps) {
return <Alert icon={<CircularProgress size={22} />} {...rest} />;
}
import { LoadingSpinnerAlert } from "./LoadingSpinnerAlert";
export default function DaemonStatusAlert() {
const contextStatus = useAppSelector((s) => s.rpc.status);

View file

@ -0,0 +1,6 @@
import { CircularProgress } from "@material-ui/core";
import { AlertProps, Alert } from "@material-ui/lab";
export function LoadingSpinnerAlert({ ...rest }: AlertProps) {
return <Alert icon={<CircularProgress size={22} />} {...rest} />;
}

View file

@ -8,6 +8,7 @@ import UnfinishedSwapsAlert from "../alert/UnfinishedSwapsAlert";
import DiscordIcon from "../icons/DiscordIcon";
import LinkIconButton from "../icons/LinkIconButton";
import { DISCORD_URL } from "../pages/help/ContactInfoBox";
import BackgroundRefundAlert from "../alert/BackgroundRefundAlert";
const useStyles = makeStyles((theme) => ({
outer: {
@ -29,6 +30,7 @@ export default function NavigationFooter() {
<Box className={classes.outer}>
<FundsLeftInWalletAlert />
<UnfinishedSwapsAlert />
<BackgroundRefundAlert />
<DaemonStatusAlert />
<MoneroWalletRpcUpdatingAlert />
<Box className={classes.linksOuter}>

View file

@ -28,10 +28,12 @@ import {
CheckElectrumNodeArgs,
CheckElectrumNodeResponse,
GetMoneroAddressesResponse,
TauriBackgroundRefundEvent,
} from "models/tauriModel";
import {
contextStatusEventReceived,
receivedCliLog,
rpcSetBackgroundRefundState,
rpcSetBalance,
rpcSetSwapInfo,
timelockChangeEventReceived,
@ -100,6 +102,11 @@ export async function initEventListeners() {
console.log('Received timelock change event', event.payload);
store.dispatch(timelockChangeEventReceived(event.payload));
})
listen<TauriBackgroundRefundEvent>('background-refund', (event) => {
console.log('Received background refund event', event.payload);
store.dispatch(rpcSetBackgroundRefundState(event.payload));
})
}
async function invoke<ARGS, RESPONSE>(

View file

@ -5,6 +5,7 @@ import {
GetSwapInfoResponse,
TauriContextStatusEvent,
TauriTimelockChangeEvent,
BackgroundRefundState,
} from "models/tauriModel";
import { MoneroRecoveryResponse } from "../../models/rpcModel";
import { GetSwapInfoResponseExt } from "models/tauriModelExt";
@ -27,6 +28,10 @@ interface State {
// TODO: Reimplement this using Tauri
updateState: false;
};
backgroundRefund: {
swapId: string;
state: BackgroundRefundState;
} | null;
}
export interface RPCSlice {
@ -46,6 +51,7 @@ const initialState: RPCSlice = {
moneroWalletRpc: {
updateState: false,
},
backgroundRefund: null,
},
logs: [],
};
@ -109,6 +115,12 @@ export const rpcSlice = createSlice({
rpcResetMoneroRecoveryKeys(slice) {
slice.state.moneroRecovery = null;
},
rpcSetBackgroundRefundState(slice, action: PayloadAction<{ swap_id: string, state: BackgroundRefundState }>) {
slice.state.backgroundRefund = {
swapId: action.payload.swap_id,
state: action.payload.state,
};
},
},
});
@ -122,6 +134,7 @@ export const {
rpcSetSwapInfo,
rpcSetMoneroRecoveryKeys,
rpcResetMoneroRecoveryKeys,
rpcSetBackgroundRefundState,
timelockChangeEventReceived
} = rpcSlice.actions;

View file

@ -192,6 +192,13 @@
dependencies:
regenerator-runtime "^0.14.0"
"@babel/runtime@^7.9.2":
version "7.26.0"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.26.0.tgz#8600c2f595f277c60815256418b85356a65173c1"
integrity sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==
dependencies:
regenerator-runtime "^0.14.0"
"@babel/template@^7.24.7":
version "7.24.7"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.24.7.tgz#02efcee317d0609d2c07117cb70ef8fb17ab7315"
@ -552,10 +559,10 @@
redux-thunk "^3.1.0"
reselect "^5.1.0"
"@remix-run/router@1.17.1":
version "1.17.1"
resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.17.1.tgz#bf93997beb81863fde042ebd05013a2618471362"
integrity sha512-mCOMec4BKd6BRGBZeSnGiIgwsbLGp3yhVqAD8H+PxiRNEHgDpZb8J1TnrSDlg97t0ySKMQJTHCWBCmBpSmkF6Q==
"@remix-run/router@1.21.0":
version "1.21.0"
resolved "https://registry.yarnpkg.com/@remix-run/router/-/router-1.21.0.tgz#c65ae4262bdcfe415dbd4f64ec87676e4a56e2b5"
integrity sha512-xfSkCAchbdG5PnbrKqFWwia4Bi61nH+wm8wLEqfHDyp7Y3dZzgqS2itV8i4gAq9pC2HsTpwyBC6Ds8VHZ96JlA==
"@rollup/plugin-virtual@^3.0.2":
version "3.0.2"
@ -971,6 +978,14 @@
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50"
integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==
"@types/hoist-non-react-statics@^3.3.0":
version "3.3.5"
resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.5.tgz#dab7867ef789d87e2b4b0003c9d65c49cc44a494"
integrity sha512-SbcrWzkKBw2cdwRTwQAswfpB9g9LJWfjtUeW/jvNwbhC8cpmmNYVePa+ncbUe0rGTQ7G3Ff6mYUN2VMfLVr+Sg==
dependencies:
"@types/react" "*"
hoist-non-react-statics "^3.3.0"
"@types/humanize-duration@^3.27.4":
version "3.27.4"
resolved "https://registry.yarnpkg.com/@types/humanize-duration/-/humanize-duration-3.27.4.tgz#51d6d278213374735440bc3749de920935e9127e"
@ -1000,6 +1015,16 @@
dependencies:
"@types/react" "*"
"@types/react-redux@^7.1.34":
version "7.1.34"
resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.34.tgz#83613e1957c481521e6776beeac4fd506d11bd0e"
integrity sha512-GdFaVjEbYv4Fthm2ZLvj1VSCedV7TqE5y1kNwnjSdBOTXuRSgowux6J8TAct15T3CKBr63UMk+2CO7ilRhyrAQ==
dependencies:
"@types/hoist-non-react-statics" "^3.3.0"
"@types/react" "*"
hoist-non-react-statics "^3.3.0"
redux "^4.0.0"
"@types/react-transition-group@^4.2.0":
version "4.4.10"
resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.10.tgz#6ee71127bdab1f18f11ad8fb3322c6da27c327ac"
@ -2221,7 +2246,7 @@ help-me@^5.0.0:
resolved "https://registry.yarnpkg.com/help-me/-/help-me-5.0.0.tgz#b1ebe63b967b74060027c2ac61f9be12d354a6f6"
integrity sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==
hoist-non-react-statics@^3.3.2:
hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
@ -3139,20 +3164,20 @@ react-refresh@^0.14.2:
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.2.tgz#3833da01ce32da470f1f936b9d477da5c7028bf9"
integrity sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==
react-router-dom@^6.24.1:
version "6.24.1"
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.24.1.tgz#b1a22f7d6c5a1bfce30732bd370713f991ab4de4"
integrity sha512-U19KtXqooqw967Vw0Qcn5cOvrX5Ejo9ORmOtJMzYWtCT4/WOfFLIZGGsVLxcd9UkBO0mSTZtXqhZBsWlHr7+Sg==
react-router-dom@^6.28.0:
version "6.28.0"
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-6.28.0.tgz#f73ebb3490e59ac9f299377062ad1d10a9f579e6"
integrity sha512-kQ7Unsl5YdyOltsPGl31zOjLrDv+m2VcIEcIHqYYD3Lp0UppLjrzcfJqDJwXxFw3TH/yvapbnUvPlAj7Kx5nbg==
dependencies:
"@remix-run/router" "1.17.1"
react-router "6.24.1"
"@remix-run/router" "1.21.0"
react-router "6.28.0"
react-router@6.24.1:
version "6.24.1"
resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.24.1.tgz#5a3bbba0000afba68d42915456ca4c806f37a7de"
integrity sha512-PTXFXGK2pyXpHzVo3rR9H7ip4lSPZZc0bHG5CARmj65fTT6qG7sTngmb6lcYu1gf3y/8KxORoy9yn59pGpCnpg==
react-router@6.28.0:
version "6.28.0"
resolved "https://registry.yarnpkg.com/react-router/-/react-router-6.28.0.tgz#29247c86d7ba901d7e5a13aa79a96723c3e59d0d"
integrity sha512-HrYdIFqdrnhDw0PqG/AKjAqEqM7AvxCz0DQ4h2W8k6nqmc5uRBYDag0SBxx9iYz5G8gnuNVLzUe13wl9eAsXXg==
dependencies:
"@remix-run/router" "1.17.1"
"@remix-run/router" "1.21.0"
react-transition-group@^4.4.0:
version "4.4.5"
@ -3204,6 +3229,13 @@ redux-thunk@^3.1.0:
resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-3.1.0.tgz#94aa6e04977c30e14e892eae84978c1af6058ff3"
integrity sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==
redux@^4.0.0:
version "4.2.1"
resolved "https://registry.yarnpkg.com/redux/-/redux-4.2.1.tgz#c08f4306826c49b5e9dc901dee0452ea8fce6197"
integrity sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==
dependencies:
"@babel/runtime" "^7.9.2"
redux@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/redux/-/redux-5.0.1.tgz#97fa26881ce5746500125585d5642c77b6e9447b"