import { useState, useRef, useCallback } from "react";
import { useTranslation } from 'react-i18next';
import { BrowserRouter, Routes, Route, NavLink, Navigate, useLocation, useNavigate } from "react-router-dom";
import Dashboard from "./Dashboard";
import Analysis from "./Analysis";
import Statistics from "./Statistics";
import Settings from "./Settings";
import Intro from "./Intro";
import Login from "./Login";
import Register from "./Register";
import { SettingsIcon, HomeIcon, ActivityIcon, BarChart3, Menu, X, LogOut, Moon, Sun } from 'lucide-react';
import { AuthProvider, useAuth } from './context/AuthContext';
import { ThemeProvider, useTheme } from './context/ThemeContext';
import LoadingOverlay from './components/LoadingOverlay';
const NAV_ITEMS = [
{ icon: HomeIcon, label: "Dashboard", path: "/" },
{ icon: ActivityIcon, label: "Analysis", path: "/stats" },
{ icon: BarChart3, label: "Statistics", path: "/charts" },
{ icon: SettingsIcon, label: "Settings", path: "/settings" },
];
function ProtectedRoute({ children }) {
const { user, loading } = useAuth();
const location = useLocation();
if (loading) return
Loading...
;
if (!user) return ;
return children;
}
function MainAppShell() {
const { t } = useTranslation();
const { theme, toggleTheme } = useTheme();
const { user, logout } = useAuth();
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
const navigate = useNavigate();
const location = useLocation();
const touchStartX = useRef(null);
const touchStartY = useRef(null);
const isHorizontalSwipe = useRef(false);
const swipeDir = useRef(0); // -1 = left, +1 = right
const [dragX, setDragX] = useState(0);
const [slideClass, setSlideClass] = useState('');
const [showIntro, setShowIntro] = useState(() => {
return !sessionStorage.getItem('introPlayed');
});
const handleIntroComplete = () => {
sessionStorage.setItem('introPlayed', 'true');
setShowIntro(false);
};
const handleTouchStart = useCallback((e) => {
touchStartX.current = e.touches[0].clientX;
touchStartY.current = e.touches[0].clientY;
isHorizontalSwipe.current = false;
}, []);
const handleTouchMove = useCallback((e) => {
if (touchStartX.current === null) return;
const deltaX = e.touches[0].clientX - touchStartX.current;
const deltaY = e.touches[0].clientY - touchStartY.current;
// Decide swipe axis on first meaningful movement
if (!isHorizontalSwipe.current && (Math.abs(deltaX) > 8 || Math.abs(deltaY) > 8)) {
isHorizontalSwipe.current = Math.abs(deltaX) > Math.abs(deltaY);
}
if (isHorizontalSwipe.current) {
// Prevent vertical scroll while swiping horizontally
e.preventDefault();
const currentIndex = NAV_ITEMS.findIndex(item => item.path === location.pathname);
// Block drag if already at edge
const canGoLeft = currentIndex < NAV_ITEMS.length - 1;
const canGoRight = currentIndex > 0;
if ((deltaX < 0 && !canGoLeft) || (deltaX > 0 && !canGoRight)) {
// Allow small rubber-band at edges
setDragX(deltaX * 0.1);
} else {
setDragX(deltaX * 0.35); // dampened drag feel
}
}
}, [location.pathname]);
const handleTouchEnd = useCallback((e) => {
if (touchStartX.current === null) return;
const deltaX = e.changedTouches[0].clientX - touchStartX.current;
const deltaY = e.changedTouches[0].clientY - touchStartY.current;
if (isHorizontalSwipe.current && Math.abs(deltaX) > 60) {
const currentIndex = NAV_ITEMS.findIndex(item => item.path === location.pathname);
if (deltaX < 0 && currentIndex < NAV_ITEMS.length - 1) {
swipeDir.current = -1;
setSlideClass('slide-in-left');
setDragX(0);
navigate(NAV_ITEMS[currentIndex + 1].path);
} else if (deltaX > 0 && currentIndex > 0) {
swipeDir.current = 1;
setSlideClass('slide-in-right');
setDragX(0);
navigate(NAV_ITEMS[currentIndex - 1].path);
} else {
setDragX(0);
}
} else {
setDragX(0);
}
touchStartX.current = null;
touchStartY.current = null;
isHorizontalSwipe.current = false;
}, [location.pathname, navigate]);
return (
<>
{showIntro && }
{/* SIDEBAR (Desktop Only) */}
{/* MAIN CONTENT WRAPPER */}
{/* MOBILE HEADER */}
setSlideClass('')}
style={{
transform: dragX !== 0 ? `translateX(${dragX}px)` : undefined,
transition: dragX === 0 ? 'transform 0.25s ease' : 'none',
willChange: 'transform',
}}
>
} />
} />
} />
} />
{/* MOBILE BOTTOM NAV — fixed to viewport bottom */}
>
);
}
function AuthLoadingGate({ children }) {
const { authLoading, authLoadingMessage } = useAuth();
return (
<>
{authLoading && }
{children}
>
);
}
export function App() {
return (
} />
} />
} />
);
}
export default App;