mirror of
https://github.com/purrgrammer/grimoire.git
synced 2026-06-16 17:48:34 +02:00
fix: prevent infinite transaction loading loop in WalletViewer
Previously, the transaction list would try to load constantly and fail repeatedly due to a circular dependency in the useEffect hooks. The listTransactions function from useWallet wasn't wrapped in useCallback, causing loadInitialTransactions to be recreated on every render, which triggered the useEffect infinitely. Changes: - Add txLoadAttempted and txLoadFailed flags to prevent repeated attempts - Only attempt to load transactions once on wallet connection - Fail silently on initial load (no toast spam) - Show retry button when transaction loading fails - Reset flags when wallet connects/disconnects or after successful payments - Make transaction list truly optional - wallet still works if loading fails This ensures a better UX when wallets don't support list_transactions or when the method fails for any reason.
This commit is contained in:
@@ -206,6 +206,8 @@ export default function WalletViewer() {
|
||||
const [hasMore, setHasMore] = useState(true);
|
||||
const [connectDialogOpen, setConnectDialogOpen] = useState(false);
|
||||
const [disconnectDialogOpen, setDisconnectDialogOpen] = useState(false);
|
||||
const [txLoadAttempted, setTxLoadAttempted] = useState(false);
|
||||
const [txLoadFailed, setTxLoadFailed] = useState(false);
|
||||
|
||||
// Send dialog state
|
||||
const [sendDialogOpen, setSendDialogOpen] = useState(false);
|
||||
@@ -245,7 +247,11 @@ export default function WalletViewer() {
|
||||
}, [getInfo]);
|
||||
|
||||
const loadInitialTransactions = useCallback(async () => {
|
||||
// Prevent repeated attempts if already failed
|
||||
if (txLoadFailed) return;
|
||||
|
||||
setLoading(true);
|
||||
setTxLoadAttempted(true);
|
||||
try {
|
||||
const result = await listTransactions({
|
||||
limit: BATCH_SIZE,
|
||||
@@ -254,26 +260,37 @@ export default function WalletViewer() {
|
||||
const txs = result.transactions || [];
|
||||
setTransactions(txs);
|
||||
setHasMore(txs.length === BATCH_SIZE);
|
||||
setTxLoadFailed(false);
|
||||
} catch (error) {
|
||||
console.error("Failed to load transactions:", error);
|
||||
toast.error("Failed to load transactions");
|
||||
setTxLoadFailed(true);
|
||||
// Don't show toast on initial load - just fail silently
|
||||
// User can retry via refresh button if needed
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [listTransactions]);
|
||||
}, [listTransactions, txLoadFailed]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isConnected) {
|
||||
loadWalletInfo();
|
||||
// Reset transaction loading flags when wallet connects
|
||||
setTxLoadAttempted(false);
|
||||
setTxLoadFailed(false);
|
||||
setTransactions([]);
|
||||
}
|
||||
}, [isConnected, loadWalletInfo]);
|
||||
|
||||
// Load transactions when wallet info is available
|
||||
// Load transactions when wallet info is available (only once)
|
||||
useEffect(() => {
|
||||
if (walletInfo?.methods.includes("list_transactions")) {
|
||||
if (
|
||||
walletInfo?.methods.includes("list_transactions") &&
|
||||
!txLoadAttempted &&
|
||||
!loading
|
||||
) {
|
||||
loadInitialTransactions();
|
||||
}
|
||||
}, [walletInfo, loadInitialTransactions]);
|
||||
}, [walletInfo, txLoadAttempted, loading, loadInitialTransactions]);
|
||||
useEffect(() => {
|
||||
if (!generatedPaymentHash || !receiveDialogOpen) return;
|
||||
|
||||
@@ -288,6 +305,9 @@ export default function WalletViewer() {
|
||||
toast.success("Payment received!");
|
||||
setReceiveDialogOpen(false);
|
||||
resetReceiveDialog();
|
||||
// Reload transactions - reset flags to allow reload
|
||||
setTxLoadAttempted(false);
|
||||
setTxLoadFailed(false);
|
||||
loadInitialTransactions();
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -491,7 +511,9 @@ export default function WalletViewer() {
|
||||
toast.success("Payment sent successfully");
|
||||
resetSendDialog();
|
||||
setSendDialogOpen(false);
|
||||
// Reload transactions
|
||||
// Reload transactions - reset flags to allow reload
|
||||
setTxLoadAttempted(false);
|
||||
setTxLoadFailed(false);
|
||||
loadInitialTransactions();
|
||||
} catch (error) {
|
||||
console.error("Payment failed:", error);
|
||||
@@ -823,6 +845,24 @@ export default function WalletViewer() {
|
||||
<div className="flex h-full items-center justify-center">
|
||||
<RefreshCw className="size-6 animate-spin text-muted-foreground" />
|
||||
</div>
|
||||
) : txLoadFailed ? (
|
||||
<div className="flex h-full flex-col items-center justify-center gap-3 p-4">
|
||||
<p className="text-sm text-muted-foreground text-center">
|
||||
Failed to load transaction history
|
||||
</p>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
setTxLoadFailed(false);
|
||||
setTxLoadAttempted(false);
|
||||
loadInitialTransactions();
|
||||
}}
|
||||
>
|
||||
<RefreshCw className="mr-2 size-4" />
|
||||
Retry
|
||||
</Button>
|
||||
</div>
|
||||
) : transactionsWithMarkers.length === 0 ? (
|
||||
<div className="flex h-full items-center justify-center">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
|
||||
Reference in New Issue
Block a user