Source

App.js

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} />
    </>
  );
}