import React, { useEffect } from 'react'; import { Redirect, Route, useHistory, useLocation } from 'react-router-dom'; import { IonApp, IonIcon, IonLabel, IonRouterOutlet, IonTabBar, IonTabButton, IonTabs, } from '@ionic/react'; import { IonReactRouter } from '@ionic/react-router'; import { Capacitor } from '@capacitor/core'; import { PushNotifications } from '@capacitor/push-notifications'; import type { ActionPerformed, PushNotificationSchema, } from '@capacitor/push-notifications'; import { supabase } from './supabase'; import { heart, heartOutline, listOutline, peopleOutline, personOutline, } from 'ionicons/icons'; import '@ionic/react/css/core.css'; import '@ionic/react/css/normalize.css'; import '@ionic/react/css/structure.css'; import '@ionic/react/css/typography.css'; import '@ionic/react/css/padding.css'; import '@ionic/react/css/float-elements.css'; import '@ionic/react/css/text-alignment.css'; import '@ionic/react/css/text-transformation.css'; import '@ionic/react/css/flex-utils.css'; import '@ionic/react/css/display.css'; import '@ionic/react/css/palettes/dark.system.css'; import './theme/variables.css'; import { setStatusBarStyle, Style } from './utils/statusBar'; import { AuthProvider, useAuth } from './contexts/AuthContext'; import ProtectedRoute from './components/ProtectedRoute'; // Auth Pages import AuthPage from './pages/AuthPage'; import VerifyEmailPage from './pages/VerifyEmailPage'; import ForgotPasswordPage from './pages/ForgotPasswordPage'; import VerifyResetPage from './pages/VerifyResetPage'; import SetupProfilePage from './pages/SetupProfilePage'; // Main App Pages import HomePage from './pages/HomePage'; import RecipientsPage from './pages/RecipientsPage'; import RecipientFormPage from './pages/RecipientFormPage'; import RecipientDetailPage from './pages/RecipientDetailPage'; import SupportFlowPage from './pages/SupportFlowPage'; import OrderDetailPage from './pages/OrderDetailPage'; import VoucherDetailPage from './pages/VoucherDetailPage'; import ActivityPage from './pages/ActivityPage'; import NotificationsPage from './pages/NotificationsPage'; import ProfilePage from './pages/ProfilePage'; import EditProfilePage from './pages/EditProfilePage'; const SetupProfileRoute: React.FC = () => { const { user, profileStatus } = useAuth(); return ( { if (!user) return ; if (profileStatus === 'loading') return null; if (profileStatus === 'loaded') return ; return ; }} exact /> ); }; const SendSupportFab: React.FC = () => { const history = useHistory(); const location = useLocation(); const hiddenPaths = [ '/auth', '/verify-email', '/forgot-password', '/verify-reset', '/setup-profile', '/support/new', ]; const showOnPaths = ['/home', '/recipients', '/activity', '/profile']; const isVisibleRoute = showOnPaths.includes(location.pathname) || /^\/recipients\/[0-9a-fA-F-]{36}$/.test(location.pathname); if (!isVisibleRoute || hiddenPaths.includes(location.pathname)) return null; return ( ); }; const PushNotificationHandler: React.FC = () => { const { user, profile } = useAuth(); const history = useHistory(); useEffect(() => { if (!Capacitor.isNativePlatform() || !user || !profile) return; const setupPush = async () => { const handleNotificationTap = (notification: PushNotificationSchema) => { const { orderId, voucherId, screen } = notification.data || {}; if (voucherId) { history.push(`/voucher/${voucherId}`, { parentRoot: '/home' }); } else if (orderId) { history.push(`/orders/${orderId}`, { parentRoot: '/home' }); } else if (screen) { history.push(screen, { parentRoot: '/home' }); } }; // Capacitor automatically triggers pushNotificationActionPerformed // for the notification that launched the app once the listener is attached. // Check if user has explicitly enabled push notifications if (!profile.notification_push_enabled) return; const status = await PushNotifications.checkPermissions(); if (status.receive === 'granted') { await PushNotifications.register(); } else if (status.receive === 'prompt') { const result = await PushNotifications.requestPermissions(); if (result.receive === 'granted') { await PushNotifications.register(); } } PushNotifications.addListener('registration', async (token) => { await supabase .from('profiles') .update({ fcm_token: token.value }) .eq('id', user.id); }); PushNotifications.addListener('registrationError', (err) => { console.error('Push registration error:', err); }); PushNotifications.addListener('pushNotificationReceived', () => { // Handled natively via presentationOptions banner }); PushNotifications.addListener( 'pushNotificationActionPerformed', (action: ActionPerformed) => { handleNotificationTap(action.notification); } ); await PushNotifications.removeAllDeliveredNotifications(); }; setupPush(); return () => { if (Capacitor.isNativePlatform()) { PushNotifications.removeAllListeners(); } }; }, [user, profile, history]); return null; }; const RootRedirect: React.FC = () => { const { user, profileStatus } = useAuth(); if (!user) return ; if (profileStatus === 'loading') return null; if (profileStatus === 'missing') { return ; } return ; }; const App: React.FC = () => { useEffect(() => { setStatusBarStyle(Style.Light); }, []); return ( {/* Auth Routes */} {/* Setup Profile Route - Requires authenticated user but not necessarily complete profile */} {/* Top-level Protected Routes (no tab bar) */} {/* Tab Routes */} ( Care Recipients Activity Profile )} /> {/* Orders Redirect */} } /> {/* Root Redirect */} ); }; export default App;