diff --git a/frontend/src/components/SendMoneyFlow.tsx b/frontend/src/components/SendMoneyFlow.tsx index 4c652340..dec4012f 100644 --- a/frontend/src/components/SendMoneyFlow.tsx +++ b/frontend/src/components/SendMoneyFlow.tsx @@ -133,6 +133,9 @@ export const SendMoneyFlow: React.FC = ({ const [isSubmitting, setIsSubmitting] = useState(false); const [isComplete, setIsComplete] = useState(false); const [limitStatus, setLimitStatus] = useState(null); + const [fxFetchedAt, setFxFetchedAt] = useState(null); + const [fxSecondsLeft, setFxSecondsLeft] = useState(null); + const [fxExpired, setFxExpired] = useState(false); const parsedAmount = useMemo(() => Number(amount), [amount]); @@ -146,6 +149,26 @@ export const SendMoneyFlow: React.FC = ({ return () => { cancelled = true; }; }, [asset, destinationCountry, getDailyLimitStatus, senderAddress]); + // Record when the FX rate was fetched (entering review step) + useEffect(() => { + if (step === 4 || step === 5) { + setFxFetchedAt(Date.now()); + setFxExpired(false); + } + }, [step]); + + // Countdown timer for FX rate expiry on review step + useEffect(() => { + if ((step !== 4 && step !== 5) || fxFetchedAt === null) return; + const interval = setInterval(() => { + const elapsed = Date.now() - fxFetchedAt; + const remaining = Math.max(0, Math.ceil((FX_TTL_MS - elapsed) / 1000)); + setFxSecondsLeft(remaining); + if (remaining === 0) setFxExpired(true); + }, 1000); + return () => clearInterval(interval); + }, [step, fxFetchedAt]); + const validateCurrentStep = (): string | null => { if (step === 1) { if (!amount) return t('sendMoney.errors.amountRequired'); @@ -339,26 +362,37 @@ export const SendMoneyFlow: React.FC = ({ if (step === 4 || step === 5) { return ( -
-
-
{t('sendMoney.review.amount')}
-
{amount || '-'}
-
-
-
{t('sendMoney.review.asset')}
-
{asset || '-'}
-
-
-
{t('sendMoney.review.recipient')}
-
{recipient || '-'}
-
- {memo.trim() && ( + <> +
+
+
{t('sendMoney.review.amount')}
+
{amount || '-'}
+
+
+
{t('sendMoney.review.asset')}
+
{asset || '-'}
+
-
{t('sendMoney.review.memo')}
-
{memo.trim()}
+
{t('sendMoney.review.recipient')}
+
{recipient || '-'}
+ {memo.trim() && ( +
+
{t('sendMoney.review.memo')}
+
{memo.trim()}
+
+ )} +
+ {fxExpired ? ( +

+ Rate expired — please go back and refresh. +

+ ) : fxSecondsLeft !== null && ( +

+ Rate refreshes in {fxSecondsLeft}s +

)} -
+ ); }