import React, { useEffect } from 'react';
import { BrowserRouter, Routes, Route, useLocation, useNavigate } from 'react-router-dom';
import { Toaster } from 'react-hot-toast';
import { FlutterProvider } from './contexts/FlutterContext';
import { AuthProvider } from './contexts/AuthContext';
import { useAuth } from './hooks/useAuth';
import { getOrGenerateFcmToken } from './notification/firebase';
import LoginPage from './components/auth/LoginPage';
import DashboardLayout from './components/dashboard/DashboardLayout';
import DashboardContent from './components/dashboard/DashboardContent';
import WardManager from './components/wards/WardManager';
import MemosPage from './components/memos/MemosPage';
import MemoDetail from './components/memos/MemoDetail';
import Setting from './components/Setting';
const useFlutterBridge = () => {
const location = useLocation();
const navigate = useNavigate();
useEffect(() => {
const params = new URLSearchParams(location.search);
const isFromFlutter = params.get('source') === 'flutter';
if (!isFromFlutter) return;
const handleUserReady = () => {
const raw = localStorage.getItem('memo_user');
if (raw) {
console.log('[FLUTTER] User data injected, reloading...');
window.location.replace('/?source=flutter'); // avoid double reload loop
}
};
window.addEventListener('memo_user_ready', handleUserReady);
handleUserReady(); // run once in case already injected
return () => window.removeEventListener('memo_user_ready', handleUserReady);
}, [location]);
};
const AppRoutes = () => {
const { isAuthenticated } = useAuth();
return (
<Routes>
{!isAuthenticated ? (
<Route path="*" element={<LoginPage />} />
) : (
<Route element={<DashboardLayout />}>
<Route index element={<DashboardContent />} />
<Route path="users" element={<DashboardContent activeTab="users" />} />
<Route path="roles" element={<DashboardContent activeTab="roles" />} />
<Route path="blocks" element={<DashboardContent activeTab="blocks" />} />
<Route path="shifts" element={<DashboardContent activeTab="shifts" />} />
<Route path="hierarchies" element={<DashboardContent activeTab="hierarchies" />} />
<Route path="memos" element={<MemosPage />} />
<Route path="settings" element={<Setting />} />
<Route path="memo/:memoId" element={<MemoDetail />} />
<Route path="wards/:blockId" element={<WardManager />} />
</Route>
)}
</Routes>
);
};
/**
* Root component for the MemoTrack application.
*
* This component sets up the main context providers (AuthProvider, FlutterProvider)
* and the router (BrowserRouter). It then renders the FlutterAwareWrapper
* which handles Flutter bridge interactions and the main application routes.
*
* @returns {JSX.Element} The root MemoTrack application component.
*/
export default function MemoTrackApp() {
return (
<AuthProvider>
<FlutterProvider>
<BrowserRouter>
<FlutterAwareWrapper />
</BrowserRouter>
</FlutterProvider>
</AuthProvider>
);
}
/**
* Wrapper component that integrates the Flutter bridge logic.
*
* This component calls the `useFlutterBridge` hook to handle communication
* and data injection from the Flutter environment. It then renders the
* main application routes (`AppRoutes`) and the `Toaster` component
* for displaying notifications, ensuring they are within the router context.
*
* @returns {JSX.Element} The wrapper component containing routes and toaster.
*/
function FlutterAwareWrapper() {
useFlutterBridge(); // ⬅️ This handles Flutter injection
return (
// The AppRoutes component is wrapped in a fragment along with the Toaster.
// This ensures that both the routing logic and the notification display
// are rendered within the same component tree managed by the BrowserRouter.
<>
<AppRoutes />
<Toaster position="top-right" reverseOrder={false} />
</>
);
}
Source