From 167aeee6757e511ac7183dd5f4dc042628e0d5ec Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 30 Jan 2026 00:24:46 +0000 Subject: [PATCH] perf: fix Receive tab QR code performance and New Address refresh - Remove aggressive cache clearing on screen focus that was causing 5-15 second delays - Show wallet's last address instantly on load, then verify in background - Fix New Address button to directly use the newly generated address instead of stale cache lookup - Clear address metadata cache after generating new address for fresh future lookups - Remove unused imports (useFocusEffect, useCallback) The previous implementation cleared the address cache every time the Receive tab was focused, forcing a full blockchain address discovery. This caused slow load times on every tab navigation. The "New Address" button also failed to show the new address because it queried stale cached metadata that didn't include the newly generated address. https://claude.ai/code/session_01QdDhtY1VwbnMResDP2spm8 --- app/(tabs)/receive.tsx | 126 ++++++++++++++++++++++------------------- 1 file changed, 67 insertions(+), 59 deletions(-) diff --git a/app/(tabs)/receive.tsx b/app/(tabs)/receive.tsx index a35abdd..083c7c2 100644 --- a/app/(tabs)/receive.tsx +++ b/app/(tabs)/receive.tsx @@ -6,9 +6,9 @@ import { useTabAnimation } from '@/hooks/use-tab-animation'; import { useWallet } from '@/hooks/wallet-store'; import { loadWalletService } from '@/utils/wallet-service-loader'; import * as Clipboard from 'expo-clipboard'; -import { Stack, router, useFocusEffect } from 'expo-router'; +import { Stack, router } from 'expo-router'; import { Copy, RefreshCw, Share as ShareIcon } from 'lucide-react-native'; -import React, { useCallback, useEffect, useRef, useState } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; import { Alert, Animated, @@ -59,58 +59,70 @@ function ReceiveScreenContent({ walletContext }: { walletContext: ReturnType(true); const [lastGenTime, setLastGenTime] = useState(0); - // Clear address cache when screen becomes focused to ensure fresh data - // This prevents showing used addresses after receiving funds - useFocusEffect( - useCallback(() => { - if (currentWallet?.xpub && walletService.clearAddressCache) { - console.log('🔄 Receive screen focused - clearing address cache for fresh data'); - walletService.clearAddressCache(currentWallet.xpub); - } - }, [currentWallet?.xpub]) - ); + // NOTE: Removed aggressive cache clearing on focus - this was causing slow load times + // Cache TTL (2 minutes) provides sufficient freshness while maintaining performance + // The address discovery service handles cache invalidation appropriately - // Load first unused address when wallet changes - // Note: We track the xpub to detect wallet switches. The effect will also run when - // addresses change, which is acceptable because the getFirstUnusedReceivingAddress - // function uses cached discovery data, making subsequent calls efficient. + // Load receiving address when wallet changes + // OPTIMIZED: Show wallet's last address immediately for instant UI, then verify in background + // This eliminates the 5-15 second delay while still ensuring address freshness useEffect(() => { - const loadFirstUnusedAddress = async () => { - if (!currentWallet?.xpub) { - setIsLoadingAddress(false); - return; - } + if (!currentWallet?.xpub) { + setIsLoadingAddress(false); + return; + } + + // INSTANT: Use wallet's last address immediately (no API calls) + // This ensures the QR code appears instantly when opening the Receive tab + const lastAddress = currentWallet.addresses?.[currentWallet.addresses.length - 1] || ''; + if (lastAddress) { + console.log('⚡ Showing wallet address instantly:', lastAddress.substring(0, 20) + '...'); + setCurrentAddress(lastAddress); + setIsLoadingAddress(false); + } + // BACKGROUND: Verify/find first unused address (non-blocking) + // This runs after the UI is already displayed + const verifyAddressInBackground = async () => { try { - setIsLoadingAddress(true); - console.log('🔍 Loading first unused receiving address...'); - - // Get first unused address within gap limit - const unusedAddress = walletService.getFirstUnusedReceivingAddress + console.log('🔍 Background: Verifying first unused receiving address...'); + + const unusedAddress = walletService.getFirstUnusedReceivingAddress ? await walletService.getFirstUnusedReceivingAddress(currentWallet.xpub) : null; - - if (unusedAddress) { - console.log('✅ Found first unused address:', unusedAddress.substring(0, 20) + '...'); + + if (unusedAddress && unusedAddress !== lastAddress) { + console.log('✅ Background: Found different unused address:', unusedAddress.substring(0, 20) + '...'); setCurrentAddress(unusedAddress); } else { - // Fallback to last wallet address if no unused found - console.log('⚠️ No unused address found, using last wallet address'); - const fallbackAddress = currentWallet.addresses?.[currentWallet.addresses.length - 1] || ''; - setCurrentAddress(fallbackAddress); + console.log('✅ Background: Current address is valid'); } } catch (error) { - console.error('❌ Failed to load first unused address:', error); - // Use same fallback logic as above - const fallbackAddress = currentWallet.addresses?.[currentWallet.addresses.length - 1] || ''; - setCurrentAddress(fallbackAddress); - } finally { - setIsLoadingAddress(false); + console.error('⚠️ Background verification failed (keeping current address):', error); + // Keep the current address on error - don't disrupt the UI } }; - loadFirstUnusedAddress(); - }, [currentWallet]); + // Only run background verification if we have an address to verify + if (lastAddress) { + verifyAddressInBackground(); + } else { + // No addresses in wallet yet - need to wait for initial address generation + setIsLoadingAddress(true); + walletService.getFirstUnusedReceivingAddress?.(currentWallet.xpub) + .then(unusedAddress => { + if (unusedAddress) { + setCurrentAddress(unusedAddress); + } + }) + .catch(error => { + console.error('❌ Failed to load initial address:', error); + }) + .finally(() => { + setIsLoadingAddress(false); + }); + } + }, [currentWallet?.xpub, currentWallet?.addresses?.length]); // Spin animation for the refresh icon useEffect(() => { @@ -166,15 +178,25 @@ function ReceiveScreenContent({ walletContext }: { walletContext: ReturnType= GAP_LIMIT_WARNING_THRESHOLD) { Alert.alert( 'Address Limit Warning', @@ -182,20 +204,6 @@ function ReceiveScreenContent({ walletContext }: { walletContext: ReturnType