diff --git a/src/components/WaitingForRound.tsx b/src/components/WaitingForRound.tsx deleted file mode 100644 index b3fd2719..00000000 --- a/src/components/WaitingForRound.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { useEffect, useRef, useState } from 'react' -import LoadingLogo from './LoadingLogo' -import { getInfoLogLineMsg, getInfoLogsLength } from '../lib/logs' -import { sleep } from '../lib/sleep' - -interface WaitingForRoundProps { - rollover?: boolean - settle?: boolean - done?: boolean - exitMode?: 'fly-to-target' | 'fly-up' | 'none' - onExitComplete?: () => void -} - -export default function WaitingForRound({ rollover, settle, done, exitMode, onExitComplete }: WaitingForRoundProps) { - const initial = settle ? 'Settling transactions' : rollover ? 'Renewing your virtual coins' : 'Paying to mainnet' - const message = initial + '. This may take a few moments.' - - const [logLength, setLogLength] = useState(getInfoLogsLength()) - const [logMessage, setLogMessage] = useState(message) - - const firstRun = useRef(true) - - useEffect(() => { - let interval: NodeJS.Timeout - // give 2 seconds to read initial message - sleep(2000).then(() => { - interval = setInterval(() => { - setLogLength(getInfoLogsLength()) - }, 500) - }) - return () => clearInterval(interval) - }, []) - - useEffect(() => { - if (firstRun.current) { - firstRun.current = false - return - } - setLogMessage(getInfoLogLineMsg(logLength - 1)) - }, [logLength]) - - return -} diff --git a/src/icons/Loading.tsx b/src/icons/Loading.tsx index 46cb9989..c61f624e 100644 --- a/src/icons/Loading.tsx +++ b/src/icons/Loading.tsx @@ -1,5 +1,5 @@ export default function LoadingIcon({ small }: { small?: boolean }) { - const size = small ? 32 : 64 + const size = small ? 20 : 64 return ( diff --git a/src/screens/Settings/Vtxos.tsx b/src/screens/Settings/Vtxos.tsx index e08956b2..61ab0c73 100644 --- a/src/screens/Settings/Vtxos.tsx +++ b/src/screens/Settings/Vtxos.tsx @@ -13,7 +13,8 @@ import FlexRow from '../../components/FlexRow' import { ConfigContext } from '../../providers/config' import { extractError } from '../../lib/error' import ErrorMessage from '../../components/Error' -import WaitingForRound from '../../components/WaitingForRound' +import Info from '../../components/Info' +import LoadingIcon from '../../icons/Loading' import { AspContext } from '../../providers/asp' import Reminder from '../../components/Reminder' import { getInputsToSettle, settleVtxos } from '../../lib/asp' @@ -297,73 +298,74 @@ export default function Vtxos() { text={showList ? 'Virtual Coins' : 'Next Renewal'} /> - {rollingover ? ( - - ) : ( - - - - {listableVtxos.length + allUtxos.length === 0 ? ( - - ) : showList ? ( - - {success ? : null} - {listableVtxos.length > 0 ? ( - - - Your virtual coins with amount and expiration - - {listableVtxos.map((v: ExtendedVirtualCoin) => ( - - ))} - - ) : null} - {!hideUtxos && allUtxos.length > 0 ? ( - - - Your boarding utxos with amount and expiration - - {allUtxos.map((u: ExtendedCoin) => ( - - ))} - - ) : null} - - ) : ( - <> - + + + + {rollingover ? ( + } title='Renewing'> + Renewing your virtual coins. This may take a few moments. + + ) : null} + {listableVtxos.length + allUtxos.length === 0 ? ( + + ) : showList ? ( + + {success ? : null} + {listableVtxos.length > 0 ? ( + + + Your virtual coins with amount and expiration + + {listableVtxos.map((v: ExtendedVirtualCoin) => ( + + ))} + + ) : null} + {!hideUtxos && allUtxos.length > 0 ? ( + - Next renewal + Your boarding utxos with amount and expiration - - {prettyDate(wallet.nextRollover)} - {prettyAgo(wallet.nextRollover)} - - {success ? : null} + {allUtxos.map((u: ExtendedCoin) => ( + + ))} - - First virtual coin expiration: {prettyAgo(wallet.nextRollover)}. - {wallet.thresholdMs ? ( + ) : null} + + ) : ( + <> + + + Next renewal + + + {prettyDate(wallet.nextRollover)} + {prettyAgo(wallet.nextRollover)} + + {success ? : null} + + + First virtual coin expiration: {prettyAgo(wallet.nextRollover)}. + {wallet.thresholdMs ? ( + + Automatic renewal occurs for virtual coins expiring within{' '} + {prettyDelta(Math.floor(wallet.thresholdMs / 1_000))}. + + ) : null} + {startTime && duration ? ( + <> + Settlement during market hours offers lower fees. - Automatic renewal occurs for virtual coins expiring within{' '} - {prettyDelta(Math.floor(wallet.thresholdMs / 1_000))}. + Next market hour: {prettyDate(startTime)} ({prettyAgo(startTime, true)}) for{' '} + {prettyDelta(duration)}. - ) : null} - {startTime && duration ? ( - <> - Settlement during market hours offers lower fees. - - Next market hour: {prettyDate(startTime)} ({prettyAgo(startTime, true)}) for{' '} - {prettyDelta(duration)}. - - - ) : null} - - - )} - - - )} + + ) : null} + + + )} + + {utxoTxsAllowed() && vtxoTxsAllowed() ? ( <> diff --git a/src/screens/Wallet/Send/Details.tsx b/src/screens/Wallet/Send/Details.tsx index 249ab535..55772c82 100644 --- a/src/screens/Wallet/Send/Details.tsx +++ b/src/screens/Wallet/Send/Details.tsx @@ -183,7 +183,7 @@ export default function SendDetails() { /> ) : ( { setButtonLabel(settling ? 'Settling...' : defaultButtonLabel) - }, [settling]) + }, [settling, defaultButtonLabel]) useEffect(() => { if (!tx) return @@ -94,7 +97,10 @@ export default function Transaction() { await settlePreconfirmed() await sleep(2000) // give time to read last message setSettleSuccess(true) - if (tx) setTxInfo({ ...tx, preconfirmed: false, settled: true }) + // Note: We don't optimistically update txInfo here because: + // 1. The wallet will reload and reflect the settled state automatically + // 2. Updating txInfo after an async operation can corrupt navigation if + // the user has navigated to a different transaction } catch (err) { setError(extractError(err)) } @@ -107,15 +113,16 @@ export default function Transaction() { direction: issuanceTx ? 'Issuance' : burnTx ? 'Burn' : tx.type === 'sent' ? 'Sent' : 'Received', when: tx.createdAt ? prettyAgo(tx.createdAt) : !unconfirmedBoardingTx ? 'Unknown' : 'Unconfirmed', date: tx.createdAt ? prettyDate(tx.createdAt) : !unconfirmedBoardingTx ? 'Unknown' : 'Unconfirmed', - status: expiredBoardingTx - ? 'Expired' - : unconfirmedBoardingTx - ? 'Unconfirmed' - : boardingTx && tx.preconfirmed - ? 'Pending boarding' - : tx.settled - ? 'Settled' - : 'Preconfirmed', + status: + settleSuccess || tx.settled + ? 'Settled' + : expiredBoardingTx + ? 'Expired' + : unconfirmedBoardingTx + ? 'Unconfirmed' + : boardingTx && tx.preconfirmed + ? 'Pending boarding' + : 'Preconfirmed', type: boardingTx ? 'Boarding' : 'Offchain', txid: tx.boardingTxid || tx.redeemTxid || '', isOffchainTx: !tx.boardingTxid && Boolean(tx.redeemTxid), @@ -131,7 +138,12 @@ export default function Transaction() { - {expiredBoardingTx ? ( + {settling ? ( + } title='Settling'> + {boardingTx ? 'Processing your boarding transaction...' : 'Settling transaction...'} + + ) : null} + {expiredBoardingTx && !hideStatusBanners ? ( } title='Expired'> Boarding transaction expired. @@ -139,7 +151,7 @@ export default function Transaction() { } title='Unconfirmed'> Onchain transaction unconfirmed. Please wait for confirmation. - ) : tx.preconfirmed && tx.boardingTxid ? ( + ) : tx.preconfirmed && tx.boardingTxid && !hideStatusBanners ? ( } title='Pending boarding'> Onboard transaction confirmed on-chain. @@ -193,7 +205,7 @@ export default function Transaction() { !settling const Buttons = () => - expiredBoardingTx ? ( + expiredBoardingTx && !hideStatusBanners ? ( @@ -216,7 +228,7 @@ export default function Transaction() { return ( <>
- {settling ? :
} + )