Skip to content

Commit b248e18

Browse files
authored
Merge pull request #409 from manuelusman73-png/feat/issue-262-live-exchange-rate
feat: live exchange rate display in payment form (#262)
2 parents a8bfd48 + 6f58ce0 commit b248e18

3 files changed

Lines changed: 19 additions & 14 deletions

File tree

frontend/src/App.jsx

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ function App() {
8989

9090
const wsStatus = useWebSocket(account?.publicKey ?? null, handleWsMessage);
9191
const { status: networkStatus } = useNetworkStatus();
92-
const xlmUsdRate = useExchangeRate(lastWsMessage);
92+
const { rate: xlmUsdRate, loading: rateLoading } = useExchangeRate(lastWsMessage);
9393

9494
useEffect(() => {
9595
const onKeyDown = (e) => {
@@ -542,11 +542,12 @@ function App() {
542542
)}
543543
</AnimatePresence>
544544
<FeeDisplay amount={amount} visible={amountValid} />
545-
{amountValid && xlmUsdRate && (
546-
<p className="rate-estimate" aria-live="polite">
547-
≈ ${(parseFloat(amount) * xlmUsdRate).toFixed(2)} USD
548-
<span className="rate-source"> · live rate</span>
549-
</p>
545+
{amountValid && (xlmUsdRate
546+
? <p className="rate-estimate" aria-live="polite">
547+
≈ ${(parseFloat(amount) * xlmUsdRate).toFixed(2)} USD
548+
<span className="rate-source"> · live rate</span>
549+
</p>
550+
: rateLoading && <p className="rate-estimate rate-estimate--loading" aria-live="polite">Loading rate…</p>
550551
)}
551552
<div style={{ display: 'flex', gap: 8, alignItems: 'center', flexWrap: 'wrap' }}>
552553
<motion.button onClick={sendPayment} {...tap} disabled={!recipientValid || !amountValid || loading === 'send'}>
@@ -758,11 +759,12 @@ function App() {
758759
</div>
759760

760761
<FeeDisplay amount={amount} visible={amountValid} />
761-
{amountValid && xlmUsdRate && (
762-
<p className="rate-estimate" aria-live="polite">
763-
≈ ${(parseFloat(amount) * xlmUsdRate).toFixed(2)} USD
764-
<span className="rate-source"> · live rate</span>
765-
</p>
762+
{amountValid && (xlmUsdRate
763+
? <p className="rate-estimate" aria-live="polite">
764+
≈ ${(parseFloat(amount) * xlmUsdRate).toFixed(2)} USD
765+
<span className="rate-source"> · live rate</span>
766+
</p>
767+
: rateLoading && <p className="rate-estimate rate-estimate--loading" aria-live="polite">Loading rate…</p>
766768
)}
767769
<div style={{ display: 'flex', gap: 8, alignItems: 'center', flexWrap: 'wrap' }}>
768770
<motion.button

frontend/src/hooks/useExchangeRate.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,17 @@ import axios from 'axios';
66
* rateChange WebSocket events (passed in as `wsMessage`).
77
*
88
* @param {object|null} wsMessage – latest message from useWebSocket's onMessage
9-
* @returns {number|null} rate – XLM price in USD, or null while loading
9+
* @returns {{ rate: number|null, loading: boolean }}
1010
*/
1111
export function useExchangeRate(wsMessage) {
1212
const [rate, setRate] = useState(null);
13+
const [loading, setLoading] = useState(true);
1314

1415
useEffect(() => {
1516
axios.get('/api/stellar/exchange-rate/XLM/USD')
1617
.then(({ data }) => setRate(data.rate))
17-
.catch(() => {});
18+
.catch(() => {})
19+
.finally(() => setLoading(false));
1820
}, []);
1921

2022
useEffect(() => {
@@ -23,5 +25,5 @@ export function useExchangeRate(wsMessage) {
2325
}
2426
}, [wsMessage]);
2527

26-
return rate;
28+
return { rate, loading };
2729
}

frontend/src/index.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -795,6 +795,7 @@ kbd {
795795
font-weight: 500;
796796
}
797797
.rate-source { color: #94a3b8; font-weight: 400; font-size: 11px; }
798+
.rate-estimate--loading { color: #94a3b8; font-style: italic; }
798799
[data-theme="dark"] .rate-estimate { color: #38bdf8; }
799800
[data-theme="dark"] .rate-source { color: #64748b; }
800801
.fee-tooltip {

0 commit comments

Comments
 (0)