2025-04-04 22:43:03 -04:00
|
|
|
import React, { ReactNode, useEffect, useState, createContext, useMemo, useRef } from 'react';
|
2025-04-04 15:46:31 -04:00
|
|
|
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
|
|
import { createQueryClient } from '../queryClient';
|
|
|
|
import NDK from '@nostr-dev-kit/ndk-mobile';
|
|
|
|
import { initializeNDK } from '@/lib/initNDK';
|
2025-04-04 22:43:03 -04:00
|
|
|
import { createLogger } from '@/lib/utils/logger';
|
|
|
|
import * as SecureStore from 'expo-secure-store';
|
|
|
|
import { SECURE_STORE_KEYS } from './constants';
|
|
|
|
|
|
|
|
// Create auth-specific logger
|
|
|
|
const logger = createLogger('ReactQueryAuthProvider');
|
2025-04-04 15:46:31 -04:00
|
|
|
|
|
|
|
// Create context for NDK instance
|
|
|
|
export const NDKContext = createContext<{ ndk: NDK | null; isInitialized: boolean }>({
|
|
|
|
ndk: null,
|
|
|
|
isInitialized: false,
|
|
|
|
});
|
|
|
|
|
|
|
|
interface ReactQueryAuthProviderProps {
|
|
|
|
children: ReactNode;
|
|
|
|
enableOfflineMode?: boolean;
|
|
|
|
queryClient?: QueryClient;
|
2025-04-04 18:00:20 -04:00
|
|
|
enableNDK?: boolean; // New prop to control NDK initialization
|
2025-04-04 15:46:31 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2025-04-04 22:43:03 -04:00
|
|
|
* ReactQueryAuthProvider - Enhanced with persistence support
|
2025-04-04 15:46:31 -04:00
|
|
|
*
|
|
|
|
* Main provider component for React Query integration with authentication.
|
|
|
|
* This component:
|
|
|
|
* - Creates and configures the QueryClient
|
2025-04-04 22:43:03 -04:00
|
|
|
* - Creates an NDK instance with proper credential restoration
|
2025-04-04 15:46:31 -04:00
|
|
|
* - Provides React Query context and NDK context
|
|
|
|
* - Ensures consistent hook ordering regardless of initialization state
|
|
|
|
*/
|
|
|
|
export function ReactQueryAuthProvider({
|
|
|
|
children,
|
|
|
|
enableOfflineMode = true,
|
|
|
|
queryClient: customQueryClient,
|
2025-04-04 18:00:20 -04:00
|
|
|
enableNDK = true, // Default to true for backward compatibility
|
2025-04-04 15:46:31 -04:00
|
|
|
}: ReactQueryAuthProviderProps) {
|
|
|
|
// Create Query Client if not provided (always created)
|
|
|
|
const queryClient = useMemo(() => customQueryClient ?? createQueryClient(), [customQueryClient]);
|
|
|
|
|
|
|
|
// NDK state - but we ALWAYS render regardless of state
|
|
|
|
const [ndk, setNdk] = useState<NDK | null>(null);
|
|
|
|
const [isInitialized, setIsInitialized] = useState(false);
|
|
|
|
|
2025-04-04 22:43:03 -04:00
|
|
|
// Track initialization attempts
|
|
|
|
const initAttemptRef = useRef(0);
|
|
|
|
|
2025-04-04 15:46:31 -04:00
|
|
|
// NDK context value (memoized to prevent unnecessary re-renders)
|
|
|
|
const ndkContextValue = useMemo(() => ({
|
|
|
|
ndk,
|
|
|
|
isInitialized
|
|
|
|
}), [ndk, isInitialized]);
|
|
|
|
|
2025-04-04 22:43:03 -04:00
|
|
|
// Enhanced initialization with credential checking
|
2025-04-04 15:46:31 -04:00
|
|
|
useEffect(() => {
|
2025-04-04 18:00:20 -04:00
|
|
|
// Skip NDK initialization if enableNDK is false
|
|
|
|
if (!enableNDK) {
|
2025-04-04 22:43:03 -04:00
|
|
|
logger.info("NDK initialization skipped (enableNDK=false)");
|
2025-04-04 18:00:20 -04:00
|
|
|
setIsInitialized(true); // Still mark as initialized so the app can proceed
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2025-04-04 22:43:03 -04:00
|
|
|
// Track this initialization attempt
|
|
|
|
const currentAttempt = ++initAttemptRef.current;
|
|
|
|
|
2025-04-04 15:46:31 -04:00
|
|
|
const initNDK = async () => {
|
|
|
|
try {
|
2025-04-04 22:43:03 -04:00
|
|
|
logger.info(`Initializing NDK (attempt ${currentAttempt})...`);
|
|
|
|
|
|
|
|
// Pre-check for credentials to improve logging - using constants for key names
|
|
|
|
const hasPrivateKey = await SecureStore.getItemAsync(SECURE_STORE_KEYS.PRIVATE_KEY);
|
|
|
|
const hasExternalSigner = await SecureStore.getItemAsync(SECURE_STORE_KEYS.EXTERNAL_SIGNER);
|
|
|
|
|
|
|
|
logger.debug("Auth credentials status:", {
|
|
|
|
hasPrivateKey: !!hasPrivateKey,
|
|
|
|
hasExternalSigner: !!hasExternalSigner
|
|
|
|
});
|
|
|
|
|
|
|
|
// Initialize NDK with context name
|
2025-04-04 18:00:20 -04:00
|
|
|
const result = await initializeNDK('react-query');
|
2025-04-04 22:43:03 -04:00
|
|
|
|
|
|
|
// Update state only if this is still the most recent initialization attempt
|
|
|
|
if (currentAttempt === initAttemptRef.current) {
|
|
|
|
setNdk(result.ndk);
|
|
|
|
setIsInitialized(true);
|
|
|
|
logger.info("NDK initialized successfully");
|
|
|
|
|
|
|
|
// Force refetch auth state to ensure it's up to date
|
|
|
|
queryClient.invalidateQueries({ queryKey: ['auth', 'current'] });
|
|
|
|
}
|
2025-04-04 15:46:31 -04:00
|
|
|
} catch (err) {
|
2025-04-04 22:43:03 -04:00
|
|
|
logger.error("Error initializing NDK:", err);
|
2025-04-04 15:46:31 -04:00
|
|
|
// Still mark as initialized so the app can handle the error state
|
2025-04-04 22:43:03 -04:00
|
|
|
if (currentAttempt === initAttemptRef.current) {
|
|
|
|
setIsInitialized(true);
|
|
|
|
}
|
2025-04-04 15:46:31 -04:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
initNDK();
|
2025-04-04 22:43:03 -04:00
|
|
|
}, [enableOfflineMode, enableNDK, queryClient]);
|
2025-04-04 15:46:31 -04:00
|
|
|
|
|
|
|
// Always render children, regardless of NDK initialization status
|
|
|
|
// This ensures consistent hook ordering in child components
|
|
|
|
return (
|
|
|
|
<QueryClientProvider client={queryClient}>
|
|
|
|
<NDKContext.Provider value={ndkContextValue}>
|
|
|
|
{children}
|
|
|
|
</NDKContext.Provider>
|
|
|
|
</QueryClientProvider>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Example usage in app/_layout.tsx:
|
|
|
|
*
|
|
|
|
* ```tsx
|
|
|
|
* import { ReactQueryAuthProvider } from '@/lib/auth/ReactQueryAuthProvider';
|
|
|
|
*
|
|
|
|
* export default function RootLayout() {
|
|
|
|
* return (
|
|
|
|
* <ReactQueryAuthProvider>
|
|
|
|
* <Stack />
|
|
|
|
* </ReactQueryAuthProvider>
|
|
|
|
* );
|
|
|
|
* }
|
|
|
|
* ```
|
|
|
|
*/
|