Skip to content

Commit 0a7ce06

Browse files
AmazingAngclaude
andcommitted
Fix mobile wallet browser crash: reduce refresh storm after trade signing
- OrderForm: reduce refreshTradeState from 4 rounds to 2 (immediate + 3s), add AbortController to cancel on unmount/re-trigger, delay order-placed event by 1s to avoid overlapping with balance refresh - WalletButton: debounce (200ms) all refresh-header-balance and order-placed event listeners to coalesce rapid-fire events into single requests - TradeModal: remove currentPrice from modal key to prevent remount on every price update (root cause of "page refresh" feel after wallet signing) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 5961b71 commit 0a7ce06

3 files changed

Lines changed: 34 additions & 15 deletions

File tree

src/components/OrderForm.tsx

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -317,17 +317,23 @@ export default function OrderForm({
317317
}
318318
}, [optimisticSharesHeld, sharesHeld]);
319319

320+
const refreshAbortRef = useRef<AbortController | null>(null);
320321
const refreshTradeState = useCallback(async () => {
321-
const delays = [500, 2000, 5000];
322+
refreshAbortRef.current?.abort();
323+
const ac = new AbortController();
324+
refreshAbortRef.current = ac;
325+
notifyHeaderBalanceRefresh();
326+
await Promise.allSettled([refetchShares(), refetchUsdcBalance()]);
327+
await sleep(3000);
328+
if (ac.signal.aborted) return;
322329
notifyHeaderBalanceRefresh();
323330
await Promise.allSettled([refetchShares(), refetchUsdcBalance()]);
324-
for (const delay of delays) {
325-
await sleep(delay);
326-
notifyHeaderBalanceRefresh();
327-
await Promise.allSettled([refetchShares(), refetchUsdcBalance()]);
328-
}
329331
}, [refetchShares, refetchUsdcBalance]);
330332

333+
useEffect(() => {
334+
return () => { refreshAbortRef.current?.abort(); };
335+
}, []);
336+
331337
const handleEOAApprove = useCallback(async () => {
332338
if (!address) return;
333339
const exchangeAddr = negRisk ? NEG_RISK_EXCHANGE_ADDR : EXCHANGE_ADDRESS;
@@ -508,7 +514,9 @@ export default function OrderForm({
508514
}
509515
void refreshTradeState();
510516
if (typeof window !== "undefined") {
511-
window.dispatchEvent(new CustomEvent("polyworld:order-placed"));
517+
setTimeout(() => {
518+
window.dispatchEvent(new CustomEvent("polyworld:order-placed"));
519+
}, 1000);
512520
}
513521
addTradeToast(
514522
"success",

src/components/TradeModal.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,6 @@ interface TradeModalProps {
203203
export default function TradeModal({ state, onClose }: TradeModalProps) {
204204
const modalKey = [
205205
state.tokenId,
206-
state.currentPrice,
207206
state.outcomeName,
208207
state.defaultSide,
209208
state.marketTitle,

src/components/WalletButton.tsx

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -336,10 +336,14 @@ export default function WalletButton({ onRefresh, loading, lastSyncTime, onTrade
336336
fetchBal();
337337
fetchPortfolio();
338338
const iv = setInterval(() => { void fetchBal(); void fetchPortfolio(); }, 30_000);
339-
// Force bypass server cache when triggered by trade completion
340-
const onRefreshEv = () => { void fetchBal(true); void fetchPortfolio(); };
339+
// Force bypass server cache when triggered by trade completion (debounced 200ms)
340+
let refreshTimer: ReturnType<typeof setTimeout> | null = null;
341+
const onRefreshEv = () => {
342+
if (refreshTimer) clearTimeout(refreshTimer);
343+
refreshTimer = setTimeout(() => { void fetchBal(true); void fetchPortfolio(); }, 200);
344+
};
341345
window.addEventListener("polyworld:refresh-header-balance", onRefreshEv);
342-
return () => { cancelled = true; clearInterval(iv); window.removeEventListener("polyworld:refresh-header-balance", onRefreshEv); };
346+
return () => { cancelled = true; clearInterval(iv); if (refreshTimer) clearTimeout(refreshTimer); window.removeEventListener("polyworld:refresh-header-balance", onRefreshEv); };
343347
}, [effectiveProxy]);
344348

345349
// Fetch positions for portfolio hover dropdown
@@ -387,9 +391,13 @@ export default function WalletButton({ onRefresh, loading, lastSyncTime, onTrade
387391
};
388392
fetchPositions();
389393
const iv = setInterval(fetchPositions, 30_000);
390-
const onRefreshPositions = () => { void fetchPositions(); };
394+
let posTimer: ReturnType<typeof setTimeout> | null = null;
395+
const onRefreshPositions = () => {
396+
if (posTimer) clearTimeout(posTimer);
397+
posTimer = setTimeout(() => { void fetchPositions(); }, 200);
398+
};
391399
window.addEventListener("polyworld:refresh-header-balance", onRefreshPositions);
392-
return () => { cancelled = true; clearInterval(iv); window.removeEventListener("polyworld:refresh-header-balance", onRefreshPositions); };
400+
return () => { cancelled = true; clearInterval(iv); if (posTimer) clearTimeout(posTimer); window.removeEventListener("polyworld:refresh-header-balance", onRefreshPositions); };
393401
}, [effectiveProxy]);
394402

395403
// Fetch open orders for portfolio hover dropdown
@@ -404,9 +412,13 @@ export default function WalletButton({ onRefresh, loading, lastSyncTime, onTrade
404412
};
405413
fetchOrders();
406414
const iv = setInterval(fetchOrders, 15_000);
407-
const onOrderPlaced = () => { void fetchOrders(); };
415+
let orderTimer: ReturnType<typeof setTimeout> | null = null;
416+
const onOrderPlaced = () => {
417+
if (orderTimer) clearTimeout(orderTimer);
418+
orderTimer = setTimeout(() => { void fetchOrders(); }, 200);
419+
};
408420
window.addEventListener("polyworld:order-placed", onOrderPlaced);
409-
return () => { cancelled = true; clearInterval(iv); window.removeEventListener("polyworld:order-placed", onOrderPlaced); };
421+
return () => { cancelled = true; clearInterval(iv); if (orderTimer) clearTimeout(orderTimer); window.removeEventListener("polyworld:order-placed", onOrderPlaced); };
410422
}, [effectiveProxy, tradeSession?.sessionToken]);
411423

412424
const handleCancelOrder = useCallback(async (orderId: string) => {

0 commit comments

Comments
 (0)