Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 0 additions & 43 deletions src/components/WaitingForRound.tsx

This file was deleted.

2 changes: 1 addition & 1 deletion src/icons/Loading.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export default function LoadingIcon({ small }: { small?: boolean }) {
const size = small ? 32 : 64
const size = small ? 20 : 64
return (
<svg height={size} width={size} viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'>
<circle cx='12' cy='2' r='0' fill='currentColor'>
Expand Down
128 changes: 65 additions & 63 deletions src/screens/Settings/Vtxos.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -296,73 +297,74 @@ export default function Vtxos() {
text={showList ? 'Virtual Coins' : 'Next Renewal'}
/>
<Content>
{rollingover ? (
<WaitingForRound rollover />
) : (
<Padded>
<FlexCol className='scroll-fade'>
<ErrorMessage error={Boolean(error)} text={error} />
{listableVtxos.length + allUtxos.length === 0 ? (
<EmptyCoinsList />
) : showList ? (
<FlexCol gap='2rem'>
{success ? <WarningBox green text='Coins renewed successfully' /> : null}
{listableVtxos.length > 0 ? (
<FlexCol gap='0.5rem'>
<Text capitalize color='dark50' smaller>
Your virtual coins with amount and expiration
</Text>
{listableVtxos.map((v: ExtendedVirtualCoin) => (
<VtxoLine key={v.txid} vtxo={v} />
))}
</FlexCol>
) : null}
{!hideUtxos && allUtxos.length > 0 ? (
<FlexCol gap='0.5rem'>
<Text capitalize color='dark50' smaller>
Your boarding utxos with amount and expiration
</Text>
{allUtxos.map((u: ExtendedCoin) => (
<UtxoLine key={u.txid} utxo={u} />
))}
</FlexCol>
) : null}
</FlexCol>
) : (
<>
<FlexCol gap='0.5rem' margin='0 0 1rem 0'>
<Padded>
<FlexCol className='scroll-fade'>
<ErrorMessage error={Boolean(error)} text={error} />
{rollingover ? (
<Info color='purple' icon={<LoadingIcon small />} title='Renewing'>
<Text wrap>Renewing your virtual coins. This may take a few moments.</Text>
</Info>
) : null}
{listableVtxos.length + allUtxos.length === 0 ? (
<EmptyCoinsList />
) : showList ? (
<FlexCol gap='2rem'>
{success ? <WarningBox green text='Coins renewed successfully' /> : null}
{listableVtxos.length > 0 ? (
<FlexCol gap='0.5rem'>
<Text capitalize color='dark50' smaller>
Your virtual coins with amount and expiration
</Text>
{listableVtxos.map((v: ExtendedVirtualCoin) => (
<VtxoLine key={v.txid} vtxo={v} />
))}
</FlexCol>
) : null}
{!hideUtxos && allUtxos.length > 0 ? (
<FlexCol gap='0.5rem'>
<Text capitalize color='dark50' smaller>
Next renewal
Your boarding utxos with amount and expiration
</Text>
<Box>
<Text>{prettyDate(wallet.nextRollover)}</Text>
<Text>{prettyAgo(wallet.nextRollover)}</Text>
</Box>
{success ? <WarningBox green text='Coins renewed successfully' /> : null}
{allUtxos.map((u: ExtendedCoin) => (
<UtxoLine key={u.txid} utxo={u} />
))}
</FlexCol>
<FlexCol gap='0.5rem' margin='2rem 0 0 0'>
<TextSecondary>First virtual coin expiration: {prettyAgo(wallet.nextRollover)}.</TextSecondary>
{wallet.thresholdMs ? (
) : null}
</FlexCol>
) : (
<>
<FlexCol gap='0.5rem' margin='0 0 1rem 0'>
<Text capitalize color='dark50' smaller>
Next renewal
</Text>
<Box>
<Text>{prettyDate(wallet.nextRollover)}</Text>
<Text>{prettyAgo(wallet.nextRollover)}</Text>
</Box>
{success ? <WarningBox green text='Coins renewed successfully' /> : null}
</FlexCol>
<FlexCol gap='0.5rem' margin='2rem 0 0 0'>
<TextSecondary>First virtual coin expiration: {prettyAgo(wallet.nextRollover)}.</TextSecondary>
{wallet.thresholdMs ? (
<TextSecondary>
Automatic renewal occurs for virtual coins expiring within{' '}
{prettyDelta(Math.floor(wallet.thresholdMs / 1_000))}.
</TextSecondary>
) : null}
{startTime && duration ? (
<>
<TextSecondary>Settlement during market hours offers lower fees.</TextSecondary>
<TextSecondary>
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)}.
</TextSecondary>
) : null}
{startTime && duration ? (
<>
<TextSecondary>Settlement during market hours offers lower fees.</TextSecondary>
<TextSecondary>
Next market hour: {prettyDate(startTime)} ({prettyAgo(startTime, true)}) for{' '}
{prettyDelta(duration)}.
</TextSecondary>
</>
) : null}
</FlexCol>
</>
)}
</FlexCol>
</Padded>
)}
</>
) : null}
</FlexCol>
</>
)}
</FlexCol>
</Padded>
</Content>
{utxoTxsAllowed() && vtxoTxsAllowed() ? (
<>
Expand Down
2 changes: 1 addition & 1 deletion src/screens/Wallet/Send/Details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ export default function SendDetails() {
/>
) : (
<LoadingLogo
text='Paying to Bitcoin'
text='Paying to mainnet'
done={sendDone}
exitMode='fly-up'
onExitComplete={handleExitComplete}
Expand Down
46 changes: 29 additions & 17 deletions src/screens/Wallet/Transaction.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,21 @@ import Content from '../../components/Content'
import Info from '../../components/Info'
import FlexCol from '../../components/FlexCol'
import FlexRow from '../../components/FlexRow'
import WaitingForRound from '../../components/WaitingForRound'
import { sleep } from '../../lib/sleep'
import Text, { TextSecondary } from '../../components/Text'
import AssetAvatar from '../../components/AssetAvatar'
import Details, { DetailsProps } from '../../components/Details'
import VtxosIcon from '../../icons/Vtxos'
import CheckMarkIcon from '../../icons/CheckMark'
import LoadingIcon from '../../icons/Loading'
import { AspContext } from '../../providers/asp'
import Reminder from '../../components/Reminder'
import { LimitsContext } from '../../providers/limits'
import { getInputsToSettle } from '../../lib/asp'

export default function Transaction() {
const { utxoTxsAllowed, vtxoTxsAllowed } = useContext(LimitsContext)
const { txInfo, setTxInfo } = useContext(FlowContext)
const { txInfo } = useContext(FlowContext)
const { aspInfo, calcBestMarketHour } = useContext(AspContext)
const { assetMetadataCache, settlePreconfirmed, vtxos, vtxoManager, wallet, svcWallet } = useContext(WalletContext)

Expand All @@ -52,9 +52,12 @@ export default function Transaction() {
const [settling, setSettling] = useState(false)
const [startTime, setStartTime] = useState(0)

// Hide status banners while settling or after success to prevent conflicting UI states
const hideStatusBanners = settling || settleSuccess

useEffect(() => {
setButtonLabel(settling ? 'Settling...' : defaultButtonLabel)
}, [settling])
}, [settling, defaultButtonLabel])

useEffect(() => {
if (!tx) return
Expand Down Expand Up @@ -93,7 +96,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))
}
Expand All @@ -106,15 +112,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),
Expand All @@ -130,15 +137,20 @@ export default function Transaction() {
<Padded>
<FlexCol>
<ErrorMessage error={Boolean(error)} text={error} />
{expiredBoardingTx ? (
{settling ? (
<Info color='purple' icon={<LoadingIcon small />} title='Settling'>
<Text wrap>{boardingTx ? 'Processing your boarding transaction...' : 'Settling transaction...'}</Text>
</Info>
) : null}
{expiredBoardingTx && !hideStatusBanners ? (
<Info color='red' icon={<VtxosIcon />} title='Expired'>
<Text wrap>Boarding transaction expired.</Text>
</Info>
) : unconfirmedBoardingTx ? (
<Info color='orange' icon={<VtxosIcon />} title='Unconfirmed'>
<Text wrap>Onchain transaction unconfirmed. Please wait for confirmation.</Text>
</Info>
) : tx.preconfirmed && tx.boardingTxid ? (
) : tx.preconfirmed && tx.boardingTxid && !hideStatusBanners ? (
<Info color='orange' icon={<VtxosIcon />} title='Pending boarding'>
<Text wrap>Onboard transaction confirmed on-chain.</Text>
</Info>
Expand Down Expand Up @@ -192,7 +204,7 @@ export default function Transaction() {
!settling

const Buttons = () =>
expiredBoardingTx ? (
expiredBoardingTx && !hideStatusBanners ? (
<ButtonsOnBottom>
<Button onClick={handleResend} label='Resend' disabled={resending || true} />
</ButtonsOnBottom>
Expand All @@ -215,7 +227,7 @@ export default function Transaction() {
return (
<>
<Header text='Transaction' back />
{settling ? <WaitingForRound settle /> : <Body />}
<Body />
<Buttons />
</>
)
Expand Down
Loading