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