import NetInfo from '@react-native-community/netinfo'; /** * Utility functions for detecting and handling network errors */ interface NetworkErrorInfo { isNetworkError: boolean; errorType: 'offline' | 'timeout' | 'connection' | 'server' | 'unknown'; userMessage: string; technicalMessage: string; } /** * Checks if the device has internet connectivity using NetInfo */ export async function isDeviceConnected(): Promise { try { const netInfo = await NetInfo.fetch(); // During network transitions, isInternetReachable can be null // Only consider device online if both connected AND internet is explicitly reachable // If isInternetReachable is null (unknown), consider it offline to prevent premature API calls return netInfo.isConnected === true && netInfo.isInternetReachable === true; } catch (error) { console.debug('Error checking network connectivity:', error); // Fallback to true to avoid blocking app functionality return true; } } /** * Checks if the device has a stable internet connection * Useful for critical operations like token refresh */ export async function hasStableConnection(): Promise { try { // First check basic connectivity const isConnected = await isDeviceConnected(); if (!isConnected) return false; // For critical operations, double-check after a short delay // This helps ensure the connection is stable, not just transitioning await new Promise((resolve) => setTimeout(resolve, 500)); const netInfo = await NetInfo.fetch(); // Require explicit true values for stable connection return ( netInfo.isConnected === true && netInfo.isInternetReachable === true && netInfo.details !== null ); } catch (error) { console.debug('Error checking stable connectivity:', error); return false; } } /** * Synchronous version for immediate error analysis (pattern matching only) */ export function analyzeNetworkErrorSync(error: unknown): NetworkErrorInfo { return analyzeErrorPatterns(error); } /** * Async version with NetInfo connectivity check for enhanced detection */ export async function analyzeNetworkError(error: unknown): Promise { // First, check actual device connectivity using NetInfo const isConnected = await isDeviceConnected(); if (!isConnected) { return { isNetworkError: true, errorType: 'offline', userMessage: 'No internet connection detected. Please check your connection and try again.', technicalMessage: 'NetInfo: Device is offline', }; } // Fall back to pattern matching return analyzeErrorPatterns(error); } /** * Internal function to analyze error patterns */ function analyzeErrorPatterns(error: unknown): NetworkErrorInfo { const errorMessage = (error as any)?.message?.toLowerCase() || ''; const errorName = (error as any)?.name?.toLowerCase() || ''; // Offline/No internet connection errors if ( errorMessage.includes('not connected to the internet') || errorMessage.includes('no internet connection') || errorMessage.includes('offline') || errorMessage.includes('internet connection appears to be offline') || errorMessage.includes('the internet connection appears to be offline') ) { return { isNetworkError: true, errorType: 'offline', userMessage: 'No internet connection detected. Please check your connection and try again.', technicalMessage: (error as any).message || 'Device appears to be offline', }; } // Network connection errors if ( errorMessage.includes('network') || errorMessage.includes('fetch') || errorMessage.includes('network request failed') || errorMessage.includes('network error') || errorName.includes('networkerror') || errorMessage.includes('failed to fetch') || errorMessage.includes('request failed') || errorMessage.includes('unable to connect') ) { return { isNetworkError: true, errorType: 'connection', userMessage: 'Unable to connect to server. Please check your internet connection and try again.', technicalMessage: (error as any).message || 'Network connection failed', }; } // Timeout errors if ( errorMessage.includes('timeout') || errorMessage.includes('timed out') || errorName.includes('timeout') ) { return { isNetworkError: true, errorType: 'timeout', userMessage: 'Request timed out. Please check your connection and try again.', technicalMessage: (error as any).message || 'Request timeout', }; } // Connection refused/reset errors if ( errorMessage.includes('connection refused') || errorMessage.includes('connection reset') || errorMessage.includes('econnrefused') || errorMessage.includes('econnreset') ) { return { isNetworkError: true, errorType: 'connection', userMessage: 'Unable to reach server. Please check your internet connection.', technicalMessage: (error as any).message || 'Connection refused', }; } // Check for HTTP status-based network errors if it's a fetch response if ((error as any)?.status || (error instanceof Error && 'status' in error)) { const status = (error as any).status; // Server errors that could be network-related if (status >= 500) { return { isNetworkError: true, errorType: 'server', userMessage: 'Server is temporarily unavailable. Please try again in a moment.', technicalMessage: `Server error: ${status}`, }; } // Gateway timeout, bad gateway, service unavailable if (status === 502 || status === 503 || status === 504) { return { isNetworkError: true, errorType: 'server', userMessage: 'Service is temporarily unavailable. Please try again in a moment.', technicalMessage: `Gateway error: ${status}`, }; } } // DNS resolution errors if ( errorMessage.includes('dns') || errorMessage.includes('getaddrinfo') || errorMessage.includes('enotfound') ) { return { isNetworkError: true, errorType: 'connection', userMessage: 'Unable to reach server. Please check your internet connection.', technicalMessage: (error as any).message || 'DNS resolution failed', }; } // Not a recognized network error return { isNetworkError: false, errorType: 'unknown', userMessage: 'An unexpected error occurred. Please try again.', technicalMessage: (error as any).message || 'Unknown error', }; }