import React from "react";
import { Box, Alert, AlertTitle } from "@mui/material";
import {
BobStateName,
GetSwapInfoResponseExt,
GetSwapInfoResponseExtRunningSwap,
isGetSwapInfoResponseRunningSwap,
TimelockCancel,
TimelockNone,
} from "models/tauriModelExt";
import { ReactNode } from "react";
import { exhaustiveGuard } from "utils/typescriptUtils";
import HumanizedBitcoinBlockDuration from "../../other/HumanizedBitcoinBlockDuration";
import TruncatedText from "../../other/TruncatedText";
import { SwapMoneroRecoveryButton } from "../../pages/history/table/SwapMoneroRecoveryButton";
import { TimelockTimeline } from "./TimelockTimeline";
import { useIsSpecificSwapRunning, useAppSelector } from "store/hooks";
import { selectSwapTimelock } from "store/selectors";
import { ExpiredTimelocks } from "models/tauriModel";
/**
* Component for displaying a list of messages.
* @param messages - Array of messages to display.
* @returns JSX.Element
*/
function MessageList({ messages }: { messages: ReactNode[] }) {
return (
{messages
.filter((msg) => msg != null)
.map((msg, i) => (
{msg}
))}
);
}
/**
* Sub-component for displaying alerts when the swap is in a safe state.
* @param swap - The swap information.
* @returns JSX.Element
*/
function BitcoinRedeemedStateAlert({ swap }: { swap: GetSwapInfoResponseExt }) {
return (
);
}
/**
* Sub-component for displaying alerts when the swap is in a state with no timelock info.
* @param swap - The swap information.
* @param punishTimelockOffset - The punish timelock offset.
* @returns JSX.Element
*/
function BitcoinLockedNoTimelockExpiredStateAlert({
timelock,
cancelTimelockOffset,
punishTimelockOffset,
isRunning,
}: {
timelock: TimelockNone;
cancelTimelockOffset: number;
punishTimelockOffset: number;
isRunning: boolean;
}) {
return (
If the swap isn't completed in{" "}
, it will be refunded
>,
"For that, you need to have the app open sometime within the refund period",
<>
After that, cooperation from the other party would be required to
recover the funds
>,
isRunning ? null : "Please resume the swap to continue",
]}
/>
);
}
/**
* Sub-component for displaying alerts when the swap timelock is expired
* The swap could be cancelled but not necessarily (the transaction might not have been published yet)
* But it doesn't matter because the swap cannot be completed anymore
* @param swap - The swap information.
* @returns JSX.Element
*/
function BitcoinPossiblyCancelledAlert({
swap,
timelock,
}: {
swap: GetSwapInfoResponseExt;
timelock: TimelockCancel;
}) {
return (
If we haven't refunded in{" "}
, cooperation from the other party will be required to recover the
funds
>,
]}
/>
);
}
/**
* Sub-component for displaying alerts requiring immediate action.
* @returns JSX.Element
*/
function PunishTimelockExpiredAlert() {
return (
);
}
/**
* Main component for displaying the appropriate swap alert status text.
* @param swap - The swap information.
* @returns JSX.Element | null
*/
export function StateAlert({
swap,
timelock,
isRunning,
}: {
swap: GetSwapInfoResponseExtRunningSwap;
timelock: ExpiredTimelocks | null;
isRunning: boolean;
}) {
switch (swap.state_name) {
// This is the state where the swap is safe because the other party has redeemed the Bitcoin
// It cannot be punished anymore
case BobStateName.BtcRedeemed:
return ;
// These are states that are at risk of punishment because the Bitcoin have been locked
// but has not been redeemed yet by the other party
case BobStateName.BtcLocked:
case BobStateName.XmrLockProofReceived:
case BobStateName.XmrLocked:
case BobStateName.EncSigSent:
case BobStateName.CancelTimelockExpired:
case BobStateName.BtcCancelled:
case BobStateName.BtcRefundPublished: // Even if the transactions have been published, it cannot be
case BobStateName.BtcEarlyRefundPublished: // guaranteed that they will be confirmed in time
if (timelock != null) {
switch (timelock.type) {
case "None":
return (
);
case "Cancel":
return (
);
case "Punish":
return ;
default:
exhaustiveGuard(timelock);
}
}
return ;
// If the Bitcoin lock transaction has not been published yet
// there is no need to display an alert
case BobStateName.BtcLockReadyToPublish:
return null;
default:
exhaustiveGuard(swap.state_name);
}
}
// How many blocks need to be left for the timelock to be considered unusual
// A bit arbitrary but we don't want to alarm the user
// 72 is the default cancel timelock in blocks
// 4 blocks are around 40 minutes
// If the swap has taken longer than 40 minutes, we consider it unusual
const UNUSUAL_AMOUNT_OF_TIME_HAS_PASSED_THRESHOLD = 72 - 4;
/**
* Main component for displaying the swap status alert.
* @param swap - The swap information.
* @returns JSX.Element | null
*/
export default function SwapStatusAlert({
swap,
onlyShowIfUnusualAmountOfTimeHasPassed,
}: {
swap: GetSwapInfoResponseExt;
onlyShowIfUnusualAmountOfTimeHasPassed?: boolean;
}) {
const timelock = useAppSelector(selectSwapTimelock(swap.swap_id));
if (!isGetSwapInfoResponseRunningSwap(swap)) {
return null;
}
if (timelock == null) {
return null;
}
const hasUnusualAmountOfTimePassed =
timelock.type === "None" &&
timelock.content.blocks_left > UNUSUAL_AMOUNT_OF_TIME_HAS_PASSED_THRESHOLD;
if (onlyShowIfUnusualAmountOfTimeHasPassed && hasUnusualAmountOfTimePassed) {
return null;
}
const isRunning = useIsSpecificSwapRunning(swap.swap_id);
return (
{isRunning ? (
hasUnusualAmountOfTimePassed ? (
"Swap has been running for a while"
) : (
"Swap is running"
)
) : (
<>
Swap {swap.swap_id} is
not running
>
)}
);
}