diff --git a/.cursor/rules/never-upgrade-deps-without-explicit-confirmation.mdc b/.cursor/rules/never-upgrade-deps-without-explicit-confirmation.mdc
new file mode 100644
index 0000000000..5a41e7350d
--- /dev/null
+++ b/.cursor/rules/never-upgrade-deps-without-explicit-confirmation.mdc
@@ -0,0 +1,6 @@
+---
+description:
+globs:
+alwaysApply: false
+---
+NEVER NEVER NEVER upgrade or changr the versions used in dependencies without explicitly asking for confirmation!!
\ No newline at end of file
diff --git a/Cargo.lock b/Cargo.lock
index 2a05fef88f..ccce6d561f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -7591,15 +7591,6 @@ dependencies = [
"thiserror 1.0.69",
]
-[[package]]
-name = "qrcode"
-version = "0.14.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d68782463e408eb1e668cf6152704bd856c78c5b6417adaee3203d8f4c1fc9ec"
-dependencies = [
- "image",
-]
-
[[package]]
name = "quick-error"
version = "1.2.3"
@@ -9823,7 +9814,6 @@ dependencies = [
"once_cell",
"pem",
"proptest",
- "qrcode",
"rand 0.8.5",
"rand_chacha 0.3.1",
"regex",
@@ -10806,6 +10796,8 @@ dependencies = [
"futures-core",
"futures-io",
"futures-sink",
+ "futures-util",
+ "hashbrown 0.15.4",
"pin-project-lite",
"tokio",
]
diff --git a/src-gui/package.json b/src-gui/package.json
index 72e2424c28..fa99b61f1e 100644
--- a/src-gui/package.json
+++ b/src-gui/package.json
@@ -32,6 +32,7 @@
"@tauri-apps/plugin-store": "^2.0.0",
"@tauri-apps/plugin-updater": "2.7.1",
"@types/react-redux": "^7.1.34",
+ "boring-avatars": "^1.11.2",
"humanize-duration": "^3.32.1",
"jdenticon": "^3.3.0",
"lodash": "^4.17.21",
diff --git a/src-gui/src/models/tauriModelExt.ts b/src-gui/src/models/tauriModelExt.ts
index 6af467e5f6..d5c5a57366 100644
--- a/src-gui/src/models/tauriModelExt.ts
+++ b/src-gui/src/models/tauriModelExt.ts
@@ -4,6 +4,8 @@ import {
ExpiredTimelocks,
GetSwapInfoResponse,
PendingCompleted,
+ QuoteWithAddress,
+ SelectMakerDetails,
TauriBackgroundProgress,
TauriSwapProgressEvent,
} from "./tauriModel";
@@ -303,3 +305,49 @@ export function isBitcoinSyncProgress(
): progress is TauriBitcoinSyncProgress {
return progress.componentName === "SyncingBitcoinWallet";
}
+
+export type PendingSelectMakerApprovalRequest = PendingApprovalRequest & {
+ request: { type: "SelectMaker"; content: SelectMakerDetails };
+};
+
+export interface SortableQuoteWithAddress extends QuoteWithAddress {
+ expiration_ts?: number;
+ request_id?: string;
+}
+
+export function isPendingSelectMakerApprovalEvent(
+ event: ApprovalRequest,
+): event is PendingSelectMakerApprovalRequest {
+ // Check if the request is pending
+ if (event.request_status.state !== "Pending") {
+ return false;
+ }
+
+ // Check if the request is a SelectMaker request
+ return event.request.type === "SelectMaker";
+}
+
+/**
+ * Checks if any funds have been locked yet based on the swap progress event
+ * Returns true for events where funds have been locked
+ * @param event The TauriSwapProgressEvent to check
+ * @returns True if funds have been locked, false otherwise
+ */
+export function haveFundsBeenLocked(
+ event: TauriSwapProgressEvent | null,
+): boolean {
+ if (event === null) {
+ return false;
+ }
+
+ switch (event.type) {
+ case "RequestingQuote":
+ case "Resuming":
+ case "ReceivedQuote":
+ case "WaitingForBtcDeposit":
+ case "SwapSetupInflight":
+ return false;
+ }
+
+ return true;
+}
diff --git a/src-gui/src/renderer/components/alert/RemainingFundsWillBeUsedAlert.tsx b/src-gui/src/renderer/components/alert/RemainingFundsWillBeUsedAlert.tsx
deleted file mode 100644
index 25fd4c0d9a..0000000000
--- a/src-gui/src/renderer/components/alert/RemainingFundsWillBeUsedAlert.tsx
+++ /dev/null
@@ -1,26 +0,0 @@
-import { Box } from "@mui/material";
-import { Alert } from "@mui/material";
-import { useAppSelector } from "store/hooks";
-import { SatsAmount } from "../other/Units";
-import WalletRefreshButton from "../pages/wallet/WalletRefreshButton";
-
-export default function RemainingFundsWillBeUsedAlert() {
- const balance = useAppSelector((s) => s.rpc.state.balance);
-
- if (balance == null || balance <= 0) {
- return <>>;
- }
-
- return (
-
- }
- variant="filled"
- >
- The remaining funds of in the wallet
- will be used for the next swap
-
-
- );
-}
diff --git a/src-gui/src/renderer/components/alert/SwapStatusAlert/SwapStatusAlert.tsx b/src-gui/src/renderer/components/alert/SwapStatusAlert/SwapStatusAlert.tsx
index 17cfdee49f..cec3719903 100644
--- a/src-gui/src/renderer/components/alert/SwapStatusAlert/SwapStatusAlert.tsx
+++ b/src-gui/src/renderer/components/alert/SwapStatusAlert/SwapStatusAlert.tsx
@@ -14,6 +14,7 @@ import HumanizedBitcoinBlockDuration from "../../other/HumanizedBitcoinBlockDura
import TruncatedText from "../../other/TruncatedText";
import { SwapMoneroRecoveryButton } from "../../pages/history/table/SwapMoneroRecoveryButton";
import { TimelockTimeline } from "./TimelockTimeline";
+import { useIsSpecificSwapRunning } from "store/hooks";
/**
* Component for displaying a list of messages.
@@ -233,13 +234,15 @@ const UNUSUAL_AMOUNT_OF_TIME_HAS_PASSED_THRESHOLD = 72 - 4;
*/
export default function SwapStatusAlert({
swap,
- isRunning,
onlyShowIfUnusualAmountOfTimeHasPassed,
}: {
swap: GetSwapInfoResponseExt;
- isRunning: boolean;
onlyShowIfUnusualAmountOfTimeHasPassed?: boolean;
}) {
+ if (swap == null) {
+ return null;
+ }
+
// If the swap is completed, we do not need to display anything
if (!isGetSwapInfoResponseRunningSwap(swap)) {
return null;
@@ -250,16 +253,18 @@ export default function SwapStatusAlert({
return null;
}
- // If we are only showing if an unusual amount of time has passed, we need to check if the swap has been running for a while
- if (
- onlyShowIfUnusualAmountOfTimeHasPassed &&
+ const hasUnusualAmountOfTimePassed =
swap.timelock.type === "None" &&
swap.timelock.content.blocks_left >
- UNUSUAL_AMOUNT_OF_TIME_HAS_PASSED_THRESHOLD
- ) {
+ UNUSUAL_AMOUNT_OF_TIME_HAS_PASSED_THRESHOLD;
+
+ // If we are only showing if an unusual amount of time has passed, we need to check if the swap has been running for a while
+ if (onlyShowIfUnusualAmountOfTimeHasPassed && hasUnusualAmountOfTimePassed) {
return null;
}
+ const isRunning = useIsSpecificSwapRunning(swap.swap_id);
+
return (
{isRunning ? (
- "Swap has been running for a while"
+ hasUnusualAmountOfTimePassed ? (
+ "Swap has been running for a while"
+ ) : (
+ "Swap is running"
+ )
) : (
<>
Swap {swap.swap_id} is not running
diff --git a/src-gui/src/renderer/components/alert/SwapTxLockAlertsBox.tsx b/src-gui/src/renderer/components/alert/SwapTxLockAlertsBox.tsx
index 4c2791813b..dcdf065f7d 100644
--- a/src-gui/src/renderer/components/alert/SwapTxLockAlertsBox.tsx
+++ b/src-gui/src/renderer/components/alert/SwapTxLockAlertsBox.tsx
@@ -11,7 +11,7 @@ export default function SwapTxLockAlertsBox() {
return (
{swaps.map((swap) => (
-
+
))}
);
diff --git a/src-gui/src/renderer/components/inputs/MoneroAddressTextField.tsx b/src-gui/src/renderer/components/inputs/MoneroAddressTextField.tsx
index 6db1e72294..e0b570c2bb 100644
--- a/src-gui/src/renderer/components/inputs/MoneroAddressTextField.tsx
+++ b/src-gui/src/renderer/components/inputs/MoneroAddressTextField.tsx
@@ -53,6 +53,9 @@ export default function MoneroAddressTextField({
setAddresses(response.addresses);
};
fetchAddresses();
+
+ const interval = setInterval(fetchAddresses, 5000);
+ return () => clearInterval(interval);
}, []);
// Event handlers
diff --git a/src-gui/src/renderer/components/modal/SwapSuspendAlert.tsx b/src-gui/src/renderer/components/modal/SwapSuspendAlert.tsx
index d375f35bf7..51b3717965 100644
--- a/src-gui/src/renderer/components/modal/SwapSuspendAlert.tsx
+++ b/src-gui/src/renderer/components/modal/SwapSuspendAlert.tsx
@@ -5,7 +5,13 @@ import {
DialogContent,
DialogContentText,
DialogTitle,
+ List,
+ ListItem,
+ ListItemIcon,
+ ListItemText,
+ Typography,
} from "@mui/material";
+import CircleIcon from "@mui/icons-material/Circle";
import { suspendCurrentSwap } from "renderer/rpc";
import PromiseInvokeButton from "../PromiseInvokeButton";
@@ -20,10 +26,42 @@ export default function SwapSuspendAlert({
}: SwapCancelAlertProps) {
return (
diff --git a/src-gui/src/renderer/components/modal/swap/BitcoinQrCode.tsx b/src-gui/src/renderer/components/modal/swap/BitcoinQrCode.tsx
deleted file mode 100644
index e9fa7bf651..0000000000
--- a/src-gui/src/renderer/components/modal/swap/BitcoinQrCode.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import { Box } from "@mui/material";
-import QRCode from "react-qr-code";
-
-export default function BitcoinQrCode({ address }: { address: string }) {
- return (
-
-
-
- );
-}
diff --git a/src-gui/src/renderer/components/modal/swap/SwapDialog.tsx b/src-gui/src/renderer/components/modal/swap/SwapDialog.tsx
index 8950e608a6..64e74ef830 100644
--- a/src-gui/src/renderer/components/modal/swap/SwapDialog.tsx
+++ b/src-gui/src/renderer/components/modal/swap/SwapDialog.tsx
@@ -1,18 +1,11 @@
-import {
- Box,
- Button,
- Dialog,
- DialogActions,
- DialogContent,
-} from "@mui/material";
+import { Box, Dialog, DialogActions, DialogContent } from "@mui/material";
import { useState } from "react";
-import { swapReset } from "store/features/swapSlice";
-import { useAppDispatch, useAppSelector, useIsSwapRunning } from "store/hooks";
-import SwapSuspendAlert from "../SwapSuspendAlert";
+import { useAppSelector } from "store/hooks";
import DebugPage from "./pages/DebugPage";
-import SwapStatePage from "./pages/SwapStatePage";
+import SwapStatePage from "renderer/components/pages/swap/swap/SwapStatePage";
import SwapDialogTitle from "./SwapDialogTitle";
import SwapStateStepper from "./SwapStateStepper";
+import CancelButton from "renderer/components/pages/swap/swap/CancelButton";
export default function SwapDialog({
open,
@@ -22,26 +15,13 @@ export default function SwapDialog({
onClose: () => void;
}) {
const swap = useAppSelector((state) => state.swap);
- const isSwapRunning = useIsSwapRunning();
const [debug, setDebug] = useState(false);
- const [openSuspendAlert, setOpenSuspendAlert] = useState(false);
-
- const dispatch = useAppDispatch();
-
- function onCancel() {
- if (isSwapRunning) {
- setOpenSuspendAlert(true);
- } else {
- onClose();
- dispatch(swapReset());
- }
- }
// This prevents an issue where the Dialog is shown for a split second without a present swap state
if (!open) return null;
return (
-