Skip to content
Merged
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
37 changes: 23 additions & 14 deletions app/stream/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,19 +77,27 @@ export default function StreamPage() {
setLoading(true);
setError(null);
try {
if (!/^\d+$/.test(id)) {
if (isCurrent()) setError('Invalid stream ID.');
return;
}

const addr = await getStreamAddress(publicKey, BigInt(id));
if (!isCurrent()) return;
if (!addr) { setError('Stream not found.'); return; }

const [streamInfo, wAmt] = await Promise.all([
getStreamInfo(publicKey, addr),
getWithdrawable(publicKey, addr),
]);

const streamInfo = await getStreamInfo(publicKey, addr);
if (!isCurrent()) return;

setStreamAddress(addr);
setInfo(streamInfo);
setWithdrawable(wAmt);

try {
const wAmt = await getWithdrawable(publicKey, addr);
if (isCurrent()) setWithdrawable(wAmt);
} catch {
if (isCurrent()) setWithdrawable(0n);
}
} catch (e) {
if (!isCurrent()) return;
setError(e instanceof Error ? e.message : 'Failed to load stream.');
Expand All @@ -109,17 +117,17 @@ export default function StreamPage() {
const addr = streamAddress;
const t = setInterval(async () => {
try {
const [streamInfo, wAmt] = await Promise.all([
getStreamInfo(publicKey, addr),
getWithdrawable(publicKey, addr),
]);
if (mounted.current) {
setInfo(streamInfo);
setWithdrawable(wAmt);
}
const streamInfo = await getStreamInfo(publicKey, addr);
if (mounted.current) setInfo(streamInfo);
} catch {
/* keep last-good data */
}
try {
const wAmt = await getWithdrawable(publicKey, addr);
if (mounted.current) setWithdrawable(wAmt);
} catch {
/* keep last-good withdrawable */
}
}, STREAM_REFRESH_MS);
return () => clearInterval(t);
}, [publicKey, streamAddress]);
Expand Down Expand Up @@ -200,6 +208,7 @@ export default function StreamPage() {
<RateTicker
ratePerSecond={info.ratePerSecond}
startBalance={withdrawable}
endTime={info.endTime}
/>
</p>
<p className="text-xs text-gray-400 dark:text-gray-500 mt-1">{tokenSymbol}</p>
Expand Down
22 changes: 18 additions & 4 deletions components/stream/RateTicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,16 @@ interface RateTickerProps {
startBalance: bigint;
/** Decimal places to display (default: 7 for XLM) */
decimals?: number;
/** Unix timestamp when the stream ends (0 = open-ended). Ticker freezes past this. */
endTime?: number;
}

/**
* Live-updating balance counter.
* Increments every 100ms based on ratePerSecond without any contract calls.
* Freezes at endTime so the ticker doesn't overshoot the contract balance (#398).
*/
export function RateTicker({ ratePerSecond, startBalance, decimals = 7 }: RateTickerProps) {
export function RateTicker({ ratePerSecond, startBalance, decimals = 7, endTime = 0 }: RateTickerProps) {
const startRef = useRef<{ ts: number; balance: bigint }>({
ts: Date.now(),
balance: startBalance,
Expand All @@ -30,12 +33,23 @@ export function RateTicker({ ratePerSecond, startBalance, decimals = 7 }: RateTi

useEffect(() => {
const id = setInterval(() => {
const elapsed = BigInt(Math.floor((Date.now() - startRef.current.ts) / 1000));
const current = startRef.current.balance + elapsed * ratePerSecond;
const elapsedMs = Date.now() - startRef.current.ts;
let elapsedSec = BigInt(Math.floor(elapsedMs / 1000));

if (endTime > 0) {
const endMs = endTime * 1000;
const remainingMs = endMs - startRef.current.ts;
const remainingSec = BigInt(Math.max(0, Math.floor(remainingMs / 1000)));
if (elapsedSec > remainingSec) elapsedSec = remainingSec;
}

if (elapsedSec < 0n) elapsedSec = 0n;

const current = startRef.current.balance + elapsedSec * ratePerSecond;
setDisplay(fromStroops(current, decimals));
}, 100);
return () => clearInterval(id);
}, [ratePerSecond, decimals]);
}, [ratePerSecond, decimals, endTime]);

return (
<span className="amount" aria-live="polite" aria-atomic="true">
Expand Down
2 changes: 1 addition & 1 deletion lib/indexer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export async function fetchTransactionHistory(
return;
}
if (mock) {
resolve(publicKey ? DEMO_TXS : []);
resolve(publicKey ? [] : DEMO_TXS);
} else {
reject(
new Error(
Expand Down