wip: WithdrawDialog migrated to Tauri IPC

This commit is contained in:
binarybaron 2024-08-09 19:46:58 +02:00
parent 47821cbe79
commit 3d16ff6d5c
No known key found for this signature in database
GPG key ID: 99B75D3E1476A26E
118 changed files with 1779 additions and 1868 deletions

View file

@ -24,5 +24,5 @@ export interface Alert {
id: number; id: number;
title: string; title: string;
body: string; body: string;
severity: 'info' | 'warning' | 'error'; severity: "info" | "warning" | "error";
} }

View file

@ -1,14 +1,14 @@
export enum SwapSpawnType { export enum SwapSpawnType {
INIT = 'init', INIT = "init",
RESUME = 'resume', RESUME = "resume",
CANCEL_REFUND = 'cancel-refund', CANCEL_REFUND = "cancel-refund",
} }
export type CliLogSpanType = string | 'BitcoinWalletSubscription'; export type CliLogSpanType = string | "BitcoinWalletSubscription";
export interface CliLog { export interface CliLog {
timestamp: string; timestamp: string;
level: 'DEBUG' | 'INFO' | 'WARN' | 'ERROR' | 'TRACE'; level: "DEBUG" | "INFO" | "WARN" | "ERROR" | "TRACE";
fields: { fields: {
message: string; message: string;
[index: string]: unknown; [index: string]: unknown;
@ -20,12 +20,12 @@ export interface CliLog {
} }
export function isCliLog(log: unknown): log is CliLog { export function isCliLog(log: unknown): log is CliLog {
if (log && typeof log === 'object') { if (log && typeof log === "object") {
return ( return (
'timestamp' in (log as CliLog) && "timestamp" in (log as CliLog) &&
'level' in (log as CliLog) && "level" in (log as CliLog) &&
'fields' in (log as CliLog) && "fields" in (log as CliLog) &&
typeof (log as CliLog).fields?.message === 'string' typeof (log as CliLog).fields?.message === "string"
); );
} }
return false; return false;
@ -33,7 +33,7 @@ export function isCliLog(log: unknown): log is CliLog {
export interface CliLogStartedRpcServer extends CliLog { export interface CliLogStartedRpcServer extends CliLog {
fields: { fields: {
message: 'Started RPC server'; message: "Started RPC server";
addr: string; addr: string;
}; };
} }
@ -41,12 +41,12 @@ export interface CliLogStartedRpcServer extends CliLog {
export function isCliLogStartedRpcServer( export function isCliLogStartedRpcServer(
log: CliLog, log: CliLog,
): log is CliLogStartedRpcServer { ): log is CliLogStartedRpcServer {
return log.fields.message === 'Started RPC server'; return log.fields.message === "Started RPC server";
} }
export interface CliLogReleasingSwapLockLog extends CliLog { export interface CliLogReleasingSwapLockLog extends CliLog {
fields: { fields: {
message: 'Releasing swap lock'; message: "Releasing swap lock";
swap_id: string; swap_id: string;
}; };
} }
@ -54,23 +54,23 @@ export interface CliLogReleasingSwapLockLog extends CliLog {
export function isCliLogReleasingSwapLockLog( export function isCliLogReleasingSwapLockLog(
log: CliLog, log: CliLog,
): log is CliLogReleasingSwapLockLog { ): log is CliLogReleasingSwapLockLog {
return log.fields.message === 'Releasing swap lock'; return log.fields.message === "Releasing swap lock";
} }
export interface CliLogApiCallError extends CliLog { export interface CliLogApiCallError extends CliLog {
fields: { fields: {
message: 'API call resulted in an error'; message: "API call resulted in an error";
err: string; err: string;
}; };
} }
export function isCliLogApiCallError(log: CliLog): log is CliLogApiCallError { export function isCliLogApiCallError(log: CliLog): log is CliLogApiCallError {
return log.fields.message === 'API call resulted in an error'; return log.fields.message === "API call resulted in an error";
} }
export interface CliLogAcquiringSwapLockLog extends CliLog { export interface CliLogAcquiringSwapLockLog extends CliLog {
fields: { fields: {
message: 'Acquiring swap lock'; message: "Acquiring swap lock";
swap_id: string; swap_id: string;
}; };
} }
@ -78,12 +78,12 @@ export interface CliLogAcquiringSwapLockLog extends CliLog {
export function isCliLogAcquiringSwapLockLog( export function isCliLogAcquiringSwapLockLog(
log: CliLog, log: CliLog,
): log is CliLogAcquiringSwapLockLog { ): log is CliLogAcquiringSwapLockLog {
return log.fields.message === 'Acquiring swap lock'; return log.fields.message === "Acquiring swap lock";
} }
export interface CliLogReceivedQuote extends CliLog { export interface CliLogReceivedQuote extends CliLog {
fields: { fields: {
message: 'Received quote'; message: "Received quote";
price: string; price: string;
minimum_amount: string; minimum_amount: string;
maximum_amount: string; maximum_amount: string;
@ -91,12 +91,12 @@ export interface CliLogReceivedQuote extends CliLog {
} }
export function isCliLogReceivedQuote(log: CliLog): log is CliLogReceivedQuote { export function isCliLogReceivedQuote(log: CliLog): log is CliLogReceivedQuote {
return log.fields.message === 'Received quote'; return log.fields.message === "Received quote";
} }
export interface CliLogWaitingForBtcDeposit extends CliLog { export interface CliLogWaitingForBtcDeposit extends CliLog {
fields: { fields: {
message: 'Waiting for Bitcoin deposit'; message: "Waiting for Bitcoin deposit";
deposit_address: string; deposit_address: string;
min_deposit_until_swap_will_start: string; min_deposit_until_swap_will_start: string;
max_deposit_until_maximum_amount_is_reached: string; max_deposit_until_maximum_amount_is_reached: string;
@ -111,24 +111,24 @@ export interface CliLogWaitingForBtcDeposit extends CliLog {
export function isCliLogWaitingForBtcDeposit( export function isCliLogWaitingForBtcDeposit(
log: CliLog, log: CliLog,
): log is CliLogWaitingForBtcDeposit { ): log is CliLogWaitingForBtcDeposit {
return log.fields.message === 'Waiting for Bitcoin deposit'; return log.fields.message === "Waiting for Bitcoin deposit";
} }
export interface CliLogReceivedBtc extends CliLog { export interface CliLogReceivedBtc extends CliLog {
fields: { fields: {
message: 'Received Bitcoin'; message: "Received Bitcoin";
max_giveable: string; max_giveable: string;
new_balance: string; new_balance: string;
}; };
} }
export function isCliLogReceivedBtc(log: CliLog): log is CliLogReceivedBtc { export function isCliLogReceivedBtc(log: CliLog): log is CliLogReceivedBtc {
return log.fields.message === 'Received Bitcoin'; return log.fields.message === "Received Bitcoin";
} }
export interface CliLogDeterminedSwapAmount extends CliLog { export interface CliLogDeterminedSwapAmount extends CliLog {
fields: { fields: {
message: 'Determined swap amount'; message: "Determined swap amount";
amount: string; amount: string;
fees: string; fees: string;
}; };
@ -137,49 +137,49 @@ export interface CliLogDeterminedSwapAmount extends CliLog {
export function isCliLogDeterminedSwapAmount( export function isCliLogDeterminedSwapAmount(
log: CliLog, log: CliLog,
): log is CliLogDeterminedSwapAmount { ): log is CliLogDeterminedSwapAmount {
return log.fields.message === 'Determined swap amount'; return log.fields.message === "Determined swap amount";
} }
export interface CliLogStartedSwap extends CliLog { export interface CliLogStartedSwap extends CliLog {
fields: { fields: {
message: 'Starting new swap'; message: "Starting new swap";
swap_id: string; swap_id: string;
}; };
} }
export function isCliLogStartedSwap(log: CliLog): log is CliLogStartedSwap { export function isCliLogStartedSwap(log: CliLog): log is CliLogStartedSwap {
return log.fields.message === 'Starting new swap'; return log.fields.message === "Starting new swap";
} }
export interface CliLogPublishedBtcTx extends CliLog { export interface CliLogPublishedBtcTx extends CliLog {
fields: { fields: {
message: 'Published Bitcoin transaction'; message: "Published Bitcoin transaction";
txid: string; txid: string;
kind: 'lock' | 'cancel' | 'withdraw' | 'refund'; kind: "lock" | "cancel" | "withdraw" | "refund";
}; };
} }
export function isCliLogPublishedBtcTx( export function isCliLogPublishedBtcTx(
log: CliLog, log: CliLog,
): log is CliLogPublishedBtcTx { ): log is CliLogPublishedBtcTx {
return log.fields.message === 'Published Bitcoin transaction'; return log.fields.message === "Published Bitcoin transaction";
} }
export interface CliLogBtcTxFound extends CliLog { export interface CliLogBtcTxFound extends CliLog {
fields: { fields: {
message: 'Found relevant Bitcoin transaction'; message: "Found relevant Bitcoin transaction";
txid: string; txid: string;
status: string; status: string;
}; };
} }
export function isCliLogBtcTxFound(log: CliLog): log is CliLogBtcTxFound { export function isCliLogBtcTxFound(log: CliLog): log is CliLogBtcTxFound {
return log.fields.message === 'Found relevant Bitcoin transaction'; return log.fields.message === "Found relevant Bitcoin transaction";
} }
export interface CliLogBtcTxStatusChanged extends CliLog { export interface CliLogBtcTxStatusChanged extends CliLog {
fields: { fields: {
message: 'Bitcoin transaction status changed'; message: "Bitcoin transaction status changed";
txid: string; txid: string;
new_status: string; new_status: string;
}; };
@ -188,12 +188,12 @@ export interface CliLogBtcTxStatusChanged extends CliLog {
export function isCliLogBtcTxStatusChanged( export function isCliLogBtcTxStatusChanged(
log: CliLog, log: CliLog,
): log is CliLogBtcTxStatusChanged { ): log is CliLogBtcTxStatusChanged {
return log.fields.message === 'Bitcoin transaction status changed'; return log.fields.message === "Bitcoin transaction status changed";
} }
export interface CliLogAliceLockedXmr extends CliLog { export interface CliLogAliceLockedXmr extends CliLog {
fields: { fields: {
message: 'Alice locked Monero'; message: "Alice locked Monero";
txid: string; txid: string;
}; };
} }
@ -201,12 +201,12 @@ export interface CliLogAliceLockedXmr extends CliLog {
export function isCliLogAliceLockedXmr( export function isCliLogAliceLockedXmr(
log: CliLog, log: CliLog,
): log is CliLogAliceLockedXmr { ): log is CliLogAliceLockedXmr {
return log.fields.message === 'Alice locked Monero'; return log.fields.message === "Alice locked Monero";
} }
export interface CliLogReceivedXmrLockTxConfirmation extends CliLog { export interface CliLogReceivedXmrLockTxConfirmation extends CliLog {
fields: { fields: {
message: 'Received new confirmation for Monero lock tx'; message: "Received new confirmation for Monero lock tx";
txid: string; txid: string;
seen_confirmations: string; seen_confirmations: string;
needed_confirmations: string; needed_confirmations: string;
@ -216,50 +216,50 @@ export interface CliLogReceivedXmrLockTxConfirmation extends CliLog {
export function isCliLogReceivedXmrLockTxConfirmation( export function isCliLogReceivedXmrLockTxConfirmation(
log: CliLog, log: CliLog,
): log is CliLogReceivedXmrLockTxConfirmation { ): log is CliLogReceivedXmrLockTxConfirmation {
return log.fields.message === 'Received new confirmation for Monero lock tx'; return log.fields.message === "Received new confirmation for Monero lock tx";
} }
export interface CliLogAdvancingState extends CliLog { export interface CliLogAdvancingState extends CliLog {
fields: { fields: {
message: 'Advancing state'; message: "Advancing state";
state: state:
| 'quote has been requested' | "quote has been requested"
| 'execution setup done' | "execution setup done"
| 'btc is locked' | "btc is locked"
| 'XMR lock transaction transfer proof received' | "XMR lock transaction transfer proof received"
| 'xmr is locked' | "xmr is locked"
| 'encrypted signature is sent' | "encrypted signature is sent"
| 'btc is redeemed' | "btc is redeemed"
| 'cancel timelock is expired' | "cancel timelock is expired"
| 'btc is cancelled' | "btc is cancelled"
| 'btc is refunded' | "btc is refunded"
| 'xmr is redeemed' | "xmr is redeemed"
| 'btc is punished' | "btc is punished"
| 'safely aborted'; | "safely aborted";
}; };
} }
export function isCliLogAdvancingState( export function isCliLogAdvancingState(
log: CliLog, log: CliLog,
): log is CliLogAdvancingState { ): log is CliLogAdvancingState {
return log.fields.message === 'Advancing state'; return log.fields.message === "Advancing state";
} }
export interface CliLogRedeemedXmr extends CliLog { export interface CliLogRedeemedXmr extends CliLog {
fields: { fields: {
message: 'Successfully transferred XMR to wallet'; message: "Successfully transferred XMR to wallet";
monero_receive_address: string; monero_receive_address: string;
txid: string; txid: string;
}; };
} }
export function isCliLogRedeemedXmr(log: CliLog): log is CliLogRedeemedXmr { export function isCliLogRedeemedXmr(log: CliLog): log is CliLogRedeemedXmr {
return log.fields.message === 'Successfully transferred XMR to wallet'; return log.fields.message === "Successfully transferred XMR to wallet";
} }
export interface YouHaveBeenPunishedCliLog extends CliLog { export interface YouHaveBeenPunishedCliLog extends CliLog {
fields: { fields: {
message: 'You have been punished for not refunding in time'; message: "You have been punished for not refunding in time";
}; };
} }
@ -267,7 +267,7 @@ export function isYouHaveBeenPunishedCliLog(
log: CliLog, log: CliLog,
): log is YouHaveBeenPunishedCliLog { ): log is YouHaveBeenPunishedCliLog {
return ( return (
log.fields.message === 'You have been punished for not refunding in time' log.fields.message === "You have been punished for not refunding in time"
); );
} }
@ -280,14 +280,14 @@ function getCliLogSpanAttribute<T>(log: CliLog, key: string): T | null {
} }
export function getCliLogSpanSwapId(log: CliLog): string | null { export function getCliLogSpanSwapId(log: CliLog): string | null {
return getCliLogSpanAttribute<string>(log, 'swap_id'); return getCliLogSpanAttribute<string>(log, "swap_id");
} }
export function getCliLogSpanLogReferenceId(log: CliLog): string | null { export function getCliLogSpanLogReferenceId(log: CliLog): string | null {
return ( return (
getCliLogSpanAttribute<string>(log, 'log_reference_id')?.replace( getCliLogSpanAttribute<string>(log, "log_reference_id")?.replace(
/"/g, /"/g,
'', "",
) || null ) || null
); );
} }
@ -301,7 +301,7 @@ export function hasCliLogOneOfMultipleSpans(
export interface CliLogStartedSyncingMoneroWallet extends CliLog { export interface CliLogStartedSyncingMoneroWallet extends CliLog {
fields: { fields: {
message: 'Syncing Monero wallet'; message: "Syncing Monero wallet";
current_sync_height?: boolean; current_sync_height?: boolean;
}; };
} }
@ -309,18 +309,18 @@ export interface CliLogStartedSyncingMoneroWallet extends CliLog {
export function isCliLogStartedSyncingMoneroWallet( export function isCliLogStartedSyncingMoneroWallet(
log: CliLog, log: CliLog,
): log is CliLogStartedSyncingMoneroWallet { ): log is CliLogStartedSyncingMoneroWallet {
return log.fields.message === 'Syncing Monero wallet'; return log.fields.message === "Syncing Monero wallet";
} }
export interface CliLogFinishedSyncingMoneroWallet extends CliLog { export interface CliLogFinishedSyncingMoneroWallet extends CliLog {
fields: { fields: {
message: 'Synced Monero wallet'; message: "Synced Monero wallet";
}; };
} }
export interface CliLogFailedToSyncMoneroWallet extends CliLog { export interface CliLogFailedToSyncMoneroWallet extends CliLog {
fields: { fields: {
message: 'Failed to sync Monero wallet'; message: "Failed to sync Monero wallet";
error: string; error: string;
}; };
} }
@ -328,18 +328,18 @@ export interface CliLogFailedToSyncMoneroWallet extends CliLog {
export function isCliLogFailedToSyncMoneroWallet( export function isCliLogFailedToSyncMoneroWallet(
log: CliLog, log: CliLog,
): log is CliLogFailedToSyncMoneroWallet { ): log is CliLogFailedToSyncMoneroWallet {
return log.fields.message === 'Failed to sync Monero wallet'; return log.fields.message === "Failed to sync Monero wallet";
} }
export function isCliLogFinishedSyncingMoneroWallet( export function isCliLogFinishedSyncingMoneroWallet(
log: CliLog, log: CliLog,
): log is CliLogFinishedSyncingMoneroWallet { ): log is CliLogFinishedSyncingMoneroWallet {
return log.fields.message === 'Monero wallet synced'; return log.fields.message === "Monero wallet synced";
} }
export interface CliLogDownloadingMoneroWalletRpc extends CliLog { export interface CliLogDownloadingMoneroWalletRpc extends CliLog {
fields: { fields: {
message: 'Downloading monero-wallet-rpc'; message: "Downloading monero-wallet-rpc";
progress: string; progress: string;
size: string; size: string;
download_url: string; download_url: string;
@ -349,19 +349,19 @@ export interface CliLogDownloadingMoneroWalletRpc extends CliLog {
export function isCliLogDownloadingMoneroWalletRpc( export function isCliLogDownloadingMoneroWalletRpc(
log: CliLog, log: CliLog,
): log is CliLogDownloadingMoneroWalletRpc { ): log is CliLogDownloadingMoneroWalletRpc {
return log.fields.message === 'Downloading monero-wallet-rpc'; return log.fields.message === "Downloading monero-wallet-rpc";
} }
export interface CliLogStartedSyncingMoneroWallet extends CliLog { export interface CliLogStartedSyncingMoneroWallet extends CliLog {
fields: { fields: {
message: 'Syncing Monero wallet'; message: "Syncing Monero wallet";
current_sync_height?: boolean; current_sync_height?: boolean;
}; };
} }
export interface CliLogDownloadingMoneroWalletRpc extends CliLog { export interface CliLogDownloadingMoneroWalletRpc extends CliLog {
fields: { fields: {
message: 'Downloading monero-wallet-rpc'; message: "Downloading monero-wallet-rpc";
progress: string; progress: string;
size: string; size: string;
download_url: string; download_url: string;
@ -370,7 +370,7 @@ export interface CliLogDownloadingMoneroWalletRpc extends CliLog {
export interface CliLogGotNotificationForNewBlock extends CliLog { export interface CliLogGotNotificationForNewBlock extends CliLog {
fields: { fields: {
message: 'Got notification for new block'; message: "Got notification for new block";
block_height: string; block_height: string;
}; };
} }
@ -378,29 +378,36 @@ export interface CliLogGotNotificationForNewBlock extends CliLog {
export function isCliLogGotNotificationForNewBlock( export function isCliLogGotNotificationForNewBlock(
log: CliLog, log: CliLog,
): log is CliLogGotNotificationForNewBlock { ): log is CliLogGotNotificationForNewBlock {
return log.fields.message === 'Got notification for new block'; return log.fields.message === "Got notification for new block";
} }
export interface CliLogAttemptingToCooperativelyRedeemXmr extends CliLog { export interface CliLogAttemptingToCooperativelyRedeemXmr extends CliLog {
fields: { fields: {
message: 'Attempting to cooperatively redeem XMR after being punished'; message: "Attempting to cooperatively redeem XMR after being punished";
}; };
} }
export function isCliLogAttemptingToCooperativelyRedeemXmr( export function isCliLogAttemptingToCooperativelyRedeemXmr(
log: CliLog, log: CliLog,
): log is CliLogAttemptingToCooperativelyRedeemXmr { ): log is CliLogAttemptingToCooperativelyRedeemXmr {
return log.fields.message === 'Attempting to cooperatively redeem XMR after being punished'; return (
log.fields.message ===
"Attempting to cooperatively redeem XMR after being punished"
);
} }
export interface CliLogAliceHasAcceptedOurRequestToCooperativelyRedeemTheXmr extends CliLog { export interface CliLogAliceHasAcceptedOurRequestToCooperativelyRedeemTheXmr
extends CliLog {
fields: { fields: {
message: 'Alice has accepted our request to cooperatively redeem the XMR'; message: "Alice has accepted our request to cooperatively redeem the XMR";
}; };
} }
export function isCliLogAliceHasAcceptedOurRequestToCooperativelyRedeemTheXmr( export function isCliLogAliceHasAcceptedOurRequestToCooperativelyRedeemTheXmr(
log: CliLog, log: CliLog,
): log is CliLogAliceHasAcceptedOurRequestToCooperativelyRedeemTheXmr { ): log is CliLogAliceHasAcceptedOurRequestToCooperativelyRedeemTheXmr {
return log.fields.message === 'Alice has accepted our request to cooperatively redeem the XMR'; return (
log.fields.message ===
"Alice has accepted our request to cooperatively redeem the XMR"
);
} }

View file

@ -1,5 +1,5 @@
import { CliLog, SwapSpawnType } from './cliModel'; import { CliLog, SwapSpawnType } from "./cliModel";
import { Provider } from './apiModel'; import { Provider } from "./apiModel";
export interface SwapSlice { export interface SwapSlice {
state: SwapState | null; state: SwapState | null;
@ -20,21 +20,21 @@ export interface SwapState {
} }
export enum SwapStateType { export enum SwapStateType {
INITIATED = 'initiated', INITIATED = "initiated",
RECEIVED_QUOTE = 'received quote', RECEIVED_QUOTE = "received quote",
WAITING_FOR_BTC_DEPOSIT = 'waiting for btc deposit', WAITING_FOR_BTC_DEPOSIT = "waiting for btc deposit",
STARTED = 'started', STARTED = "started",
BTC_LOCK_TX_IN_MEMPOOL = 'btc lock tx is in mempool', BTC_LOCK_TX_IN_MEMPOOL = "btc lock tx is in mempool",
XMR_LOCK_TX_IN_MEMPOOL = 'xmr lock tx is in mempool', XMR_LOCK_TX_IN_MEMPOOL = "xmr lock tx is in mempool",
XMR_LOCKED = 'xmr is locked', XMR_LOCKED = "xmr is locked",
BTC_REDEEMED = 'btc redeemed', BTC_REDEEMED = "btc redeemed",
XMR_REDEEM_IN_MEMPOOL = 'xmr redeem tx is in mempool', XMR_REDEEM_IN_MEMPOOL = "xmr redeem tx is in mempool",
PROCESS_EXITED = 'process exited', PROCESS_EXITED = "process exited",
BTC_CANCELLED = 'btc cancelled', BTC_CANCELLED = "btc cancelled",
BTC_REFUNDED = 'btc refunded', BTC_REFUNDED = "btc refunded",
BTC_PUNISHED = 'btc punished', BTC_PUNISHED = "btc punished",
ATTEMPTING_COOPERATIVE_REDEEM = 'attempting cooperative redeem', ATTEMPTING_COOPERATIVE_REDEEM = "attempting cooperative redeem",
COOPERATIVE_REDEEM_REJECTED = 'cooperative redeem rejected', COOPERATIVE_REDEEM_REJECTED = "cooperative redeem rejected",
} }
export function isSwapState(state?: SwapState | null): state is SwapState { export function isSwapState(state?: SwapState | null): state is SwapState {

View file

@ -1,6 +1,6 @@
import { Alert, ExtendedProviderStatus } from 'models/apiModel'; import { Alert, ExtendedProviderStatus } from "models/apiModel";
const API_BASE_URL = 'https://api.unstoppableswap.net'; const API_BASE_URL = "https://api.unstoppableswap.net";
export async function fetchProvidersViaHttp(): Promise< export async function fetchProvidersViaHttp(): Promise<
ExtendedProviderStatus[] ExtendedProviderStatus[]
@ -23,9 +23,9 @@ export async function submitFeedbackViaHttp(
}; };
const response = await fetch(`${API_BASE_URL}/api/submit-feedback`, { const response = await fetch(`${API_BASE_URL}/api/submit-feedback`, {
method: 'POST', method: "POST",
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
}, },
body: JSON.stringify({ body, attachedData }), body: JSON.stringify({ body, attachedData }),
}); });
@ -53,9 +53,9 @@ async function fetchCurrencyUsdPrice(currency: string): Promise<number> {
} }
export async function fetchBtcPrice(): Promise<number> { export async function fetchBtcPrice(): Promise<number> {
return fetchCurrencyUsdPrice('bitcoin'); return fetchCurrencyUsdPrice("bitcoin");
} }
export async function fetchXmrPrice(): Promise<number> { export async function fetchXmrPrice(): Promise<number> {
return fetchCurrencyUsdPrice('monero'); return fetchCurrencyUsdPrice("monero");
} }

View file

@ -1,13 +1,13 @@
import { Box, makeStyles, CssBaseline } from '@material-ui/core'; import { Box, makeStyles, CssBaseline } from "@material-ui/core";
import { createTheme, ThemeProvider } from '@material-ui/core/styles'; import { createTheme, ThemeProvider } from "@material-ui/core/styles";
import { indigo } from '@material-ui/core/colors'; import { indigo } from "@material-ui/core/colors";
import { MemoryRouter as Router, Routes, Route } from 'react-router-dom'; import { MemoryRouter as Router, Routes, Route } from "react-router-dom";
import Navigation, { drawerWidth } from './navigation/Navigation'; import Navigation, { drawerWidth } from "./navigation/Navigation";
import HistoryPage from './pages/history/HistoryPage'; import HistoryPage from "./pages/history/HistoryPage";
import SwapPage from './pages/swap/SwapPage'; import SwapPage from "./pages/swap/SwapPage";
import WalletPage from './pages/wallet/WalletPage'; import WalletPage from "./pages/wallet/WalletPage";
import HelpPage from './pages/help/HelpPage'; import HelpPage from "./pages/help/HelpPage";
import GlobalSnackbarProvider from './snackbar/GlobalSnackbarProvider'; import GlobalSnackbarProvider from "./snackbar/GlobalSnackbarProvider";
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles((theme) => ({
innerContent: { innerContent: {
@ -20,14 +20,14 @@ const useStyles = makeStyles((theme) => ({
const theme = createTheme({ const theme = createTheme({
palette: { palette: {
type: 'dark', type: "dark",
primary: { primary: {
main: '#f4511e', main: "#f4511e",
}, },
secondary: indigo, secondary: indigo,
}, },
transitions: { transitions: {
create: () => 'none', create: () => "none",
}, },
props: { props: {
MuiButtonBase: { MuiButtonBase: {

View file

@ -4,12 +4,12 @@ import {
CircularProgress, CircularProgress,
IconButton, IconButton,
Tooltip, Tooltip,
} from '@material-ui/core'; } from "@material-ui/core";
import { ReactElement, ReactNode, useEffect, useState } from 'react'; import { ReactElement, ReactNode, useEffect, useState } from "react";
import { useSnackbar } from 'notistack'; import { useSnackbar } from "notistack";
import { useAppSelector } from 'store/hooks'; import { useAppSelector } from "store/hooks";
import { RpcProcessStateType } from 'models/rpcModel'; import { RpcProcessStateType } from "models/rpcModel";
import { isExternalRpc } from 'store/config'; import { isExternalRpc } from "store/config";
function IpcButtonTooltip({ function IpcButtonTooltip({
requiresRpcAndNotReady, requiresRpcAndNotReady,
@ -27,19 +27,19 @@ function IpcButtonTooltip({
} }
const getMessage = () => { const getMessage = () => {
if (!requiresRpcAndNotReady) return ''; if (!requiresRpcAndNotReady) return "";
switch (processType) { switch (processType) {
case RpcProcessStateType.LISTENING_FOR_CONNECTIONS: case RpcProcessStateType.LISTENING_FOR_CONNECTIONS:
return ''; return "";
case RpcProcessStateType.STARTED: case RpcProcessStateType.STARTED:
return 'Cannot execute this action because the Swap Daemon is still starting and not yet ready to accept connections. Please wait a moment and try again'; return "Cannot execute this action because the Swap Daemon is still starting and not yet ready to accept connections. Please wait a moment and try again";
case RpcProcessStateType.EXITED: case RpcProcessStateType.EXITED:
return 'Cannot execute this action because the Swap Daemon has been stopped. Please start the Swap Daemon again to continue'; return "Cannot execute this action because the Swap Daemon has been stopped. Please start the Swap Daemon again to continue";
case RpcProcessStateType.NOT_STARTED: case RpcProcessStateType.NOT_STARTED:
return 'Cannot execute this action because the Swap Daemon has not been started yet. Please start the Swap Daemon first'; return "Cannot execute this action because the Swap Daemon has not been started yet. Please start the Swap Daemon first";
default: default:
return ''; return "";
} }
}; };
@ -108,13 +108,13 @@ export default function IpcInvokeButton<T>({
setIsPending(true); setIsPending(true);
try { try {
// const result = await ipcRenderer.invoke(ipcChannel, ...ipcArgs); // const result = await ipcRenderer.invoke(ipcChannel, ...ipcArgs);
throw new Error('Not implemented'); throw new Error("Not implemented");
// onSuccess?.(result); // onSuccess?.(result);
} catch (e: unknown) { } catch (e: unknown) {
if (displayErrorSnackbar) { if (displayErrorSnackbar) {
enqueueSnackbar((e as Error).message, { enqueueSnackbar((e as Error).message, {
autoHideDuration: 60 * 1000, autoHideDuration: 60 * 1000,
variant: 'error', variant: "error",
}); });
} }
} finally { } finally {

View file

@ -6,7 +6,7 @@ import { ReactNode, useEffect, useState } from "react";
interface IpcInvokeButtonProps<T> { interface IpcInvokeButtonProps<T> {
onSuccess?: (data: T) => void; onSuccess?: (data: T) => void;
onClick: () => Promise<T>; onClick: () => Promise<T>;
onPendingChange?: (bool) => void; onPendingChange?: (isPending: boolean) => void;
isLoadingOverride?: boolean; isLoadingOverride?: boolean;
isIconButton?: boolean; isIconButton?: boolean;
loadIcon?: ReactNode; loadIcon?: ReactNode;
@ -46,7 +46,7 @@ export default function PromiseInvokeButton<T>({
onSuccess?.(result); onSuccess?.(result);
} catch (e: unknown) { } catch (e: unknown) {
if (displayErrorSnackbar) { if (displayErrorSnackbar) {
enqueueSnackbar((e as Error).message, { enqueueSnackbar(e as String, {
autoHideDuration: 60 * 1000, autoHideDuration: 60 * 1000,
variant: "error", variant: "error",
}); });

View file

@ -1,7 +1,7 @@
import { Button } from '@material-ui/core'; import { Button } from "@material-ui/core";
import Alert from '@material-ui/lab/Alert'; import Alert from "@material-ui/lab/Alert";
import { useNavigate } from 'react-router-dom'; import { useNavigate } from "react-router-dom";
import { useAppSelector } from 'store/hooks'; import { useAppSelector } from "store/hooks";
export default function FundsLeftInWalletAlert() { export default function FundsLeftInWalletAlert() {
const fundsLeft = useAppSelector((state) => state.rpc.state.balance); const fundsLeft = useAppSelector((state) => state.rpc.state.balance);
@ -16,7 +16,7 @@ export default function FundsLeftInWalletAlert() {
<Button <Button
color="inherit" color="inherit"
size="small" size="small"
onClick={() => navigate('/wallet')} onClick={() => navigate("/wallet")}
> >
View View
</Button> </Button>

View file

@ -1,6 +1,6 @@
import { Alert } from '@material-ui/lab'; import { Alert } from "@material-ui/lab";
import { Box, LinearProgress } from '@material-ui/core'; import { Box, LinearProgress } from "@material-ui/core";
import { useAppSelector } from 'store/hooks'; import { useAppSelector } from "store/hooks";
export default function MoneroWalletRpcUpdatingAlert() { export default function MoneroWalletRpcUpdatingAlert() {
const updateState = useAppSelector( const updateState = useAppSelector(
@ -17,7 +17,7 @@ export default function MoneroWalletRpcUpdatingAlert() {
return ( return (
<Alert severity="info"> <Alert severity="info">
<Box style={{ display: 'flex', flexDirection: 'column', gap: '0.5rem' }}> <Box style={{ display: "flex", flexDirection: "column", gap: "0.5rem" }}>
<span>The Monero wallet is updating. This may take a few moments</span> <span>The Monero wallet is updating. This may take a few moments</span>
<LinearProgress <LinearProgress
variant="determinate" variant="determinate"

View file

@ -1,8 +1,8 @@
import { Alert } from '@material-ui/lab'; import { Alert } from "@material-ui/lab";
import { Box, makeStyles } from '@material-ui/core'; import { Box, makeStyles } from "@material-ui/core";
import { useAppSelector } from 'store/hooks'; import { useAppSelector } from "store/hooks";
import WalletRefreshButton from '../pages/wallet/WalletRefreshButton'; import WalletRefreshButton from "../pages/wallet/WalletRefreshButton";
import { SatsAmount } from '../other/Units'; import { SatsAmount } from "../other/Units";
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles((theme) => ({
outer: { outer: {

View file

@ -1,7 +1,7 @@
import { Alert } from '@material-ui/lab'; import { Alert } from "@material-ui/lab";
import { CircularProgress } from '@material-ui/core'; import { CircularProgress } from "@material-ui/core";
import { useAppSelector } from 'store/hooks'; import { useAppSelector } from "store/hooks";
import { RpcProcessStateType } from 'models/rpcModel'; import { RpcProcessStateType } from "models/rpcModel";
export default function RpcStatusAlert() { export default function RpcStatusAlert() {
const rpcProcess = useAppSelector((s) => s.rpc.process); const rpcProcess = useAppSelector((s) => s.rpc.process);

View file

@ -38,21 +38,20 @@ export default function SwapMightBeCancelledAlert({
return ( return (
<Alert severity="warning" className={classes.outer} variant="filled"> <Alert severity="warning" className={classes.outer} variant="filled">
<AlertTitle>Be careful!</AlertTitle> <AlertTitle>Be careful!</AlertTitle>
The swap provider has taken a long time to lock their Monero. This The swap provider has taken a long time to lock their Monero. This might
might mean that: mean that:
<ul className={classes.list}> <ul className={classes.list}>
<li> <li>
There is a technical issue that prevents them from locking There is a technical issue that prevents them from locking their funds
their funds
</li> </li>
<li>They are a malicious actor (unlikely)</li> <li>They are a malicious actor (unlikely)</li>
</ul> </ul>
<br /> <br />
There is still hope for the swap to be successful but you have to be There is still hope for the swap to be successful but you have to be extra
extra careful. Regardless of why it has taken them so long, it is careful. Regardless of why it has taken them so long, it is important that
important that you refund the swap within the required time period you refund the swap within the required time period if the swap is not
if the swap is not completed. If you fail to to do so, you will be completed. If you fail to to do so, you will be punished and lose your
punished and lose your money. money.
<ul className={classes.list}> <ul className={classes.list}>
{isSwapTimelockInfoNone(timelock) && ( {isSwapTimelockInfoNone(timelock) && (
<> <>
@ -67,13 +66,9 @@ export default function SwapMightBeCancelledAlert({
<li> <li>
<strong> <strong>
If you have not refunded or completed the swap If you have not refunded or completed the swap in about{" "}
in about{" "}
<HumanizedBitcoinBlockDuration <HumanizedBitcoinBlockDuration
blocks={ blocks={timelock.None.blocks_left + punishTimelockOffset}
timelock.None.blocks_left +
punishTimelockOffset
}
/> />
, you will lose your funds. , you will lose your funds.
</strong> </strong>
@ -83,8 +78,7 @@ export default function SwapMightBeCancelledAlert({
{isSwapTimelockInfoCancelled(timelock) && ( {isSwapTimelockInfoCancelled(timelock) && (
<li> <li>
<strong> <strong>
If you have not refunded or completed the swap in If you have not refunded or completed the swap in about{" "}
about{" "}
<HumanizedBitcoinBlockDuration <HumanizedBitcoinBlockDuration
blocks={timelock.Cancel.blocks_left} blocks={timelock.Cancel.blocks_left}
/> />
@ -94,8 +88,8 @@ export default function SwapMightBeCancelledAlert({
)} )}
<li> <li>
As long as you see this screen, the swap will be refunded As long as you see this screen, the swap will be refunded
automatically when the time comes. If this fails, you have automatically when the time comes. If this fails, you have to manually
to manually refund by navigating to the History page. refund by navigating to the History page.
</li> </li>
</ul> </ul>
</Alert> </Alert>

View file

@ -66,11 +66,7 @@ const BitcoinRedeemedStateAlert = ({ swap }: { swap: GetSwapInfoResponse }) => {
"If this step fails, you can manually redeem the funds", "If this step fails, you can manually redeem the funds",
]} ]}
/> />
<SwapMoneroRecoveryButton <SwapMoneroRecoveryButton swap={swap} size="small" variant="contained" />
swap={swap}
size="small"
variant="contained"
/>
</Box> </Box>
); );
}; };
@ -91,16 +87,13 @@ const BitcoinLockedNoTimelockExpiredStateAlert = ({
<MessageList <MessageList
messages={[ messages={[
<> <>
Your Bitcoin is locked. If the swap is not completed in Your Bitcoin is locked. If the swap is not completed in approximately{" "}
approximately{" "} <HumanizedBitcoinBlockDuration blocks={timelock.None.blocks_left} />,
<HumanizedBitcoinBlockDuration you need to refund
blocks={timelock.None.blocks_left}
/>
, you need to refund
</>, </>,
<> <>
You will lose your funds if you do not refund or complete the You will lose your funds if you do not refund or complete the swap
swap within{" "} within{" "}
<HumanizedBitcoinBlockDuration <HumanizedBitcoinBlockDuration
blocks={timelock.None.blocks_left + punishTimelockOffset} blocks={timelock.None.blocks_left + punishTimelockOffset}
/> />
@ -138,11 +131,7 @@ const BitcoinPossiblyCancelledAlert = ({
</>, </>,
]} ]}
/> />
<SwapCancelRefundButton <SwapCancelRefundButton swap={swap} size="small" variant="contained" />
swap={swap}
size="small"
variant="contained"
/>
</Box> </Box>
); );
}; };

View file

@ -1,7 +1,7 @@
import { Button } from '@material-ui/core'; import { Button } from "@material-ui/core";
import Alert from '@material-ui/lab/Alert'; import Alert from "@material-ui/lab/Alert";
import { useNavigate } from 'react-router-dom'; import { useNavigate } from "react-router-dom";
import { useResumeableSwapsCount } from 'store/hooks'; import { useResumeableSwapsCount } from "store/hooks";
export default function UnfinishedSwapsAlert() { export default function UnfinishedSwapsAlert() {
const resumableSwapsCount = useResumeableSwapsCount(); const resumableSwapsCount = useResumeableSwapsCount();
@ -16,16 +16,16 @@ export default function UnfinishedSwapsAlert() {
<Button <Button
color="inherit" color="inherit"
size="small" size="small"
onClick={() => navigate('/history')} onClick={() => navigate("/history")}
> >
VIEW VIEW
</Button> </Button>
} }
> >
You have{' '} You have{" "}
{resumableSwapsCount > 1 {resumableSwapsCount > 1
? `${resumableSwapsCount} unfinished swaps` ? `${resumableSwapsCount} unfinished swaps`
: 'one unfinished swap'} : "one unfinished swap"}
</Alert> </Alert>
); );
} }

View file

@ -1,5 +1,5 @@
import { SvgIcon } from '@material-ui/core'; import { SvgIcon } from "@material-ui/core";
import { SvgIconProps } from '@material-ui/core/SvgIcon/SvgIcon'; import { SvgIconProps } from "@material-ui/core/SvgIcon/SvgIcon";
export default function BitcoinIcon(props: SvgIconProps) { export default function BitcoinIcon(props: SvgIconProps) {
return ( return (

View file

@ -1,5 +1,5 @@
import { SvgIconProps } from '@material-ui/core/SvgIcon/SvgIcon'; import { SvgIconProps } from "@material-ui/core/SvgIcon/SvgIcon";
import { SvgIcon } from '@material-ui/core'; import { SvgIcon } from "@material-ui/core";
export default function DiscordIcon(props: SvgIconProps) { export default function DiscordIcon(props: SvgIconProps) {
return ( return (

View file

@ -1,5 +1,5 @@
import { ReactNode } from 'react'; import { ReactNode } from "react";
import { IconButton } from '@material-ui/core'; import { IconButton } from "@material-ui/core";
export default function LinkIconButton({ export default function LinkIconButton({
url, url,
@ -9,7 +9,7 @@ export default function LinkIconButton({
children: ReactNode; children: ReactNode;
}) { }) {
return ( return (
<IconButton component="span" onClick={() => window.open(url, '_blank')}> <IconButton component="span" onClick={() => window.open(url, "_blank")}>
{children} {children}
</IconButton> </IconButton>
); );

View file

@ -1,5 +1,5 @@
import { SvgIcon } from '@material-ui/core'; import { SvgIcon } from "@material-ui/core";
import { SvgIconProps } from '@material-ui/core/SvgIcon/SvgIcon'; import { SvgIconProps } from "@material-ui/core/SvgIcon/SvgIcon";
export default function MoneroIcon(props: SvgIconProps) { export default function MoneroIcon(props: SvgIconProps) {
return ( return (

View file

@ -1,5 +1,5 @@
import { SvgIcon } from '@material-ui/core'; import { SvgIcon } from "@material-ui/core";
import { SvgIconProps } from '@material-ui/core/SvgIcon/SvgIcon'; import { SvgIconProps } from "@material-ui/core/SvgIcon/SvgIcon";
export default function TorIcon(props: SvgIconProps) { export default function TorIcon(props: SvgIconProps) {
return ( return (

View file

@ -1,8 +1,8 @@
import { useEffect } from 'react'; import { useEffect } from "react";
import { TextField } from '@material-ui/core'; import { TextField } from "@material-ui/core";
import { TextFieldProps } from '@material-ui/core/TextField/TextField'; import { TextFieldProps } from "@material-ui/core/TextField/TextField";
import { isBtcAddressValid } from 'utils/conversionUtils'; import { isBtcAddressValid } from "utils/conversionUtils";
import { isTestnet } from 'store/config'; import { isTestnet } from "store/config";
export default function BitcoinAddressTextField({ export default function BitcoinAddressTextField({
address, address,
@ -16,11 +16,11 @@ export default function BitcoinAddressTextField({
onAddressValidityChange: (valid: boolean) => void; onAddressValidityChange: (valid: boolean) => void;
helperText: string; helperText: string;
} & TextFieldProps) { } & TextFieldProps) {
const placeholder = isTestnet() ? 'tb1q4aelwalu...' : 'bc18ociqZ9mZ...'; const placeholder = isTestnet() ? "tb1q4aelwalu..." : "bc18ociqZ9mZ...";
const errorText = isBtcAddressValid(address, isTestnet()) const errorText = isBtcAddressValid(address, isTestnet())
? null ? null
: `Only bech32 addresses are supported. They begin with "${ : `Only bech32 addresses are supported. They begin with "${
isTestnet() ? 'tb1' : 'bc1' isTestnet() ? "tb1" : "bc1"
}"`; }"`;
useEffect(() => { useEffect(() => {

View file

@ -1,8 +1,8 @@
import { useEffect } from 'react'; import { useEffect } from "react";
import { TextField } from '@material-ui/core'; import { TextField } from "@material-ui/core";
import { TextFieldProps } from '@material-ui/core/TextField/TextField'; import { TextFieldProps } from "@material-ui/core/TextField/TextField";
import { isXmrAddressValid } from 'utils/conversionUtils'; import { isXmrAddressValid } from "utils/conversionUtils";
import { isTestnet } from 'store/config'; import { isTestnet } from "store/config";
export default function MoneroAddressTextField({ export default function MoneroAddressTextField({
address, address,
@ -16,10 +16,10 @@ export default function MoneroAddressTextField({
onAddressValidityChange: (valid: boolean) => void; onAddressValidityChange: (valid: boolean) => void;
helperText: string; helperText: string;
} & TextFieldProps) { } & TextFieldProps) {
const placeholder = isTestnet() ? '59McWTPGc745...' : '888tNkZrPN6J...'; const placeholder = isTestnet() ? "59McWTPGc745..." : "888tNkZrPN6J...";
const errorText = isXmrAddressValid(address, isTestnet()) const errorText = isXmrAddressValid(address, isTestnet())
? null ? null
: 'Not a valid Monero address'; : "Not a valid Monero address";
useEffect(() => { useEffect(() => {
onAddressValidityChange(!errorText); onAddressValidityChange(!errorText);

View file

@ -1,9 +1,9 @@
import { DialogTitle, makeStyles, Typography } from '@material-ui/core'; import { DialogTitle, makeStyles, Typography } from "@material-ui/core";
const useStyles = makeStyles({ const useStyles = makeStyles({
root: { root: {
display: 'flex', display: "flex",
justifyContent: 'space-between', justifyContent: "space-between",
}, },
}); });

View file

@ -1,12 +1,12 @@
import { Button, makeStyles, Paper, Typography } from '@material-ui/core'; import { Button, makeStyles, Paper, Typography } from "@material-ui/core";
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles((theme) => ({
logsOuter: { logsOuter: {
overflow: 'auto', overflow: "auto",
padding: theme.spacing(1), padding: theme.spacing(1),
marginTop: theme.spacing(1), marginTop: theme.spacing(1),
marginBottom: theme.spacing(1), marginBottom: theme.spacing(1),
maxHeight: '10rem', maxHeight: "10rem",
}, },
copyButton: { copyButton: {
marginTop: theme.spacing(1), marginTop: theme.spacing(1),
@ -17,7 +17,7 @@ export default function PaperTextBox({ stdOut }: { stdOut: string }) {
const classes = useStyles(); const classes = useStyles();
function handleCopyLogs() { function handleCopyLogs() {
throw new Error('Not implemented'); throw new Error("Not implemented");
} }
return ( return (

View file

@ -5,8 +5,8 @@ import {
DialogContent, DialogContent,
DialogContentText, DialogContentText,
DialogTitle, DialogTitle,
} from '@material-ui/core'; } from "@material-ui/core";
import IpcInvokeButton from '../IpcInvokeButton'; import IpcInvokeButton from "../IpcInvokeButton";
type SwapCancelAlertProps = { type SwapCancelAlertProps = {
open: boolean; open: boolean;

View file

@ -69,8 +69,7 @@ function SwapSelectDropDown({
{swaps.map((swap) => ( {swaps.map((swap) => (
<MenuItem value={swap.swap_id}> <MenuItem value={swap.swap_id}>
Swap {swap.swap_id.substring(0, 5)}... from{" "} Swap {swap.swap_id.substring(0, 5)}... from{" "}
{new Date(parseDateString(swap.start_date)).toDateString()}{" "} {new Date(parseDateString(swap.start_date)).toDateString()} (
(
<PiconeroAmount amount={swap.xmr_amount} />) <PiconeroAmount amount={swap.xmr_amount} />)
</MenuItem> </MenuItem>
))} ))}
@ -104,11 +103,11 @@ export default function FeedbackDialog({
<DialogTitle>Submit Feedback</DialogTitle> <DialogTitle>Submit Feedback</DialogTitle>
<DialogContent> <DialogContent>
<DialogContentText> <DialogContentText>
Got something to say? Drop us a message below. If you had an Got something to say? Drop us a message below. If you had an issue
issue with a specific swap, select it from the dropdown to with a specific swap, select it from the dropdown to attach the logs.
attach the logs. It will help us figure out what went wrong. It will help us figure out what went wrong. Hit that submit button
Hit that submit button when you are ready. We appreciate you when you are ready. We appreciate you taking the time to share your
taking the time to share your thoughts! thoughts!
</DialogContentText> </DialogContentText>
<Box <Box
style={{ style={{
@ -150,24 +149,15 @@ export default function FeedbackDialog({
try { try {
setPending(true); setPending(true);
await submitFeedback( await submitFeedback(bodyText, selectedAttachedSwap);
bodyText, enqueueSnackbar("Feedback submitted successfully!", {
selectedAttachedSwap,
);
enqueueSnackbar(
"Feedback submitted successfully!",
{
variant: "success", variant: "success",
}, });
);
} catch (e) { } catch (e) {
console.error(`Failed to submit feedback: ${e}`); console.error(`Failed to submit feedback: ${e}`);
enqueueSnackbar( enqueueSnackbar(`Failed to submit feedback (${e})`, {
`Failed to submit feedback (${e})`,
{
variant: "error", variant: "error",
}, });
);
} finally { } finally {
setPending(false); setPending(false);
} }

View file

@ -1,4 +1,4 @@
import { ChangeEvent, useState } from 'react'; import { ChangeEvent, useState } from "react";
import { import {
DialogTitle, DialogTitle,
Dialog, Dialog,
@ -11,20 +11,20 @@ import {
Chip, Chip,
makeStyles, makeStyles,
Theme, Theme,
} from '@material-ui/core'; } from "@material-ui/core";
import { Multiaddr } from 'multiaddr'; import { Multiaddr } from "multiaddr";
import { useSnackbar } from 'notistack'; import { useSnackbar } from "notistack";
import IpcInvokeButton from '../../IpcInvokeButton'; import IpcInvokeButton from "../../IpcInvokeButton";
const PRESET_RENDEZVOUS_POINTS = [ const PRESET_RENDEZVOUS_POINTS = [
'/dns4/discover.unstoppableswap.net/tcp/8888/p2p/12D3KooWA6cnqJpVnreBVnoro8midDL9Lpzmg8oJPoAGi7YYaamE', "/dns4/discover.unstoppableswap.net/tcp/8888/p2p/12D3KooWA6cnqJpVnreBVnoro8midDL9Lpzmg8oJPoAGi7YYaamE",
'/dns4/eratosthen.es/tcp/7798/p2p/12D3KooWAh7EXXa2ZyegzLGdjvj1W4G3EXrTGrf6trraoT1MEobs', "/dns4/eratosthen.es/tcp/7798/p2p/12D3KooWAh7EXXa2ZyegzLGdjvj1W4G3EXrTGrf6trraoT1MEobs",
]; ];
const useStyles = makeStyles((theme: Theme) => ({ const useStyles = makeStyles((theme: Theme) => ({
chipOuter: { chipOuter: {
display: 'flex', display: "flex",
flexWrap: 'wrap', flexWrap: "wrap",
gap: theme.spacing(1), gap: theme.spacing(1),
}, },
})); }));
@ -39,7 +39,7 @@ export default function ListSellersDialog({
onClose, onClose,
}: ListSellersDialogProps) { }: ListSellersDialogProps) {
const classes = useStyles(); const classes = useStyles();
const [rendezvousAddress, setRendezvousAddress] = useState(''); const [rendezvousAddress, setRendezvousAddress] = useState("");
const { enqueueSnackbar } = useSnackbar(); const { enqueueSnackbar } = useSnackbar();
function handleMultiAddrChange(event: ChangeEvent<HTMLInputElement>) { function handleMultiAddrChange(event: ChangeEvent<HTMLInputElement>) {
@ -49,12 +49,12 @@ export default function ListSellersDialog({
function getMultiAddressError(): string | null { function getMultiAddressError(): string | null {
try { try {
const multiAddress = new Multiaddr(rendezvousAddress); const multiAddress = new Multiaddr(rendezvousAddress);
if (!multiAddress.protoNames().includes('p2p')) { if (!multiAddress.protoNames().includes("p2p")) {
return 'The multi address must contain the peer id (/p2p/)'; return "The multi address must contain the peer id (/p2p/)";
} }
return null; return null;
} catch (e) { } catch (e) {
return 'Not a valid multi address'; return "Not a valid multi address";
} }
} }
@ -73,7 +73,7 @@ export default function ListSellersDialog({
} }
enqueueSnackbar(message, { enqueueSnackbar(message, {
variant: 'success', variant: "success",
autoHideDuration: 5000, autoHideDuration: 5000,
}); });
@ -96,7 +96,7 @@ export default function ListSellersDialog({
label="Rendezvous point" label="Rendezvous point"
fullWidth fullWidth
helperText={ helperText={
getMultiAddressError() || 'Multiaddress of the rendezvous point' getMultiAddressError() || "Multiaddress of the rendezvous point"
} }
value={rendezvousAddress} value={rendezvousAddress}
onChange={handleMultiAddrChange} onChange={handleMultiAddrChange}

View file

@ -1,24 +1,24 @@
import { makeStyles, Box, Typography, Chip, Tooltip } from '@material-ui/core'; import { makeStyles, Box, Typography, Chip, Tooltip } from "@material-ui/core";
import { VerifiedUser } from '@material-ui/icons'; import { VerifiedUser } from "@material-ui/icons";
import { satsToBtc, secondsToDays } from 'utils/conversionUtils'; import { satsToBtc, secondsToDays } from "utils/conversionUtils";
import { ExtendedProviderStatus } from 'models/apiModel'; import { ExtendedProviderStatus } from "models/apiModel";
import { import {
MoneroBitcoinExchangeRate, MoneroBitcoinExchangeRate,
SatsAmount, SatsAmount,
} from 'renderer/components/other/Units'; } from "renderer/components/other/Units";
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles((theme) => ({
content: { content: {
flex: 1, flex: 1,
'& *': { "& *": {
lineBreak: 'anywhere', lineBreak: "anywhere",
}, },
}, },
chipsOuter: { chipsOuter: {
display: 'flex', display: "flex",
marginTop: theme.spacing(1), marginTop: theme.spacing(1),
gap: theme.spacing(0.5), gap: theme.spacing(0.5),
flexWrap: 'wrap', flexWrap: "wrap",
}, },
})); }));
@ -41,7 +41,7 @@ export default function ProviderInfo({
{provider.peerId.substring(0, 8)}...{provider.peerId.slice(-8)} {provider.peerId.substring(0, 8)}...{provider.peerId.slice(-8)}
</Typography> </Typography>
<Typography variant="caption"> <Typography variant="caption">
Exchange rate:{' '} Exchange rate:{" "}
<MoneroBitcoinExchangeRate rate={satsToBtc(provider.price)} /> <MoneroBitcoinExchangeRate rate={satsToBtc(provider.price)} />
<br /> <br />
Minimum swap amount: <SatsAmount amount={provider.minSwapAmount} /> Minimum swap amount: <SatsAmount amount={provider.minSwapAmount} />
@ -49,7 +49,7 @@ export default function ProviderInfo({
Maximum swap amount: <SatsAmount amount={provider.maxSwapAmount} /> Maximum swap amount: <SatsAmount amount={provider.maxSwapAmount} />
</Typography> </Typography>
<Box className={classes.chipsOuter}> <Box className={classes.chipsOuter}>
<Chip label={provider.testnet ? 'Testnet' : 'Mainnet'} /> <Chip label={provider.testnet ? "Testnet" : "Mainnet"} />
{provider.uptime && ( {provider.uptime && (
<Tooltip title="A high uptime indicates reliability. Providers with low uptime may be unreliable and cause swaps to take longer to complete or fail entirely."> <Tooltip title="A high uptime indicates reliability. Providers with low uptime may be unreliable and cause swaps to take longer to complete or fail entirely.">
<Chip label={`${Math.round(provider.uptime * 100)} % uptime`} /> <Chip label={`${Math.round(provider.uptime * 100)} % uptime`} />
@ -58,7 +58,7 @@ export default function ProviderInfo({
{provider.age ? ( {provider.age ? (
<Chip <Chip
label={`Went online ${Math.round(secondsToDays(provider.age))} ${ label={`Went online ${Math.round(secondsToDays(provider.age))} ${
provider.age === 1 ? 'day' : 'days' provider.age === 1 ? "day" : "days"
} ago`} } ago`}
/> />
) : ( ) : (

View file

@ -11,21 +11,21 @@ import {
DialogContent, DialogContent,
makeStyles, makeStyles,
CircularProgress, CircularProgress,
} from '@material-ui/core'; } from "@material-ui/core";
import AddIcon from '@material-ui/icons/Add'; import AddIcon from "@material-ui/icons/Add";
import { useState } from 'react'; import { useState } from "react";
import SearchIcon from '@material-ui/icons/Search'; import SearchIcon from "@material-ui/icons/Search";
import { ExtendedProviderStatus } from 'models/apiModel'; import { ExtendedProviderStatus } from "models/apiModel";
import { import {
useAllProviders, useAllProviders,
useAppDispatch, useAppDispatch,
useIsRpcEndpointBusy, useIsRpcEndpointBusy,
} from 'store/hooks'; } from "store/hooks";
import { setSelectedProvider } from 'store/features/providersSlice'; import { setSelectedProvider } from "store/features/providersSlice";
import { RpcMethod } from 'models/rpcModel'; import { RpcMethod } from "models/rpcModel";
import ProviderSubmitDialog from './ProviderSubmitDialog'; import ProviderSubmitDialog from "./ProviderSubmitDialog";
import ListSellersDialog from '../listSellers/ListSellersDialog'; import ListSellersDialog from "../listSellers/ListSellersDialog";
import ProviderInfo from './ProviderInfo'; import ProviderInfo from "./ProviderInfo";
const useStyles = makeStyles({ const useStyles = makeStyles({
dialogContent: { dialogContent: {

View file

@ -4,25 +4,25 @@ import {
CardContent, CardContent,
Box, Box,
IconButton, IconButton,
} from '@material-ui/core'; } from "@material-ui/core";
import ArrowForwardIosIcon from '@material-ui/icons/ArrowForwardIos'; import ArrowForwardIosIcon from "@material-ui/icons/ArrowForwardIos";
import { useState } from 'react'; import { useState } from "react";
import { useAppSelector } from 'store/hooks'; import { useAppSelector } from "store/hooks";
import ProviderInfo from './ProviderInfo'; import ProviderInfo from "./ProviderInfo";
import ProviderListDialog from './ProviderListDialog'; import ProviderListDialog from "./ProviderListDialog";
const useStyles = makeStyles({ const useStyles = makeStyles({
inner: { inner: {
textAlign: 'left', textAlign: "left",
width: '100%', width: "100%",
height: '100%', height: "100%",
}, },
providerCard: { providerCard: {
width: '100%', width: "100%",
}, },
providerCardContent: { providerCardContent: {
display: 'flex', display: "flex",
alignItems: 'center', alignItems: "center",
}, },
}); });

View file

@ -1,4 +1,4 @@
import { ChangeEvent, useState } from 'react'; import { ChangeEvent, useState } from "react";
import { import {
DialogTitle, DialogTitle,
Dialog, Dialog,
@ -7,8 +7,8 @@ import {
TextField, TextField,
DialogActions, DialogActions,
Button, Button,
} from '@material-ui/core'; } from "@material-ui/core";
import { Multiaddr } from 'multiaddr'; import { Multiaddr } from "multiaddr";
type ProviderSubmitDialogProps = { type ProviderSubmitDialogProps = {
open: boolean; open: boolean;
@ -19,23 +19,23 @@ export default function ProviderSubmitDialog({
open, open,
onClose, onClose,
}: ProviderSubmitDialogProps) { }: ProviderSubmitDialogProps) {
const [multiAddr, setMultiAddr] = useState(''); const [multiAddr, setMultiAddr] = useState("");
const [peerId, setPeerId] = useState(''); const [peerId, setPeerId] = useState("");
async function handleProviderSubmit() { async function handleProviderSubmit() {
if (multiAddr && peerId) { if (multiAddr && peerId) {
await fetch('https://api.unstoppableswap.net/api/submit-provider', { await fetch("https://api.unstoppableswap.net/api/submit-provider", {
method: 'post', method: "post",
headers: { headers: {
'Content-Type': 'application/json', "Content-Type": "application/json",
}, },
body: JSON.stringify({ body: JSON.stringify({
multiAddr, multiAddr,
peerId, peerId,
}), }),
}); });
setMultiAddr(''); setMultiAddr("");
setPeerId(''); setPeerId("");
onClose(); onClose();
} }
} }
@ -51,15 +51,15 @@ export default function ProviderSubmitDialog({
function getMultiAddressError(): string | null { function getMultiAddressError(): string | null {
try { try {
const multiAddress = new Multiaddr(multiAddr); const multiAddress = new Multiaddr(multiAddr);
if (multiAddress.protoNames().includes('p2p')) { if (multiAddress.protoNames().includes("p2p")) {
return 'The multi address should not contain the peer id (/p2p/)'; return "The multi address should not contain the peer id (/p2p/)";
} }
if (multiAddress.protoNames().find((name) => name.includes('onion'))) { if (multiAddress.protoNames().find((name) => name.includes("onion"))) {
return 'It is currently not possible to add a provider that is only reachable via Tor'; return "It is currently not possible to add a provider that is only reachable via Tor";
} }
return null; return null;
} catch (e) { } catch (e) {
return 'Not a valid multi address'; return "Not a valid multi address";
} }
} }
@ -78,7 +78,7 @@ export default function ProviderSubmitDialog({
fullWidth fullWidth
helperText={ helperText={
getMultiAddressError() || getMultiAddressError() ||
'Tells the swap client where the provider can be reached' "Tells the swap client where the provider can be reached"
} }
value={multiAddr} value={multiAddr}
onChange={handleMultiAddrChange} onChange={handleMultiAddrChange}

View file

@ -1,18 +1,18 @@
import QRCode from 'react-qr-code'; import QRCode from "react-qr-code";
import { Box } from '@material-ui/core'; import { Box } from "@material-ui/core";
export default function BitcoinQrCode({ address }: { address: string }) { export default function BitcoinQrCode({ address }: { address: string }) {
return ( return (
<Box <Box
style={{ style={{
height: '100%', height: "100%",
margin: '0 auto', margin: "0 auto",
}} }}
> >
<QRCode <QRCode
value={`bitcoin:${address}`} value={`bitcoin:${address}`}
size={256} size={256}
style={{ height: 'auto', maxWidth: '100%', width: '100%' }} style={{ height: "auto", maxWidth: "100%", width: "100%" }}
/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */ /* eslint-disable-next-line @typescript-eslint/ban-ts-comment */
/* @ts-ignore */ /* @ts-ignore */
viewBox="0 0 256 256" viewBox="0 0 256 256"

View file

@ -1,8 +1,8 @@
import { isTestnet } from 'store/config'; import { isTestnet } from "store/config";
import { getBitcoinTxExplorerUrl } from 'utils/conversionUtils'; import { getBitcoinTxExplorerUrl } from "utils/conversionUtils";
import BitcoinIcon from 'renderer/components/icons/BitcoinIcon'; import BitcoinIcon from "renderer/components/icons/BitcoinIcon";
import { ReactNode } from 'react'; import { ReactNode } from "react";
import TransactionInfoBox from './TransactionInfoBox'; import TransactionInfoBox from "./TransactionInfoBox";
type Props = { type Props = {
title: string; title: string;

View file

@ -3,8 +3,8 @@ import {
CircularProgress, CircularProgress,
makeStyles, makeStyles,
Typography, Typography,
} from '@material-ui/core'; } from "@material-ui/core";
import { ReactNode } from 'react'; import { ReactNode } from "react";
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles((theme) => ({
subtitle: { subtitle: {

View file

@ -1,12 +1,12 @@
import { Button } from '@material-ui/core'; import { Button } from "@material-ui/core";
import { ButtonProps } from '@material-ui/core/Button/Button'; import { ButtonProps } from "@material-ui/core/Button/Button";
export default function ClipboardIconButton({ export default function ClipboardIconButton({
text, text,
...props ...props
}: { text: string } & ButtonProps) { }: { text: string } & ButtonProps) {
function writeToClipboard() { function writeToClipboard() {
throw new Error('Not implemented'); throw new Error("Not implemented");
} }
return ( return (

View file

@ -1,9 +1,9 @@
import { ReactNode } from 'react'; import { ReactNode } from "react";
import { Box, Typography } from '@material-ui/core'; import { Box, Typography } from "@material-ui/core";
import FileCopyOutlinedIcon from '@material-ui/icons/FileCopyOutlined'; import FileCopyOutlinedIcon from "@material-ui/icons/FileCopyOutlined";
import InfoBox from './InfoBox'; import InfoBox from "./InfoBox";
import ClipboardIconButton from './ClipbiardIconButton'; import ClipboardIconButton from "./ClipbiardIconButton";
import BitcoinQrCode from './BitcoinQrCode'; import BitcoinQrCode from "./BitcoinQrCode";
type Props = { type Props = {
title: string; title: string;
@ -34,10 +34,10 @@ export default function DepositAddressInfoBox({
/> />
<Box <Box
style={{ style={{
display: 'flex', display: "flex",
flexDirection: 'row', flexDirection: "row",
gap: '0.5rem', gap: "0.5rem",
alignItems: 'center', alignItems: "center",
}} }}
> >
<Box>{additionalContent}</Box> <Box>{additionalContent}</Box>

View file

@ -4,8 +4,8 @@ import {
makeStyles, makeStyles,
Paper, Paper,
Typography, Typography,
} from '@material-ui/core'; } from "@material-ui/core";
import { ReactNode } from 'react'; import { ReactNode } from "react";
type Props = { type Props = {
title: ReactNode; title: ReactNode;
@ -18,14 +18,14 @@ type Props = {
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles((theme) => ({
outer: { outer: {
padding: theme.spacing(1.5), padding: theme.spacing(1.5),
overflow: 'hidden', overflow: "hidden",
display: 'flex', display: "flex",
flexDirection: 'column', flexDirection: "column",
gap: theme.spacing(1), gap: theme.spacing(1),
}, },
upperContent: { upperContent: {
display: 'flex', display: "flex",
alignItems: 'center', alignItems: "center",
gap: theme.spacing(0.5), gap: theme.spacing(0.5),
}, },
})); }));

View file

@ -1,8 +1,8 @@
import { isTestnet } from 'store/config'; import { isTestnet } from "store/config";
import { getMoneroTxExplorerUrl } from 'utils/conversionUtils'; import { getMoneroTxExplorerUrl } from "utils/conversionUtils";
import MoneroIcon from 'renderer/components/icons/MoneroIcon'; import MoneroIcon from "renderer/components/icons/MoneroIcon";
import { ReactNode } from 'react'; import { ReactNode } from "react";
import TransactionInfoBox from './TransactionInfoBox'; import TransactionInfoBox from "./TransactionInfoBox";
type Props = { type Props = {
title: string; title: string;

View file

@ -1,25 +1,25 @@
import { useState } from 'react'; import { useState } from "react";
import { import {
Button, Button,
Dialog, Dialog,
DialogActions, DialogActions,
DialogContent, DialogContent,
makeStyles, makeStyles,
} from '@material-ui/core'; } from "@material-ui/core";
import { useAppDispatch, useAppSelector } from 'store/hooks'; import { useAppDispatch, useAppSelector } from "store/hooks";
import { swapReset } from 'store/features/swapSlice'; import { swapReset } from "store/features/swapSlice";
import SwapStatePage from './pages/SwapStatePage'; import SwapStatePage from "./pages/SwapStatePage";
import SwapStateStepper from './SwapStateStepper'; import SwapStateStepper from "./SwapStateStepper";
import SwapSuspendAlert from '../SwapSuspendAlert'; import SwapSuspendAlert from "../SwapSuspendAlert";
import SwapDialogTitle from './SwapDialogTitle'; import SwapDialogTitle from "./SwapDialogTitle";
import DebugPage from './pages/DebugPage'; import DebugPage from "./pages/DebugPage";
const useStyles = makeStyles({ const useStyles = makeStyles({
content: { content: {
minHeight: '25rem', minHeight: "25rem",
display: 'flex', display: "flex",
flexDirection: 'column', flexDirection: "column",
justifyContent: 'space-between', justifyContent: "space-between",
}, },
}); });

View file

@ -1,22 +1,17 @@
import { import { Box, DialogTitle, makeStyles, Typography } from "@material-ui/core";
Box, import TorStatusBadge from "./pages/TorStatusBadge";
DialogTitle, import FeedbackSubmitBadge from "./pages/FeedbackSubmitBadge";
makeStyles, import DebugPageSwitchBadge from "./pages/DebugPageSwitchBadge";
Typography,
} from '@material-ui/core';
import TorStatusBadge from './pages/TorStatusBadge';
import FeedbackSubmitBadge from './pages/FeedbackSubmitBadge';
import DebugPageSwitchBadge from './pages/DebugPageSwitchBadge';
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles((theme) => ({
root: { root: {
display: 'flex', display: "flex",
justifyContent: 'space-between', justifyContent: "space-between",
alignItems: 'center', alignItems: "center",
}, },
rightSide: { rightSide: {
display: 'flex', display: "flex",
alignItems: 'center', alignItems: "center",
gridGap: theme.spacing(1), gridGap: theme.spacing(1),
}, },
})); }));

View file

@ -152,10 +152,7 @@ export default function SwapStateStepper() {
const currentSwapSpawnType = useAppSelector((s) => s.swap.spawnType); const currentSwapSpawnType = useAppSelector((s) => s.swap.spawnType);
const stateName = useActiveSwapInfo()?.state_name ?? null; const stateName = useActiveSwapInfo()?.state_name ?? null;
const processExited = useAppSelector((s) => !s.swap.processRunning); const processExited = useAppSelector((s) => !s.swap.processRunning);
const [pathType, activeStep, error] = getActiveStep( const [pathType, activeStep, error] = getActiveStep(stateName, processExited);
stateName,
processExited,
);
// If the current swap is being manually cancelled and refund, we want to show the unhappy path even though the current state is not a "unhappy" state // If the current swap is being manually cancelled and refund, we want to show the unhappy path even though the current state is not a "unhappy" state
if (currentSwapSpawnType === SwapSpawnType.CANCEL_REFUND) { if (currentSwapSpawnType === SwapSpawnType.CANCEL_REFUND) {

View file

@ -1,6 +1,6 @@
import { Link, Typography } from '@material-ui/core'; import { Link, Typography } from "@material-ui/core";
import { ReactNode } from 'react'; import { ReactNode } from "react";
import InfoBox from './InfoBox'; import InfoBox from "./InfoBox";
type TransactionInfoBoxProps = { type TransactionInfoBoxProps = {
title: string; title: string;

View file

@ -1,6 +1,6 @@
import { Tooltip } from '@material-ui/core'; import { Tooltip } from "@material-ui/core";
import IconButton from '@material-ui/core/IconButton'; import IconButton from "@material-ui/core/IconButton";
import DeveloperBoardIcon from '@material-ui/icons/DeveloperBoard'; import DeveloperBoardIcon from "@material-ui/icons/DeveloperBoard";
export default function DebugPageSwitchBadge({ export default function DebugPageSwitchBadge({
enabled, enabled,
@ -14,10 +14,10 @@ export default function DebugPageSwitchBadge({
}; };
return ( return (
<Tooltip title={enabled ? 'Hide debug view' : 'Show debug view'}> <Tooltip title={enabled ? "Hide debug view" : "Show debug view"}>
<IconButton <IconButton
onClick={handleToggle} onClick={handleToggle}
color={enabled ? 'primary' : 'default'} color={enabled ? "primary" : "default"}
> >
<DeveloperBoardIcon /> <DeveloperBoardIcon />
</IconButton> </IconButton>

View file

@ -1,7 +1,7 @@
import { IconButton } from '@material-ui/core'; import { IconButton } from "@material-ui/core";
import FeedbackIcon from '@material-ui/icons/Feedback'; import FeedbackIcon from "@material-ui/icons/Feedback";
import FeedbackDialog from '../../feedback/FeedbackDialog'; import FeedbackDialog from "../../feedback/FeedbackDialog";
import { useState } from 'react'; import { useState } from "react";
export default function FeedbackSubmitBadge() { export default function FeedbackSubmitBadge() {
const [showFeedbackDialog, setShowFeedbackDialog] = useState(false); const [showFeedbackDialog, setShowFeedbackDialog] = useState(false);

View file

@ -1,5 +1,5 @@
import { Box } from '@material-ui/core'; import { Box } from "@material-ui/core";
import { useAppSelector } from 'store/hooks'; import { useAppSelector } from "store/hooks";
import { import {
isSwapStateBtcCancelled, isSwapStateBtcCancelled,
isSwapStateBtcLockInMempool, isSwapStateBtcLockInMempool,
@ -15,23 +15,23 @@ import {
isSwapStateXmrLockInMempool, isSwapStateXmrLockInMempool,
isSwapStateXmrRedeemInMempool, isSwapStateXmrRedeemInMempool,
SwapState, SwapState,
} from '../../../../../models/storeModel'; } from "../../../../../models/storeModel";
import InitiatedPage from './init/InitiatedPage'; import InitiatedPage from "./init/InitiatedPage";
import WaitingForBitcoinDepositPage from './init/WaitingForBitcoinDepositPage'; import WaitingForBitcoinDepositPage from "./init/WaitingForBitcoinDepositPage";
import StartedPage from './in_progress/StartedPage'; import StartedPage from "./in_progress/StartedPage";
import BitcoinLockTxInMempoolPage from './in_progress/BitcoinLockTxInMempoolPage'; import BitcoinLockTxInMempoolPage from "./in_progress/BitcoinLockTxInMempoolPage";
import XmrLockTxInMempoolPage from './in_progress/XmrLockInMempoolPage'; import XmrLockTxInMempoolPage from "./in_progress/XmrLockInMempoolPage";
// eslint-disable-next-line import/no-cycle // eslint-disable-next-line import/no-cycle
import ProcessExitedPage from './exited/ProcessExitedPage'; import ProcessExitedPage from "./exited/ProcessExitedPage";
import XmrRedeemInMempoolPage from './done/XmrRedeemInMempoolPage'; import XmrRedeemInMempoolPage from "./done/XmrRedeemInMempoolPage";
import ReceivedQuotePage from './in_progress/ReceivedQuotePage'; import ReceivedQuotePage from "./in_progress/ReceivedQuotePage";
import BitcoinRedeemedPage from './in_progress/BitcoinRedeemedPage'; import BitcoinRedeemedPage from "./in_progress/BitcoinRedeemedPage";
import InitPage from './init/InitPage'; import InitPage from "./init/InitPage";
import XmrLockedPage from './in_progress/XmrLockedPage'; import XmrLockedPage from "./in_progress/XmrLockedPage";
import BitcoinCancelledPage from './in_progress/BitcoinCancelledPage'; import BitcoinCancelledPage from "./in_progress/BitcoinCancelledPage";
import BitcoinRefundedPage from './done/BitcoinRefundedPage'; import BitcoinRefundedPage from "./done/BitcoinRefundedPage";
import BitcoinPunishedPage from './done/BitcoinPunishedPage'; import BitcoinPunishedPage from "./done/BitcoinPunishedPage";
import { SyncingMoneroWalletPage } from './in_progress/SyncingMoneroWalletPage'; import { SyncingMoneroWalletPage } from "./in_progress/SyncingMoneroWalletPage";
export default function SwapStatePage({ export default function SwapStatePage({
swapState, swapState,

View file

@ -1,6 +1,6 @@
import { IconButton, Tooltip } from '@material-ui/core'; import { IconButton, Tooltip } from "@material-ui/core";
import { useAppSelector } from 'store/hooks'; import { useAppSelector } from "store/hooks";
import TorIcon from '../../../icons/TorIcon'; import TorIcon from "../../../icons/TorIcon";
export default function TorStatusBadge() { export default function TorStatusBadge() {
const tor = useAppSelector((s) => s.tor); const tor = useAppSelector((s) => s.tor);

View file

@ -1,5 +1,5 @@
import { Box, DialogContentText } from '@material-ui/core'; import { Box, DialogContentText } from "@material-ui/core";
import FeedbackInfoBox from '../../../../pages/help/FeedbackInfoBox'; import FeedbackInfoBox from "../../../../pages/help/FeedbackInfoBox";
export default function BitcoinPunishedPage() { export default function BitcoinPunishedPage() {
return ( return (

View file

@ -17,10 +17,9 @@ export default function BitcoinRefundedPage({
return ( return (
<Box> <Box>
<DialogContentText> <DialogContentText>
Unfortunately, the swap was not successful. However, rest Unfortunately, the swap was not successful. However, rest assured that
assured that all your Bitcoin has been refunded to the specified all your Bitcoin has been refunded to the specified address. The swap
address. The swap process is now complete, and you are free to process is now complete, and you are free to exit the application.
exit the application.
</DialogContentText> </DialogContentText>
<Box <Box
style={{ style={{

View file

@ -1,9 +1,9 @@
import { Box, DialogContentText } from '@material-ui/core'; import { Box, DialogContentText } from "@material-ui/core";
import { SwapStateXmrRedeemInMempool } from 'models/storeModel'; import { SwapStateXmrRedeemInMempool } from "models/storeModel";
import { useActiveSwapInfo } from 'store/hooks'; import { useActiveSwapInfo } from "store/hooks";
import { getSwapXmrAmount } from 'models/rpcModel'; import { getSwapXmrAmount } from "models/rpcModel";
import MoneroTransactionInfoBox from '../../MoneroTransactionInfoBox'; import MoneroTransactionInfoBox from "../../MoneroTransactionInfoBox";
import FeedbackInfoBox from '../../../../pages/help/FeedbackInfoBox'; import FeedbackInfoBox from "../../../../pages/help/FeedbackInfoBox";
type XmrRedeemInMempoolPageProps = { type XmrRedeemInMempoolPageProps = {
state: SwapStateXmrRedeemInMempool | null; state: SwapStateXmrRedeemInMempool | null;
@ -27,9 +27,9 @@ export default function XmrRedeemInMempoolPage({
</DialogContentText> </DialogContentText>
<Box <Box
style={{ style={{
display: 'flex', display: "flex",
flexDirection: 'column', flexDirection: "column",
gap: '0.5rem', gap: "0.5rem",
}} }}
> >
{state && ( {state && (

View file

@ -1,4 +1,4 @@
import CircularProgressWithSubtitle from '../../CircularProgressWithSubtitle'; import CircularProgressWithSubtitle from "../../CircularProgressWithSubtitle";
export default function BitcoinCancelledPage() { export default function BitcoinCancelledPage() {
return <CircularProgressWithSubtitle description="Refunding your Bitcoin" />; return <CircularProgressWithSubtitle description="Refunding your Bitcoin" />;

View file

@ -1,7 +1,7 @@
import { Box, DialogContentText } from '@material-ui/core'; import { Box, DialogContentText } from "@material-ui/core";
import { SwapStateBtcLockInMempool } from 'models/storeModel'; import { SwapStateBtcLockInMempool } from "models/storeModel";
import BitcoinTransactionInfoBox from '../../BitcoinTransactionInfoBox'; import BitcoinTransactionInfoBox from "../../BitcoinTransactionInfoBox";
import SwapMightBeCancelledAlert from '../../../../alert/SwapMightBeCancelledAlert'; import SwapMightBeCancelledAlert from "../../../../alert/SwapMightBeCancelledAlert";
type BitcoinLockTxInMempoolPageProps = { type BitcoinLockTxInMempoolPageProps = {
state: SwapStateBtcLockInMempool; state: SwapStateBtcLockInMempool;

View file

@ -1,4 +1,4 @@
import CircularProgressWithSubtitle from '../../CircularProgressWithSubtitle'; import CircularProgressWithSubtitle from "../../CircularProgressWithSubtitle";
export default function BitcoinRedeemedPage() { export default function BitcoinRedeemedPage() {
return <CircularProgressWithSubtitle description="Redeeming your Monero" />; return <CircularProgressWithSubtitle description="Redeeming your Monero" />;

View file

@ -1,4 +1,4 @@
import CircularProgressWithSubtitle from '../../CircularProgressWithSubtitle'; import CircularProgressWithSubtitle from "../../CircularProgressWithSubtitle";
export default function ReceivedQuotePage() { export default function ReceivedQuotePage() {
return ( return (

View file

@ -1,6 +1,6 @@
import { SwapStateStarted } from 'models/storeModel'; import { SwapStateStarted } from "models/storeModel";
import { BitcoinAmount } from 'renderer/components/other/Units'; import { BitcoinAmount } from "renderer/components/other/Units";
import CircularProgressWithSubtitle from '../../CircularProgressWithSubtitle'; import CircularProgressWithSubtitle from "../../CircularProgressWithSubtitle";
export default function StartedPage({ state }: { state: SwapStateStarted }) { export default function StartedPage({ state }: { state: SwapStateStarted }) {
const description = state.txLockDetails ? ( const description = state.txLockDetails ? (
@ -9,7 +9,7 @@ export default function StartedPage({ state }: { state: SwapStateStarted }) {
network fee of <BitcoinAmount amount={state.txLockDetails.fees} /> network fee of <BitcoinAmount amount={state.txLockDetails.fees} />
</> </>
) : ( ) : (
'Locking Bitcoin' "Locking Bitcoin"
); );
return <CircularProgressWithSubtitle description={description} />; return <CircularProgressWithSubtitle description={description} />;

View file

@ -1,4 +1,4 @@
import CircularProgressWithSubtitle from '../../CircularProgressWithSubtitle'; import CircularProgressWithSubtitle from "../../CircularProgressWithSubtitle";
export function SyncingMoneroWalletPage() { export function SyncingMoneroWalletPage() {
return ( return (

View file

@ -1,6 +1,6 @@
import { Box, DialogContentText } from '@material-ui/core'; import { Box, DialogContentText } from "@material-ui/core";
import { SwapStateXmrLockInMempool } from 'models/storeModel'; import { SwapStateXmrLockInMempool } from "models/storeModel";
import MoneroTransactionInfoBox from '../../MoneroTransactionInfoBox'; import MoneroTransactionInfoBox from "../../MoneroTransactionInfoBox";
type XmrLockTxInMempoolPageProps = { type XmrLockTxInMempoolPageProps = {
state: SwapStateXmrLockInMempool; state: SwapStateXmrLockInMempool;

View file

@ -1,4 +1,4 @@
import CircularProgressWithSubtitle from '../../CircularProgressWithSubtitle'; import CircularProgressWithSubtitle from "../../CircularProgressWithSubtitle";
export default function XmrLockedPage() { export default function XmrLockedPage() {
return ( return (

View file

@ -1,24 +1,24 @@
import { useState } from 'react'; import { useState } from "react";
import { Box, makeStyles, TextField, Typography } from '@material-ui/core'; import { Box, makeStyles, TextField, Typography } from "@material-ui/core";
import { SwapStateWaitingForBtcDeposit } from 'models/storeModel'; import { SwapStateWaitingForBtcDeposit } from "models/storeModel";
import { useAppSelector } from 'store/hooks'; import { useAppSelector } from "store/hooks";
import { satsToBtc } from 'utils/conversionUtils'; import { satsToBtc } from "utils/conversionUtils";
import { MoneroAmount } from '../../../../other/Units'; import { MoneroAmount } from "../../../../other/Units";
const MONERO_FEE = 0.000016; const MONERO_FEE = 0.000016;
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles((theme) => ({
outer: { outer: {
display: 'flex', display: "flex",
alignItems: 'center', alignItems: "center",
gap: theme.spacing(1), gap: theme.spacing(1),
}, },
textField: { textField: {
'& input::-webkit-outer-spin-button, & input::-webkit-inner-spin-button': { "& input::-webkit-outer-spin-button, & input::-webkit-inner-spin-button": {
display: 'none', display: "none",
}, },
'& input[type=number]': { "& input[type=number]": {
MozAppearance: 'textfield', MozAppearance: "textfield",
}, },
maxWidth: theme.spacing(16), maxWidth: theme.spacing(16),
}, },
@ -83,7 +83,7 @@ export default function DepositAmountHelper({
className={classes.textField} className={classes.textField}
/> />
<Typography variant="subtitle2"> <Typography variant="subtitle2">
BTC will give you approximately{' '} BTC will give you approximately{" "}
<MoneroAmount amount={calcXMRAmount()} />. <MoneroAmount amount={calcXMRAmount()} />.
</Typography> </Typography>
</Box> </Box>

View file

@ -1,5 +1,5 @@
import CircularProgressWithSubtitle from '../../CircularProgressWithSubtitle'; import CircularProgressWithSubtitle from "../../CircularProgressWithSubtitle";
import { MoneroWalletRpcUpdateState } from '../../../../../../models/storeModel'; import { MoneroWalletRpcUpdateState } from "../../../../../../models/storeModel";
export default function DownloadingMoneroWalletRpcPage({ export default function DownloadingMoneroWalletRpcPage({
updateState, updateState,

View file

@ -1,32 +1,28 @@
import { Box, DialogContentText, makeStyles } from '@material-ui/core'; import { Box, DialogContentText, makeStyles } from "@material-ui/core";
import { useState } from 'react'; import { useState } from "react";
import BitcoinAddressTextField from 'renderer/components/inputs/BitcoinAddressTextField'; import BitcoinAddressTextField from "renderer/components/inputs/BitcoinAddressTextField";
import MoneroAddressTextField from 'renderer/components/inputs/MoneroAddressTextField'; import MoneroAddressTextField from "renderer/components/inputs/MoneroAddressTextField";
import { useAppSelector } from 'store/hooks'; import { useAppSelector } from "store/hooks";
import PlayArrowIcon from '@material-ui/icons/PlayArrow'; import PlayArrowIcon from "@material-ui/icons/PlayArrow";
import { isTestnet } from 'store/config'; import { isTestnet } from "store/config";
import RemainingFundsWillBeUsedAlert from '../../../../alert/RemainingFundsWillBeUsedAlert'; import RemainingFundsWillBeUsedAlert from "../../../../alert/RemainingFundsWillBeUsedAlert";
import IpcInvokeButton from '../../../../IpcInvokeButton'; import IpcInvokeButton from "../../../../IpcInvokeButton";
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles((theme) => ({
initButton: { initButton: {
marginTop: theme.spacing(1), marginTop: theme.spacing(1),
}, },
fieldsOuter: { fieldsOuter: {
display: 'flex', display: "flex",
flexDirection: 'column', flexDirection: "column",
gap: theme.spacing(2), gap: theme.spacing(2),
}, },
})); }));
export default function InitPage() { export default function InitPage() {
const classes = useStyles(); const classes = useStyles();
const [redeemAddress, setRedeemAddress] = useState( const [redeemAddress, setRedeemAddress] = useState("");
'' const [refundAddress, setRefundAddress] = useState("");
);
const [refundAddress, setRefundAddress] = useState(
''
);
const [redeemAddressValid, setRedeemAddressValid] = useState(false); const [redeemAddressValid, setRedeemAddressValid] = useState(false);
const [refundAddressValid, setRefundAddressValid] = useState(false); const [refundAddressValid, setRefundAddressValid] = useState(false);
const selectedProvider = useAppSelector( const selectedProvider = useAppSelector(

View file

@ -1,19 +1,19 @@
import { useAppSelector } from 'store/hooks'; import { useAppSelector } from "store/hooks";
import { SwapSpawnType } from 'models/cliModel'; import { SwapSpawnType } from "models/cliModel";
import CircularProgressWithSubtitle from '../../CircularProgressWithSubtitle'; import CircularProgressWithSubtitle from "../../CircularProgressWithSubtitle";
export default function InitiatedPage() { export default function InitiatedPage() {
const description = useAppSelector((s) => { const description = useAppSelector((s) => {
switch (s.swap.spawnType) { switch (s.swap.spawnType) {
case SwapSpawnType.INIT: case SwapSpawnType.INIT:
return 'Requesting quote from provider...'; return "Requesting quote from provider...";
case SwapSpawnType.RESUME: case SwapSpawnType.RESUME:
return 'Resuming swap...'; return "Resuming swap...";
case SwapSpawnType.CANCEL_REFUND: case SwapSpawnType.CANCEL_REFUND:
return 'Attempting to cancel & refund swap...'; return "Attempting to cancel & refund swap...";
default: default:
// Should never be hit // Should never be hit
return 'Initiating swap...'; return "Initiating swap...";
} }
}); });

View file

@ -1,25 +1,25 @@
import { Box, makeStyles, Typography } from '@material-ui/core'; import { Box, makeStyles, Typography } from "@material-ui/core";
import { SwapStateWaitingForBtcDeposit } from 'models/storeModel'; import { SwapStateWaitingForBtcDeposit } from "models/storeModel";
import { useAppSelector } from 'store/hooks'; import { useAppSelector } from "store/hooks";
import DepositAddressInfoBox from '../../DepositAddressInfoBox'; import DepositAddressInfoBox from "../../DepositAddressInfoBox";
import BitcoinIcon from '../../../../icons/BitcoinIcon'; import BitcoinIcon from "../../../../icons/BitcoinIcon";
import DepositAmountHelper from './DepositAmountHelper'; import DepositAmountHelper from "./DepositAmountHelper";
import { import {
BitcoinAmount, BitcoinAmount,
MoneroBitcoinExchangeRate, MoneroBitcoinExchangeRate,
SatsAmount, SatsAmount,
} from '../../../../other/Units'; } from "../../../../other/Units";
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles((theme) => ({
amountHelper: { amountHelper: {
display: 'flex', display: "flex",
alignItems: 'center', alignItems: "center",
}, },
additionalContent: { additionalContent: {
paddingTop: theme.spacing(1), paddingTop: theme.spacing(1),
gap: theme.spacing(0.5), gap: theme.spacing(0.5),
display: 'flex', display: "flex",
flexDirection: 'column', flexDirection: "column",
}, },
})); }));
@ -45,13 +45,13 @@ export default function WaitingForBtcDepositPage({
<ul> <ul>
{bitcoinBalance > 0 ? ( {bitcoinBalance > 0 ? (
<li> <li>
You have already deposited{' '} You have already deposited{" "}
<SatsAmount amount={bitcoinBalance} /> <SatsAmount amount={bitcoinBalance} />
</li> </li>
) : null} ) : null}
<li> <li>
Send any amount between{' '} Send any amount between{" "}
<BitcoinAmount amount={state.minDeposit} /> and{' '} <BitcoinAmount amount={state.minDeposit} /> and{" "}
<BitcoinAmount amount={state.maxDeposit} /> to the address <BitcoinAmount amount={state.maxDeposit} /> to the address
above above
{bitcoinBalance > 0 && ( {bitcoinBalance > 0 && (
@ -60,11 +60,11 @@ export default function WaitingForBtcDepositPage({
</li> </li>
<li> <li>
All Bitcoin sent to this this address will converted into All Bitcoin sent to this this address will converted into
Monero at an exchance rate of{' '} Monero at an exchance rate of{" "}
<MoneroBitcoinExchangeRate rate={state.price} /> <MoneroBitcoinExchangeRate rate={state.price} />
</li> </li>
<li> <li>
The network fee of{' '} The network fee of{" "}
<BitcoinAmount amount={state.minBitcoinLockTxFee} /> will <BitcoinAmount amount={state.minBitcoinLockTxFee} /> will
automatically be deducted from the deposited coins automatically be deducted from the deposited coins
</li> </li>
@ -74,9 +74,7 @@ export default function WaitingForBtcDepositPage({
</li> </li>
</ul> </ul>
</Typography> </Typography>
<DepositAmountHelper <DepositAmountHelper state={state} />
state={state}
/>
</Box> </Box>
} }
icon={<BitcoinIcon />} icon={<BitcoinIcon />}

View file

@ -9,6 +9,7 @@ import { useState } from "react";
import { withdrawBtc } from "renderer/rpc"; import { withdrawBtc } from "renderer/rpc";
import BtcTxInMempoolPageContent from "./pages/BitcoinWithdrawTxInMempoolPage"; import BtcTxInMempoolPageContent from "./pages/BitcoinWithdrawTxInMempoolPage";
import AddressInputPage from "./pages/AddressInputPage"; import AddressInputPage from "./pages/AddressInputPage";
import WithdrawDialogContent from "./WithdrawDialogContent";
export default function WithdrawDialog({ export default function WithdrawDialog({
open, open,
@ -30,12 +31,10 @@ export default function WithdrawDialog({
} }
} }
// This prevents an issue where the Dialog is shown for a split second without a present withdraw state
if (!open) return null;
return ( return (
<Dialog open onClose={onCancel} maxWidth="sm" fullWidth> <Dialog open={open} onClose={onCancel} maxWidth="sm" fullWidth>
<DialogHeader title="Withdraw Bitcoin" /> <DialogHeader title="Withdraw Bitcoin" />
<WithdrawDialogContent isPending={pending} withdrawTxId={withdrawTxId}>
{withdrawTxId === null ? ( {withdrawTxId === null ? (
<AddressInputPage <AddressInputPage
setWithdrawAddress={setWithdrawAddress} setWithdrawAddress={setWithdrawAddress}
@ -48,9 +47,14 @@ export default function WithdrawDialog({
onCancel={onCancel} onCancel={onCancel}
/> />
)} )}
</WithdrawDialogContent>
<DialogActions> <DialogActions>
{withdrawTxId === null ? ( <Button onClick={onCancel} color="primary" disabled={pending}>
{withdrawTxId === null ? "Cancel" : "Done"}
</Button>
{withdrawTxId === null && (
<PromiseInvokeButton <PromiseInvokeButton
displayErrorSnackbar
variant="contained" variant="contained"
color="primary" color="primary"
disabled={!withdrawAddressValid} disabled={!withdrawAddressValid}
@ -65,10 +69,6 @@ export default function WithdrawDialog({
> >
Withdraw Withdraw
</PromiseInvokeButton> </PromiseInvokeButton>
) : (
<Button onClick={onCancel} color="primary" disabled={pending}>
Close
</Button>
)} )}
</DialogActions> </DialogActions>
</Dialog> </Dialog>

View file

@ -1,27 +1,31 @@
import { ReactNode } from 'react'; import { ReactNode } from "react";
import { Box, DialogContent, makeStyles } from '@material-ui/core'; import { Box, DialogContent, makeStyles } from "@material-ui/core";
import WithdrawStepper from './WithdrawStepper'; import WithdrawStepper from "./WithdrawStepper";
const useStyles = makeStyles({ const useStyles = makeStyles({
outer: { outer: {
minHeight: '15rem', minHeight: "15rem",
display: 'flex', display: "flex",
flexDirection: 'column', flexDirection: "column",
justifyContent: 'space-between', justifyContent: "space-between",
}, },
}); });
export default function WithdrawDialogContent({ export default function WithdrawDialogContent({
children, children,
isPending,
withdrawTxId,
}: { }: {
children: ReactNode; children: ReactNode;
isPending: boolean;
withdrawTxId: string | null;
}) { }) {
const classes = useStyles(); const classes = useStyles();
return ( return (
<DialogContent dividers className={classes.outer}> <DialogContent dividers className={classes.outer}>
<Box>{children}</Box> <Box>{children}</Box>
<WithdrawStepper /> <WithdrawStepper isPending={isPending} withdrawTxId={withdrawTxId} />
</DialogContent> </DialogContent>
); );
} }

View file

@ -1,27 +0,0 @@
import { useAppSelector, useIsRpcEndpointBusy } from 'store/hooks';
import { RpcMethod } from 'models/rpcModel';
import AddressInputPage from './pages/AddressInputPage';
import InitiatedPage from './pages/InitiatedPage';
import BtcTxInMempoolPageContent from './pages/BitcoinWithdrawTxInMempoolPage';
export default function WithdrawStatePage({
onCancel,
}: {
onCancel: () => void;
}) {
const isRpcEndpointBusy = useIsRpcEndpointBusy(RpcMethod.WITHDRAW_BTC);
const withdrawTxId = useAppSelector((state) => state.rpc.state.withdrawTxId);
if (withdrawTxId !== null) {
return (
<BtcTxInMempoolPageContent
withdrawTxId={withdrawTxId}
onCancel={onCancel}
/>
);
}
if (isRpcEndpointBusy) {
return <InitiatedPage onCancel={onCancel} />;
}
return <AddressInputPage onCancel={onCancel} />;
}

View file

@ -1,12 +1,8 @@
import { Step, StepLabel, Stepper } from '@material-ui/core'; import { Step, StepLabel, Stepper } from "@material-ui/core";
import { useAppSelector, useIsRpcEndpointBusy } from 'store/hooks'; import { useAppSelector, useIsRpcEndpointBusy } from "store/hooks";
import { RpcMethod } from 'models/rpcModel';
function getActiveStep( function getActiveStep(isPending: boolean, withdrawTxId: string | null) {
isWithdrawInProgress: boolean, if (isPending) {
withdrawTxId: string | null,
) {
if (isWithdrawInProgress) {
return 1; return 1;
} }
if (withdrawTxId !== null) { if (withdrawTxId !== null) {
@ -15,12 +11,15 @@ function getActiveStep(
return 0; return 0;
} }
export default function WithdrawStepper() { export default function WithdrawStepper({
const isWithdrawInProgress = useIsRpcEndpointBusy(RpcMethod.WITHDRAW_BTC); isPending,
const withdrawTxId = useAppSelector((s) => s.rpc.state.withdrawTxId); withdrawTxId,
}: {
isPending: boolean;
withdrawTxId: string | null;
}) {
return ( return (
<Stepper activeStep={getActiveStep(isWithdrawInProgress, withdrawTxId)}> <Stepper activeStep={getActiveStep(isPending, withdrawTxId)}>
<Step key={0}> <Step key={0}>
<StepLabel>Enter withdraw address</StepLabel> <StepLabel>Enter withdraw address</StepLabel>
</Step> </Step>

View file

@ -15,10 +15,9 @@ export default function AddressInputPage({
}) { }) {
return ( return (
<> <>
<WithdrawDialogContent>
<DialogContentText> <DialogContentText>
To withdraw the BTC of the internal wallet, please enter an address. To withdraw the Bitcoin inside the internal wallet, please enter an
All funds will be sent to that address. address. All funds will be sent to that address.
</DialogContentText> </DialogContentText>
<BitcoinAddressTextField <BitcoinAddressTextField
@ -28,7 +27,6 @@ export default function AddressInputPage({
helperText="All Bitcoin of the internal wallet will be transferred to this address" helperText="All Bitcoin of the internal wallet will be transferred to this address"
fullWidth fullWidth
/> />
</WithdrawDialogContent>
</> </>
); );
} }

View file

@ -11,7 +11,6 @@ export default function BtcTxInMempoolPageContent({
}) { }) {
return ( return (
<> <>
<WithdrawDialogContent>
<DialogContentText> <DialogContentText>
All funds of the internal Bitcoin wallet have been transferred to your All funds of the internal Bitcoin wallet have been transferred to your
withdraw address. withdraw address.
@ -22,7 +21,6 @@ export default function BtcTxInMempoolPageContent({
title="Bitcoin Withdraw Transaction" title="Bitcoin Withdraw Transaction"
additionalContent={null} additionalContent={null}
/> />
</WithdrawDialogContent>
</> </>
); );
} }

View file

@ -1,6 +1,6 @@
import { Drawer, makeStyles, Box } from '@material-ui/core'; import { Drawer, makeStyles, Box } from "@material-ui/core";
import NavigationHeader from './NavigationHeader'; import NavigationHeader from "./NavigationHeader";
import NavigationFooter from './NavigationFooter'; import NavigationFooter from "./NavigationFooter";
export const drawerWidth = 240; export const drawerWidth = 240;
@ -13,11 +13,11 @@ const useStyles = makeStyles({
width: drawerWidth, width: drawerWidth,
}, },
drawerContainer: { drawerContainer: {
overflow: 'auto', overflow: "auto",
display: 'flex', display: "flex",
flexDirection: 'column', flexDirection: "column",
justifyContent: 'space-between', justifyContent: "space-between",
height: '100%', height: "100%",
}, },
}); });

View file

@ -1,24 +1,24 @@
import RedditIcon from '@material-ui/icons/Reddit'; import RedditIcon from "@material-ui/icons/Reddit";
import GitHubIcon from '@material-ui/icons/GitHub'; import GitHubIcon from "@material-ui/icons/GitHub";
import { Box, makeStyles } from '@material-ui/core'; import { Box, makeStyles } from "@material-ui/core";
import LinkIconButton from '../icons/LinkIconButton'; import LinkIconButton from "../icons/LinkIconButton";
import UnfinishedSwapsAlert from '../alert/UnfinishedSwapsAlert'; import UnfinishedSwapsAlert from "../alert/UnfinishedSwapsAlert";
import FundsLeftInWalletAlert from '../alert/FundsLeftInWalletAlert'; import FundsLeftInWalletAlert from "../alert/FundsLeftInWalletAlert";
import RpcStatusAlert from '../alert/RpcStatusAlert'; import RpcStatusAlert from "../alert/RpcStatusAlert";
import DiscordIcon from '../icons/DiscordIcon'; import DiscordIcon from "../icons/DiscordIcon";
import { DISCORD_URL } from '../pages/help/ContactInfoBox'; import { DISCORD_URL } from "../pages/help/ContactInfoBox";
import MoneroWalletRpcUpdatingAlert from '../alert/MoneroWalletRpcUpdatingAlert'; import MoneroWalletRpcUpdatingAlert from "../alert/MoneroWalletRpcUpdatingAlert";
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles((theme) => ({
outer: { outer: {
display: 'flex', display: "flex",
flexDirection: 'column', flexDirection: "column",
padding: theme.spacing(1), padding: theme.spacing(1),
gap: theme.spacing(1), gap: theme.spacing(1),
}, },
linksOuter: { linksOuter: {
display: 'flex', display: "flex",
justifyContent: 'space-evenly', justifyContent: "space-evenly",
}, },
})); }));

View file

@ -1,10 +1,10 @@
import { Box, List } from '@material-ui/core'; import { Box, List } from "@material-ui/core";
import SwapHorizOutlinedIcon from '@material-ui/icons/SwapHorizOutlined'; import SwapHorizOutlinedIcon from "@material-ui/icons/SwapHorizOutlined";
import HistoryOutlinedIcon from '@material-ui/icons/HistoryOutlined'; import HistoryOutlinedIcon from "@material-ui/icons/HistoryOutlined";
import AccountBalanceWalletIcon from '@material-ui/icons/AccountBalanceWallet'; import AccountBalanceWalletIcon from "@material-ui/icons/AccountBalanceWallet";
import HelpOutlineIcon from '@material-ui/icons/HelpOutline'; import HelpOutlineIcon from "@material-ui/icons/HelpOutline";
import RouteListItemIconButton from './RouteListItemIconButton'; import RouteListItemIconButton from "./RouteListItemIconButton";
import UnfinishedSwapsBadge from './UnfinishedSwapsCountBadge'; import UnfinishedSwapsBadge from "./UnfinishedSwapsCountBadge";
export default function NavigationHeader() { export default function NavigationHeader() {
return ( return (

View file

@ -1,6 +1,6 @@
import { ReactNode } from 'react'; import { ReactNode } from "react";
import { useNavigate } from 'react-router-dom'; import { useNavigate } from "react-router-dom";
import { ListItem, ListItemIcon, ListItemText } from '@material-ui/core'; import { ListItem, ListItemIcon, ListItemText } from "@material-ui/core";
export default function RouteListItemIconButton({ export default function RouteListItemIconButton({
name, name,

View file

@ -1,5 +1,5 @@
import { Badge } from '@material-ui/core'; import { Badge } from "@material-ui/core";
import { useResumeableSwapsCount } from 'store/hooks'; import { useResumeableSwapsCount } from "store/hooks";
export default function UnfinishedSwapsBadge({ export default function UnfinishedSwapsBadge({
children, children,

View file

@ -1,7 +1,7 @@
import { useState } from 'react'; import { useState } from "react";
import { Box, IconButton, TextField } from '@material-ui/core'; import { Box, IconButton, TextField } from "@material-ui/core";
import SearchIcon from '@material-ui/icons/Search'; import SearchIcon from "@material-ui/icons/Search";
import CloseIcon from '@material-ui/icons/Close'; import CloseIcon from "@material-ui/icons/Close";
export function ExpandableSearchBox({ export function ExpandableSearchBox({
query, query,
@ -13,8 +13,8 @@ export function ExpandableSearchBox({
const [expanded, setExpanded] = useState(false); const [expanded, setExpanded] = useState(false);
return ( return (
<Box style={{ display: 'flex', justifyContent: 'center' }}> <Box style={{ display: "flex", justifyContent: "center" }}>
<Box style={{ display: 'flex', alignItems: 'center', gap: '0.5rem' }}> <Box style={{ display: "flex", alignItems: "center", gap: "0.5rem" }}>
{expanded ? ( {expanded ? (
<> <>
<TextField <TextField
@ -26,7 +26,7 @@ export function ExpandableSearchBox({
<IconButton <IconButton
onClick={() => { onClick={() => {
setExpanded(false); setExpanded(false);
setQuery(''); setQuery("");
}} }}
size="small" size="small"
> >

View file

@ -1,4 +1,4 @@
import humanizeDuration from 'humanize-duration'; import humanizeDuration from "humanize-duration";
const AVG_BLOCK_TIME_MS = 10 * 60 * 1000; const AVG_BLOCK_TIME_MS = 10 * 60 * 1000;
@ -10,7 +10,7 @@ export default function HumanizedBitcoinBlockDuration({
return ( return (
<> <>
{`${humanizeDuration(blocks * AVG_BLOCK_TIME_MS, { {`${humanizeDuration(blocks * AVG_BLOCK_TIME_MS, {
conjunction: ' and ', conjunction: " and ",
})} (${blocks} blocks)`} })} (${blocks} blocks)`}
</> </>
); );

View file

@ -1,8 +1,8 @@
import TreeView from '@material-ui/lab/TreeView'; import TreeView from "@material-ui/lab/TreeView";
import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
import ChevronRightIcon from '@material-ui/icons/ChevronRight'; import ChevronRightIcon from "@material-ui/icons/ChevronRight";
import TreeItem from '@material-ui/lab/TreeItem'; import TreeItem from "@material-ui/lab/TreeItem";
import ScrollablePaperTextBox from './ScrollablePaperTextBox'; import ScrollablePaperTextBox from "./ScrollablePaperTextBox";
interface JsonTreeViewProps { interface JsonTreeViewProps {
data: any; data: any;
@ -13,7 +13,7 @@ export default function JsonTreeView({ data, label }: JsonTreeViewProps) {
const renderTree = (nodes: any, parentId: string) => { const renderTree = (nodes: any, parentId: string) => {
return Object.keys(nodes).map((key, _) => { return Object.keys(nodes).map((key, _) => {
const nodeId = `${parentId}.${key}`; const nodeId = `${parentId}.${key}`;
if (typeof nodes[key] === 'object' && nodes[key] !== null) { if (typeof nodes[key] === "object" && nodes[key] !== null) {
return ( return (
<TreeItem nodeId={nodeId} label={key} key={nodeId}> <TreeItem nodeId={nodeId} label={key} key={nodeId}>
{renderTree(nodes[key], nodeId)} {renderTree(nodes[key], nodeId)}
@ -38,10 +38,10 @@ export default function JsonTreeView({ data, label }: JsonTreeViewProps) {
<TreeView <TreeView
defaultCollapseIcon={<ExpandMoreIcon />} defaultCollapseIcon={<ExpandMoreIcon />}
defaultExpandIcon={<ChevronRightIcon />} defaultExpandIcon={<ChevronRightIcon />}
defaultExpanded={['root']} defaultExpanded={["root"]}
> >
<TreeItem nodeId="root" label={label}> <TreeItem nodeId="root" label={label}>
{renderTree(data ?? {}, 'root')} {renderTree(data ?? {}, "root")}
</TreeItem> </TreeItem>
</TreeView>, </TreeView>,
]} ]}

View file

@ -1,6 +1,6 @@
import React from 'react'; import React from "react";
import Button, { ButtonProps } from '@material-ui/core/Button'; import Button, { ButtonProps } from "@material-ui/core/Button";
import CircularProgress from '@material-ui/core/CircularProgress'; import CircularProgress from "@material-ui/core/CircularProgress";
interface LoadingButtonProps extends ButtonProps { interface LoadingButtonProps extends ButtonProps {
loading: boolean; loading: boolean;

View file

@ -1,47 +1,47 @@
import { Box, Chip, Typography } from '@material-ui/core'; import { Box, Chip, Typography } from "@material-ui/core";
import { useMemo, useState } from 'react'; import { useMemo, useState } from "react";
import { CliLog } from 'models/cliModel'; import { CliLog } from "models/cliModel";
import { logsToRawString } from 'utils/parseUtils'; import { logsToRawString } from "utils/parseUtils";
import ScrollablePaperTextBox from './ScrollablePaperTextBox'; import ScrollablePaperTextBox from "./ScrollablePaperTextBox";
function RenderedCliLog({ log }: { log: CliLog }) { function RenderedCliLog({ log }: { log: CliLog }) {
const { timestamp, level, fields } = log; const { timestamp, level, fields } = log;
const levelColorMap = { const levelColorMap = {
DEBUG: '#1976d2', // Blue DEBUG: "#1976d2", // Blue
INFO: '#388e3c', // Green INFO: "#388e3c", // Green
WARN: '#fbc02d', // Yellow WARN: "#fbc02d", // Yellow
ERROR: '#d32f2f', // Red ERROR: "#d32f2f", // Red
TRACE: '#8e24aa', // Purple TRACE: "#8e24aa", // Purple
}; };
return ( return (
<Box> <Box>
<Box <Box
style={{ style={{
display: 'flex', display: "flex",
gap: '0.3rem', gap: "0.3rem",
alignItems: 'center', alignItems: "center",
}} }}
> >
<Chip <Chip
label={level} label={level}
size="small" size="small"
style={{ backgroundColor: levelColorMap[level], color: 'white' }} style={{ backgroundColor: levelColorMap[level], color: "white" }}
/> />
<Chip label={timestamp} size="small" variant="outlined" /> <Chip label={timestamp} size="small" variant="outlined" />
<Typography variant="subtitle2">{fields.message}</Typography> <Typography variant="subtitle2">{fields.message}</Typography>
</Box> </Box>
<Box <Box
sx={{ sx={{
paddingLeft: '1rem', paddingLeft: "1rem",
paddingTop: '0.2rem', paddingTop: "0.2rem",
display: 'flex', display: "flex",
flexDirection: 'column', flexDirection: "column",
}} }}
> >
{Object.entries(fields).map(([key, value]) => { {Object.entries(fields).map(([key, value]) => {
if (key !== 'message') { if (key !== "message") {
return ( return (
<Typography variant="caption" key={key}> <Typography variant="caption" key={key}>
{key}: {JSON.stringify(value)} {key}: {JSON.stringify(value)}
@ -62,7 +62,7 @@ export default function CliLogsBox({
label: string; label: string;
logs: (CliLog | string)[]; logs: (CliLog | string)[];
}) { }) {
const [searchQuery, setSearchQuery] = useState<string>(''); const [searchQuery, setSearchQuery] = useState<string>("");
const memoizedLogs = useMemo(() => { const memoizedLogs = useMemo(() => {
if (searchQuery.length === 0) { if (searchQuery.length === 0) {
@ -80,7 +80,7 @@ export default function CliLogsBox({
searchQuery={searchQuery} searchQuery={searchQuery}
setSearchQuery={setSearchQuery} setSearchQuery={setSearchQuery}
rows={memoizedLogs.map((log) => rows={memoizedLogs.map((log) =>
typeof log === 'string' ? ( typeof log === "string" ? (
<Typography component="pre">{log}</Typography> <Typography component="pre">{log}</Typography>
) : ( ) : (
<RenderedCliLog log={log} key={JSON.stringify(log)} /> <RenderedCliLog log={log} key={JSON.stringify(log)} />

View file

@ -1,12 +1,12 @@
import { Box, Divider, IconButton, Paper, Typography } from '@material-ui/core'; import { Box, Divider, IconButton, Paper, Typography } from "@material-ui/core";
import { ReactNode, useRef } from 'react'; import { ReactNode, useRef } from "react";
import KeyboardArrowDownIcon from '@material-ui/icons/KeyboardArrowDown'; import KeyboardArrowDownIcon from "@material-ui/icons/KeyboardArrowDown";
import KeyboardArrowUpIcon from '@material-ui/icons/KeyboardArrowUp'; import KeyboardArrowUpIcon from "@material-ui/icons/KeyboardArrowUp";
import { VList, VListHandle } from 'virtua'; import { VList, VListHandle } from "virtua";
import FileCopyOutlinedIcon from '@material-ui/icons/FileCopyOutlined'; import FileCopyOutlinedIcon from "@material-ui/icons/FileCopyOutlined";
import { ExpandableSearchBox } from './ExpandableSearchBox'; import { ExpandableSearchBox } from "./ExpandableSearchBox";
const MIN_HEIGHT = '10rem'; const MIN_HEIGHT = "10rem";
export default function ScrollablePaperTextBox({ export default function ScrollablePaperTextBox({
rows, rows,
@ -41,31 +41,31 @@ export default function ScrollablePaperTextBox({
<Paper <Paper
variant="outlined" variant="outlined"
style={{ style={{
display: 'flex', display: "flex",
flexDirection: 'column', flexDirection: "column",
gap: '0.5rem', gap: "0.5rem",
padding: '0.5rem', padding: "0.5rem",
width: '100%', width: "100%",
}} }}
> >
<Typography>{title}</Typography> <Typography>{title}</Typography>
<Divider /> <Divider />
<Box <Box
style={{ style={{
overflow: 'auto', overflow: "auto",
whiteSpace: 'nowrap', whiteSpace: "nowrap",
maxHeight: minHeight, maxHeight: minHeight,
minHeight, minHeight,
display: 'flex', display: "flex",
flexDirection: 'column', flexDirection: "column",
gap: '0.5rem', gap: "0.5rem",
}} }}
> >
<VList ref={virtuaEl} style={{ height: MIN_HEIGHT, width: '100%' }}> <VList ref={virtuaEl} style={{ height: MIN_HEIGHT, width: "100%" }}>
{rows} {rows}
</VList> </VList>
</Box> </Box>
<Box style={{ display: 'flex', gap: '0.5rem' }}> <Box style={{ display: "flex", gap: "0.5rem" }}>
<IconButton onClick={onCopy} size="small"> <IconButton onClick={onCopy} size="small">
<FileCopyOutlinedIcon /> <FileCopyOutlinedIcon />
</IconButton> </IconButton>

View file

@ -1,17 +1,17 @@
import { Box, Button, makeStyles, Typography } from '@material-ui/core'; import { Box, Button, makeStyles, Typography } from "@material-ui/core";
import InfoBox from '../../modal/swap/InfoBox'; import InfoBox from "../../modal/swap/InfoBox";
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles((theme) => ({
spacedBox: { spacedBox: {
display: 'flex', display: "flex",
gap: theme.spacing(1), gap: theme.spacing(1),
}, },
})); }));
const GITHUB_ISSUE_URL = const GITHUB_ISSUE_URL =
'https://github.com/UnstoppableSwap/unstoppableswap-gui/issues/new/choose'; "https://github.com/UnstoppableSwap/unstoppableswap-gui/issues/new/choose";
const MATRIX_ROOM_URL = 'https://matrix.to/#/#unstoppableswap:matrix.org'; const MATRIX_ROOM_URL = "https://matrix.to/#/#unstoppableswap:matrix.org";
export const DISCORD_URL = 'https://discord.gg/APJ6rJmq'; export const DISCORD_URL = "https://discord.gg/APJ6rJmq";
export default function ContactInfoBox() { export default function ContactInfoBox() {
const classes = useStyles(); const classes = useStyles();

View file

@ -1,9 +1,9 @@
import { Typography } from '@material-ui/core'; import { Typography } from "@material-ui/core";
import DepositAddressInfoBox from '../../modal/swap/DepositAddressInfoBox'; import DepositAddressInfoBox from "../../modal/swap/DepositAddressInfoBox";
import MoneroIcon from '../../icons/MoneroIcon'; import MoneroIcon from "../../icons/MoneroIcon";
const XMR_DONATE_ADDRESS = const XMR_DONATE_ADDRESS =
'87jS4C7ngk9EHdqFFuxGFgg8AyH63dRUoULshWDybFJaP75UA89qsutG5B1L1QTc4w228nsqsv8EjhL7bz8fB3611Mh98mg'; "87jS4C7ngk9EHdqFFuxGFgg8AyH63dRUoULshWDybFJaP75UA89qsutG5B1L1QTc4w228nsqsv8EjhL7bz8fB3611Mh98mg";
export default function DonateInfoBox() { export default function DonateInfoBox() {
return ( return (

View file

@ -1,7 +1,7 @@
import { Button, Typography } from '@material-ui/core'; import { Button, Typography } from "@material-ui/core";
import { useState } from 'react'; import { useState } from "react";
import InfoBox from '../../modal/swap/InfoBox'; import InfoBox from "../../modal/swap/InfoBox";
import FeedbackDialog from '../../modal/feedback/FeedbackDialog'; import FeedbackDialog from "../../modal/feedback/FeedbackDialog";
export default function FeedbackInfoBox() { export default function FeedbackInfoBox() {
const [showDialog, setShowDialog] = useState(false); const [showDialog, setShowDialog] = useState(false);

View file

@ -1,15 +1,15 @@
import { Box, makeStyles } from '@material-ui/core'; import { Box, makeStyles } from "@material-ui/core";
import ContactInfoBox from './ContactInfoBox'; import ContactInfoBox from "./ContactInfoBox";
import FeedbackInfoBox from './FeedbackInfoBox'; import FeedbackInfoBox from "./FeedbackInfoBox";
import DonateInfoBox from './DonateInfoBox'; import DonateInfoBox from "./DonateInfoBox";
import TorInfoBox from './TorInfoBox'; import TorInfoBox from "./TorInfoBox";
import RpcControlBox from './RpcControlBox'; import RpcControlBox from "./RpcControlBox";
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles((theme) => ({
outer: { outer: {
display: 'flex', display: "flex",
gap: theme.spacing(2), gap: theme.spacing(2),
flexDirection: 'column', flexDirection: "column",
}, },
})); }));

View file

@ -1,18 +1,18 @@
import { Box, makeStyles } from '@material-ui/core'; import { Box, makeStyles } from "@material-ui/core";
import IpcInvokeButton from 'renderer/components/IpcInvokeButton'; import IpcInvokeButton from "renderer/components/IpcInvokeButton";
import { useAppSelector } from 'store/hooks'; import { useAppSelector } from "store/hooks";
import StopIcon from '@material-ui/icons/Stop'; import StopIcon from "@material-ui/icons/Stop";
import PlayArrowIcon from '@material-ui/icons/PlayArrow'; import PlayArrowIcon from "@material-ui/icons/PlayArrow";
import { RpcProcessStateType } from 'models/rpcModel'; import { RpcProcessStateType } from "models/rpcModel";
import InfoBox from '../../modal/swap/InfoBox'; import InfoBox from "../../modal/swap/InfoBox";
import CliLogsBox from '../../other/RenderedCliLog'; import CliLogsBox from "../../other/RenderedCliLog";
import FolderOpenIcon from '@material-ui/icons/FolderOpen'; import FolderOpenIcon from "@material-ui/icons/FolderOpen";
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles((theme) => ({
actionsOuter: { actionsOuter: {
display: 'flex', display: "flex",
gap: theme.spacing(1), gap: theme.spacing(1),
alignItems: 'center', alignItems: "center",
}, },
})); }));

View file

@ -1,14 +1,14 @@
import { Box, makeStyles, Typography } from '@material-ui/core'; import { Box, makeStyles, Typography } from "@material-ui/core";
import IpcInvokeButton from 'renderer/components/IpcInvokeButton'; import IpcInvokeButton from "renderer/components/IpcInvokeButton";
import { useAppSelector } from 'store/hooks'; import { useAppSelector } from "store/hooks";
import StopIcon from '@material-ui/icons/Stop'; import StopIcon from "@material-ui/icons/Stop";
import PlayArrowIcon from '@material-ui/icons/PlayArrow'; import PlayArrowIcon from "@material-ui/icons/PlayArrow";
import InfoBox from '../../modal/swap/InfoBox'; import InfoBox from "../../modal/swap/InfoBox";
import CliLogsBox from '../../other/RenderedCliLog'; import CliLogsBox from "../../other/RenderedCliLog";
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles((theme) => ({
actionsOuter: { actionsOuter: {
display: 'flex', display: "flex",
gap: theme.spacing(1), gap: theme.spacing(1),
}, },
})); }));
@ -24,10 +24,10 @@ export default function TorInfoBox() {
mainContent={ mainContent={
<Box <Box
style={{ style={{
width: '100%', width: "100%",
display: 'flex', display: "flex",
flexDirection: 'column', flexDirection: "column",
gap: '8px', gap: "8px",
}} }}
> >
<Typography variant="subtitle2"> <Typography variant="subtitle2">
@ -37,7 +37,7 @@ export default function TorInfoBox() {
below. If Tor is running, all traffic will be routed through it and below. If Tor is running, all traffic will be routed through it and
the swap provider will not be able to see your IP address. the swap provider will not be able to see your IP address.
</Typography> </Typography>
<CliLogsBox label="Tor Daemon Logs" logs={torStdOut.split('\n')} /> <CliLogsBox label="Tor Daemon Logs" logs={torStdOut.split("\n")} />
</Box> </Box>
} }
additionalContent={ additionalContent={

View file

@ -1,8 +1,8 @@
import { Typography } from '@material-ui/core'; import { Typography } from "@material-ui/core";
import { useIsSwapRunning } from 'store/hooks'; import { useIsSwapRunning } from "store/hooks";
import HistoryTable from './table/HistoryTable'; import HistoryTable from "./table/HistoryTable";
import SwapDialog from '../../modal/swap/SwapDialog'; import SwapDialog from "../../modal/swap/SwapDialog";
import SwapTxLockAlertsBox from '../../alert/SwapTxLockAlertsBox'; import SwapTxLockAlertsBox from "../../alert/SwapTxLockAlertsBox";
export default function HistoryPage() { export default function HistoryPage() {
const showDialog = useIsSwapRunning(); const showDialog = useIsSwapRunning();

View file

@ -60,27 +60,15 @@ export default function HistoryRow({ swap }: HistoryRowProps) {
<> <>
<TableRow> <TableRow>
<TableCell> <TableCell>
<IconButton <IconButton size="small" onClick={() => setExpanded(!expanded)}>
size="small" {expanded ? <KeyboardArrowUpIcon /> : <KeyboardArrowDownIcon />}
onClick={() => setExpanded(!expanded)}
>
{expanded ? (
<KeyboardArrowUpIcon />
) : (
<KeyboardArrowDownIcon />
)}
</IconButton> </IconButton>
</TableCell> </TableCell>
<TableCell>{swap.swap_id.substring(0, 5)}...</TableCell> <TableCell>{swap.swap_id.substring(0, 5)}...</TableCell>
<TableCell> <TableCell>
<AmountTransfer <AmountTransfer xmrAmount={xmrAmount} btcAmount={btcAmount} />
xmrAmount={xmrAmount}
btcAmount={btcAmount}
/>
</TableCell>
<TableCell>
{getHumanReadableDbStateType(swap.state_name)}
</TableCell> </TableCell>
<TableCell>{getHumanReadableDbStateType(swap.state_name)}</TableCell>
<TableCell> <TableCell>
<HistoryRowActions swap={swap} /> <HistoryRowActions swap={swap} />
</TableCell> </TableCell>

View file

@ -87,9 +87,7 @@ export default function HistoryRowExpanded({
<TableRow> <TableRow>
<TableCell>Exchange Rate</TableCell> <TableCell>Exchange Rate</TableCell>
<TableCell> <TableCell>
<MoneroBitcoinExchangeRate <MoneroBitcoinExchangeRate rate={exchangeRate} />
rate={exchangeRate}
/>
</TableCell> </TableCell>
</TableRow> </TableRow>
<TableRow> <TableRow>
@ -108,10 +106,7 @@ export default function HistoryRowExpanded({
<TableCell>Bitcoin lock transaction</TableCell> <TableCell>Bitcoin lock transaction</TableCell>
<TableCell> <TableCell>
<Link <Link
href={getBitcoinTxExplorerUrl( href={getBitcoinTxExplorerUrl(swap.tx_lock_id, isTestnet())}
swap.tx_lock_id,
isTestnet(),
)}
target="_blank" target="_blank"
> >
{swap.tx_lock_id} {swap.tx_lock_id}
@ -127,11 +122,7 @@ export default function HistoryRowExpanded({
variant="outlined" variant="outlined"
size="small" size="small"
/> />
<SwapCancelRefundButton <SwapCancelRefundButton swap={swap} variant="contained" size="small" />
swap={swap}
variant="contained"
size="small"
/>
<SwapMoneroRecoveryButton <SwapMoneroRecoveryButton
swap={swap} swap={swap}
variant="contained" variant="contained"

View file

@ -1,15 +1,15 @@
import { ButtonProps } from '@material-ui/core/Button/Button'; import { ButtonProps } from "@material-ui/core/Button/Button";
import { import {
Button, Button,
Dialog, Dialog,
DialogActions, DialogActions,
DialogContent, DialogContent,
DialogTitle, DialogTitle,
} from '@material-ui/core'; } from "@material-ui/core";
import { useState } from 'react'; import { useState } from "react";
import { CliLog } from 'models/cliModel'; import { CliLog } from "models/cliModel";
import IpcInvokeButton from '../../../IpcInvokeButton'; import IpcInvokeButton from "../../../IpcInvokeButton";
import CliLogsBox from '../../../other/RenderedCliLog'; import CliLogsBox from "../../../other/RenderedCliLog";
export default function SwapLogFileOpenButton({ export default function SwapLogFileOpenButton({
swapId, swapId,

View file

@ -37,17 +37,16 @@ function MoneroRecoveryKeysDialog({ swap }: { swap: GetSwapInfoResponse }) {
/> />
<DialogContent> <DialogContent>
<DialogContentText> <DialogContentText>
You can use the keys below to manually redeem the Monero You can use the keys below to manually redeem the Monero funds from
funds from the multi-signature wallet. the multi-signature wallet.
<ul> <ul>
<li> <li>
This is useful if the swap daemon fails to redeem This is useful if the swap daemon fails to redeem the funds itself
the funds itself
</li> </li>
<li> <li>
If you have come this far, there is no risk of If you have come this far, there is no risk of losing funds. You
losing funds. You are the only one with access to are the only one with access to these keys and can use them to
these keys and can use them to access your funds access your funds
</li> </li>
<li> <li>
View{" "} View{" "}
@ -58,8 +57,8 @@ function MoneroRecoveryKeysDialog({ swap }: { swap: GetSwapInfoResponse }) {
> >
this guide this guide
</Link>{" "} </Link>{" "}
for a detailed description on how to import the keys for a detailed description on how to import the keys and spend the
and spend the funds. funds.
</li> </li>
</ul> </ul>
</DialogContentText> </DialogContentText>

View file

@ -1,7 +1,7 @@
import { Box } from '@material-ui/core'; import { Box } from "@material-ui/core";
import { Alert, AlertTitle } from '@material-ui/lab'; import { Alert, AlertTitle } from "@material-ui/lab";
import { removeAlert } from 'store/features/alertsSlice'; import { removeAlert } from "store/features/alertsSlice";
import { useAppDispatch, useAppSelector } from 'store/hooks'; import { useAppDispatch, useAppSelector } from "store/hooks";
export default function ApiAlertsBox() { export default function ApiAlertsBox() {
const alerts = useAppSelector((state) => state.alerts.alerts); const alerts = useAppSelector((state) => state.alerts.alerts);
@ -14,7 +14,7 @@ export default function ApiAlertsBox() {
if (alerts.length === 0) return null; if (alerts.length === 0) return null;
return ( return (
<Box style={{ display: 'flex', justifyContent: 'center', gap: '1rem' }}> <Box style={{ display: "flex", justifyContent: "center", gap: "1rem" }}>
{alerts.map((alert) => ( {alerts.map((alert) => (
<Alert <Alert
variant="filled" variant="filled"

View file

@ -1,13 +1,13 @@
import { Box, makeStyles } from '@material-ui/core'; import { Box, makeStyles } from "@material-ui/core";
import SwapWidget from './SwapWidget'; import SwapWidget from "./SwapWidget";
import ApiAlertsBox from './ApiAlertsBox'; import ApiAlertsBox from "./ApiAlertsBox";
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles((theme) => ({
outer: { outer: {
display: 'flex', display: "flex",
width: '100%', width: "100%",
flexDirection: 'column', flexDirection: "column",
alignItems: 'center', alignItems: "center",
paddingBottom: theme.spacing(1), paddingBottom: theme.spacing(1),
gap: theme.spacing(1), gap: theme.spacing(1),
}, },

View file

@ -1,4 +1,4 @@
import { ChangeEvent, useEffect, useState } from 'react'; import { ChangeEvent, useEffect, useState } from "react";
import { import {
makeStyles, makeStyles,
Box, Box,
@ -7,21 +7,21 @@ import {
TextField, TextField,
LinearProgress, LinearProgress,
Fab, Fab,
} from '@material-ui/core'; } from "@material-ui/core";
import InputAdornment from '@material-ui/core/InputAdornment'; import InputAdornment from "@material-ui/core/InputAdornment";
import ArrowDownwardIcon from '@material-ui/icons/ArrowDownward'; import ArrowDownwardIcon from "@material-ui/icons/ArrowDownward";
import SwapHorizIcon from '@material-ui/icons/SwapHoriz'; import SwapHorizIcon from "@material-ui/icons/SwapHoriz";
import { Alert } from '@material-ui/lab'; import { Alert } from "@material-ui/lab";
import { satsToBtc } from 'utils/conversionUtils'; import { satsToBtc } from "utils/conversionUtils";
import { useAppSelector } from 'store/hooks'; import { useAppSelector } from "store/hooks";
import { ExtendedProviderStatus } from 'models/apiModel'; import { ExtendedProviderStatus } from "models/apiModel";
import { isSwapState } from 'models/storeModel'; import { isSwapState } from "models/storeModel";
import SwapDialog from '../../modal/swap/SwapDialog'; import SwapDialog from "../../modal/swap/SwapDialog";
import ProviderSelect from '../../modal/provider/ProviderSelect'; import ProviderSelect from "../../modal/provider/ProviderSelect";
import { import {
ListSellersDialogOpenButton, ListSellersDialogOpenButton,
ProviderSubmitDialogOpenButton, ProviderSubmitDialogOpenButton,
} from '../../modal/provider/ProviderListDialog'; } from "../../modal/provider/ProviderListDialog";
// After RECONNECTION_ATTEMPTS_UNTIL_ASSUME_DOWN failed reconnection attempts we can assume the public registry is down // After RECONNECTION_ATTEMPTS_UNTIL_ASSUME_DOWN failed reconnection attempts we can assume the public registry is down
const RECONNECTION_ATTEMPTS_UNTIL_ASSUME_DOWN = 1; const RECONNECTION_ATTEMPTS_UNTIL_ASSUME_DOWN = 1;
@ -32,9 +32,9 @@ function isRegistryDown(reconnectionAttempts: number): boolean {
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles((theme) => ({
inner: { inner: {
width: 'min(480px, 100%)', width: "min(480px, 100%)",
minHeight: '150px', minHeight: "150px",
display: 'grid', display: "grid",
padding: theme.spacing(1), padding: theme.spacing(1),
gridGap: theme.spacing(1), gridGap: theme.spacing(1),
}, },
@ -48,19 +48,19 @@ const useStyles = makeStyles((theme) => ({
padding: theme.spacing(1), padding: theme.spacing(1),
}, },
swapIconOuter: { swapIconOuter: {
display: 'flex', display: "flex",
justifyContent: 'center', justifyContent: "center",
}, },
swapIcon: { swapIcon: {
marginRight: theme.spacing(1), marginRight: theme.spacing(1),
}, },
noProvidersAlertOuter: { noProvidersAlertOuter: {
display: 'flex', display: "flex",
flexDirection: 'column', flexDirection: "column",
gap: theme.spacing(1), gap: theme.spacing(1),
}, },
noProvidersAlertButtonsOuter: { noProvidersAlertButtonsOuter: {
display: 'flex', display: "flex",
gap: theme.spacing(1), gap: theme.spacing(1),
}, },
})); }));
@ -111,7 +111,7 @@ function HasProviderSwapWidget({
function getBtcFieldError(): string | null { function getBtcFieldError(): string | null {
const parsedBtcAmount = Number(btcFieldValue); const parsedBtcAmount = Number(btcFieldValue);
if (Number.isNaN(parsedBtcAmount)) { if (Number.isNaN(parsedBtcAmount)) {
return 'This is not a valid number'; return "This is not a valid number";
} }
if (parsedBtcAmount < satsToBtc(selectedProvider.minSwapAmount)) { if (parsedBtcAmount < satsToBtc(selectedProvider.minSwapAmount)) {
return `The minimum swap amount is ${satsToBtc( return `The minimum swap amount is ${satsToBtc(

View file

@ -1,11 +1,11 @@
import { Box, makeStyles, Typography } from '@material-ui/core'; import { Box, makeStyles, Typography } from "@material-ui/core";
import { Alert } from '@material-ui/lab'; import { Alert } from "@material-ui/lab";
import WithdrawWidget from './WithdrawWidget'; import WithdrawWidget from "./WithdrawWidget";
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles((theme) => ({
outer: { outer: {
display: 'flex', display: "flex",
flexDirection: 'column', flexDirection: "column",
gridGap: theme.spacing(0.5), gridGap: theme.spacing(0.5),
}, },
})); }));

View file

@ -1,18 +1,18 @@
import { Box, Button, makeStyles, Typography } from '@material-ui/core'; import { Box, Button, makeStyles, Typography } from "@material-ui/core";
import { useState } from 'react'; import { useState } from "react";
import SendIcon from '@material-ui/icons/Send'; import SendIcon from "@material-ui/icons/Send";
import { useAppSelector, useIsRpcEndpointBusy } from 'store/hooks'; import { useAppSelector, useIsRpcEndpointBusy } from "store/hooks";
import { RpcMethod } from 'models/rpcModel'; import { RpcMethod } from "models/rpcModel";
import BitcoinIcon from '../../icons/BitcoinIcon'; import BitcoinIcon from "../../icons/BitcoinIcon";
import WithdrawDialog from '../../modal/wallet/WithdrawDialog'; import WithdrawDialog from "../../modal/wallet/WithdrawDialog";
import WalletRefreshButton from './WalletRefreshButton'; import WalletRefreshButton from "./WalletRefreshButton";
import InfoBox from '../../modal/swap/InfoBox'; import InfoBox from "../../modal/swap/InfoBox";
import { SatsAmount } from 'renderer/components/other/Units'; import { SatsAmount } from "renderer/components/other/Units";
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles((theme) => ({
title: { title: {
alignItems: 'center', alignItems: "center",
display: 'flex', display: "flex",
gap: theme.spacing(0.5), gap: theme.spacing(0.5),
}, },
})); }));

View file

@ -3,14 +3,14 @@ import {
SnackbarKey, SnackbarKey,
SnackbarProvider, SnackbarProvider,
useSnackbar, useSnackbar,
} from 'notistack'; } from "notistack";
import { IconButton, styled } from '@material-ui/core'; import { IconButton, styled } from "@material-ui/core";
import { Close } from '@material-ui/icons'; import { Close } from "@material-ui/icons";
import { ReactNode } from 'react'; import { ReactNode } from "react";
const StyledMaterialDesignContent = styled(MaterialDesignContent)(() => ({ const StyledMaterialDesignContent = styled(MaterialDesignContent)(() => ({
'&.notistack-MuiContent': { "&.notistack-MuiContent": {
maxWidth: '50vw', maxWidth: "50vw",
}, },
})); }));

View file

@ -1,18 +1,18 @@
import { render } from 'react-dom'; import { render } from "react-dom";
import { Provider } from 'react-redux'; import { Provider } from "react-redux";
import { store } from './store/storeRenderer'; import { store } from "./store/storeRenderer";
import { setRegistryProviders } from 'store/features/providersSlice'; import { setRegistryProviders } from "store/features/providersSlice";
import { setAlerts } from 'store/features/alertsSlice'; import { setAlerts } from "store/features/alertsSlice";
import { setXmrPrice, setBtcPrice } from 'store/features/ratesSlice'; import { setXmrPrice, setBtcPrice } from "store/features/ratesSlice";
import { import {
fetchAlertsViaHttp, fetchAlertsViaHttp,
fetchBtcPrice, fetchBtcPrice,
fetchProvidersViaHttp, fetchProvidersViaHttp,
fetchXmrPrice, fetchXmrPrice,
} from './api'; } from "./api";
import logger from '../utils/logger'; import logger from "../utils/logger";
import App from './components/App'; import App from "./components/App";
import { checkBitcoinBalance, getRawSwapInfos } from './rpc'; import { checkBitcoinBalance, getRawSwapInfos } from "./rpc";
setTimeout(() => { setTimeout(() => {
checkBitcoinBalance(); checkBitcoinBalance();
@ -23,7 +23,7 @@ render(
<Provider store={store}> <Provider store={store}>
<App /> <App />
</Provider>, </Provider>,
document.getElementById('root'), document.getElementById("root"),
); );
async function fetchInitialData() { async function fetchInitialData() {
@ -33,30 +33,30 @@ async function fetchInitialData() {
logger.info( logger.info(
{ providerList }, { providerList },
'Fetched providers via UnstoppableSwap HTTP API', "Fetched providers via UnstoppableSwap HTTP API",
); );
} catch (e) { } catch (e) {
logger.error(e, 'Failed to fetch providers via UnstoppableSwap HTTP API'); logger.error(e, "Failed to fetch providers via UnstoppableSwap HTTP API");
} }
try { try {
const alerts = await fetchAlertsViaHttp(); const alerts = await fetchAlertsViaHttp();
store.dispatch(setAlerts(alerts)); store.dispatch(setAlerts(alerts));
logger.info({ alerts }, 'Fetched alerts via UnstoppableSwap HTTP API'); logger.info({ alerts }, "Fetched alerts via UnstoppableSwap HTTP API");
} catch (e) { } catch (e) {
logger.error(e, 'Failed to fetch alerts via UnstoppableSwap HTTP API'); logger.error(e, "Failed to fetch alerts via UnstoppableSwap HTTP API");
} }
try { try {
const xmrPrice = await fetchXmrPrice(); const xmrPrice = await fetchXmrPrice();
store.dispatch(setXmrPrice(xmrPrice)); store.dispatch(setXmrPrice(xmrPrice));
logger.info({ xmrPrice }, 'Fetched XMR price'); logger.info({ xmrPrice }, "Fetched XMR price");
const btcPrice = await fetchBtcPrice(); const btcPrice = await fetchBtcPrice();
store.dispatch(setBtcPrice(btcPrice)); store.dispatch(setBtcPrice(btcPrice));
logger.info({ btcPrice }, 'Fetched BTC price'); logger.info({ btcPrice }, "Fetched BTC price");
} catch (e) { } catch (e) {
logger.error(e, 'Error retrieving fiat prices'); logger.error(e, "Error retrieving fiat prices");
} }
} }

View file

@ -1,5 +1,5 @@
import { configureStore } from '@reduxjs/toolkit'; import { configureStore } from "@reduxjs/toolkit";
import { reducers } from 'store/combinedReducer'; import { reducers } from "store/combinedReducer";
export const store = configureStore({ export const store = configureStore({
reducer: reducers, reducer: reducers,

View file

@ -1,9 +1,9 @@
import swapReducer from './features/swapSlice'; import swapReducer from "./features/swapSlice";
import providersSlice from './features/providersSlice'; import providersSlice from "./features/providersSlice";
import torSlice from './features/torSlice'; import torSlice from "./features/torSlice";
import rpcSlice from './features/rpcSlice'; import rpcSlice from "./features/rpcSlice";
import alertsSlice from './features/alertsSlice'; import alertsSlice from "./features/alertsSlice";
import ratesSlice from './features/ratesSlice'; import ratesSlice from "./features/ratesSlice";
export const reducers = { export const reducers = {
swap: swapReducer, swap: swapReducer,

View file

@ -1,5 +1,5 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit'; import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { Alert } from 'models/apiModel'; import { Alert } from "models/apiModel";
export interface AlertsSlice { export interface AlertsSlice {
alerts: Alert[]; alerts: Alert[];
@ -10,7 +10,7 @@ const initialState: AlertsSlice = {
}; };
const alertsSlice = createSlice({ const alertsSlice = createSlice({
name: 'alerts', name: "alerts",
initialState, initialState,
reducers: { reducers: {
setAlerts(slice, action: PayloadAction<Alert[]>) { setAlerts(slice, action: PayloadAction<Alert[]>) {

View file

@ -1,8 +1,8 @@
import { createSlice, PayloadAction } from '@reduxjs/toolkit'; import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { ExtendedProviderStatus, ProviderStatus } from 'models/apiModel'; import { ExtendedProviderStatus, ProviderStatus } from "models/apiModel";
import { sortProviderList } from 'utils/sortUtils'; import { sortProviderList } from "utils/sortUtils";
import { isProviderCompatible } from 'utils/multiAddrUtils'; import { isProviderCompatible } from "utils/multiAddrUtils";
import { getStubTestnetProvider } from 'store/config'; import { getStubTestnetProvider } from "store/config";
const stubTestnetProvider = getStubTestnetProvider(); const stubTestnetProvider = getStubTestnetProvider();
@ -44,7 +44,7 @@ function selectNewSelectedProvider(
} }
export const providersSlice = createSlice({ export const providersSlice = createSlice({
name: 'providers', name: "providers",
initialState, initialState,
reducers: { reducers: {
discoveredProvidersByRendezvous( discoveredProvidersByRendezvous(

Some files were not shown because too many files have changed in this diff Show more