import React, { JSX, useEffect, useState } from 'react'; import { SafeAreaView, StyleSheet, Platform, StatusBar, AppState, View, Text, ActivityIndicator, Animated } from 'react-native'; import { WebView } from 'react-native-webview'; // Utility imports import { createNotificationChannel, configureNotificationSettings, testNotification } from './utilities/notificationUtils'; import { createDeepLinkHandler, NotificationData } from './utilities/deepLinkUtils'; import { createWebViewHandler } from './utilities/webViewUtils'; import { createCookieHandler } from './utilities/cookieUtils'; import { createFCMHandler, NotificationCallbacks } from './utilities/fcmUtils'; import { ALLOWED_DOMAIN } from './utilities/constants'; import { CustomLoader, createOdooLoader, createNavigationLoader, createChatLoader } from './utilities/loaderUtils'; // Suppress Firebase deprecation warnings (temporary until Firebase v22+ is stable) const originalWarn = console.warn; console.warn = (...args) => { if (args[0] && typeof args[0] === 'string' && args[0].includes('This method is deprecated') && args[0].includes('Firebase')) { return; // Suppress only Firebase deprecation warnings } originalWarn.apply(console, args); }; // @ts-ignore import PushNotification from 'react-native-push-notification'; /** * Main App Component * Handles FCM notifications, WebView management, and deep linking */ export default function App(): JSX.Element { // Track app state to avoid duplicate notifications const [appState, setAppState] = React.useState(AppState.currentState); // WebView reference for accessing cookies const webViewRef = React.useRef(null); // Deep linking state const [currentChannel, setCurrentChannel] = React.useState(null); const webViewUrlRef = React.useRef(`${ALLOWED_DOMAIN}/web`); const [webViewKey, setWebViewKey] = React.useState(0); // Force re-render when URL changes const [isNavigating, setIsNavigating] = React.useState(false); const [navigationAttempts, setNavigationAttempts] = React.useState(0); // State to store cookies const [webViewCookies, setWebViewCookies] = React.useState(null); // Loading state const [isLoading, setIsLoading] = useState(true); const [loadingText, setLoadingText] = useState('Connecting to T4B...'); const [loaderType, setLoaderType] = useState<'odoo' | 'navigation' | 'chat'>('odoo'); // Helper function to update WebView URL immediately (without loader) const updateWebViewUrl = (newUrl: string) => { console.log('๐Ÿ”— Updating WebView URL immediately:', newUrl); // Don't show loader for navigation to avoid flickering webViewUrlRef.current = newUrl; setWebViewKey(prev => prev + 1); // Force WebView re-render console.log('โœ… WebView URL updated:', newUrl); }; // Create utility handlers const cookieHandler = createCookieHandler(setWebViewCookies); const webViewHandler = createWebViewHandler(webViewRef, updateWebViewUrl, { currentChannel, isNavigating }); // Navigation state for deep link handler const navigationState = { currentChannel, isNavigating, navigationAttempts }; const deepLinkHandler = createDeepLinkHandler( updateWebViewUrl, setIsNavigating, setCurrentChannel, setNavigationAttempts, navigationState ); // FCM notification callbacks const fcmCallbacks: NotificationCallbacks = { onForegroundNotification: (data: NotificationData) => { deepLinkHandler.handleForegroundBackgroundDeepLink(data); }, onBackgroundNotification: (data: NotificationData) => { deepLinkHandler.handleForegroundBackgroundDeepLink(data); }, onKilledAppNotification: (data: NotificationData) => { deepLinkHandler.handleKilledAppDeepLink(data); } }; const fcmHandler = createFCMHandler( () => cookieHandler.getWebViewCookies(), fcmCallbacks ); // Handle app state changes useEffect(() => { const handleAppStateChange = (nextAppState: any) => { console.log('App state changed:', appState, '->', nextAppState); setAppState(nextAppState); }; const subscription = AppState.addEventListener('change', handleAppStateChange); return () => subscription?.remove(); }, [appState]); // Initialize FCM and WebView useEffect(() => { // Configure push notifications createNotificationChannel(); configureNotificationSettings(); // Initialize FCM notifications fcmHandler.initializeNotifications(); // Start periodic cookie refresh const cleanupCookieRefresh = cookieHandler.refreshWebViewCookies(); // Setup FCM message listeners const cleanupFCMListeners = fcmHandler.setupMessageListeners(appState); // Setup test functions for debugging setupTestFunctions(); return () => { cleanupCookieRefresh(); cleanupFCMListeners(); }; }, []); // Setup test functions for debugging const setupTestFunctions = () => { if (typeof global !== 'undefined') { (global as any).testNotification = testNotification; (global as any).refreshCookies = () => { console.log('๐Ÿ”„ Manual cookie refresh triggered'); cookieHandler.getWebViewCookies(); }; (global as any).checkCookies = () => { console.log('๐Ÿ” Current cookie state:', webViewCookies); console.log('๐Ÿ” WebView ref available:', !!webViewRef.current); cookieHandler.getWebViewCookies(); }; (global as any).testDeepLink = (channelId: string) => { console.log('๐Ÿ”— Testing deep link for channel:', channelId); deepLinkHandler.navigateToChannel(channelId); }; (global as any).testDeepLinkUrl = (deepLink: string) => { console.log('๐Ÿ”— Testing deep link URL:', deepLink); deepLinkHandler.handleDeepLink(deepLink); }; (global as any).simulateNotification = (channelId: string) => { console.log('๐Ÿ”— Simulating notification tap for channel:', channelId); setTimeout(() => { deepLinkHandler.handleForegroundBackgroundDeepLink({ channel_id: channelId }); }, 100); }; (global as any).testWebViewUrlUpdate = (channelId: string) => { console.log('๐Ÿ”— Testing WebView URL update for channel:', channelId); deepLinkHandler.handleForegroundBackgroundDeepLink({ channel_id: channelId }); }; (global as any).debugWebView = () => { webViewHandler.injectDebugScript(); }; (global as any).navigateToChannel = (channelId: string) => { console.log('๐Ÿ”— Direct navigation to channel:', channelId); deepLinkHandler.navigateToChannel(channelId); }; (global as any).updateWebViewUrl = (url: string) => { console.log('๐Ÿ”— Direct WebView URL update:', url); updateWebViewUrl(url); }; console.log('๐Ÿงช Test functions available:'); console.log(' - global.testNotification()'); console.log(' - global.refreshCookies()'); console.log(' - global.checkCookies()'); console.log(' - global.testDeepLink(channelId) - For killed app deep linking'); console.log(' - global.testWebViewUrlUpdate(channelId) - For foreground/background URL update'); console.log(' - global.testDeepLinkUrl("myapp://chat/123")'); console.log(' - global.simulateNotification(channelId)'); console.log(' - global.debugWebView() - Debug WebView state and elements'); console.log(' - global.navigateToChannel(channelId) - Direct navigation using WebView URL update'); console.log(' - global.updateWebViewUrl(url) - Immediate WebView URL update'); } }; // Handle WebView navigation const handleNavigation = (request: any): boolean => { return webViewHandler.handleNavigation(request); }; // Handle WebView messages const handleWebViewMessage = (event: any) => { webViewHandler.handleWebViewMessage(event, setWebViewCookies); }; return ( { console.log('๐Ÿ”„ WebView started loading'); // Don't show loader during WebView navigation to avoid flickering }} onLoadEnd={() => { console.log('โœ… WebView finished loading'); setIsLoading(false); // Only hide initial loader webViewHandler.handleWebViewLoadEnd(currentChannel, isNavigating); }} onError={(error) => { console.error('โŒ WebView error:', error); setIsLoading(false); setLoadingText('Connection error'); }} //@ts-ignore onConsoleMessage={(event: any) => { console.log('๐ŸŒ WebView Console:', event.nativeEvent.message); }} /> {/* Dynamic Beautiful Custom Loader */} {loaderType === 'odoo' && createOdooLoader(isLoading, loadingText)} {loaderType === 'navigation' && createNavigationLoader(isLoading, loadingText)} {loaderType === 'chat' && createChatLoader(isLoading, loadingText)} ); } const styles = StyleSheet.create({ container: { flex: 1, // paddingTop: Platform.OS === 'android' ? StatusBar.currentHeight ?? 0 : 0, }, webview: { flex: 1, }, });