87 lines
3.1 KiB
TypeScript

import React from "react";
import { Link, NavLink, Route, Routes, useLocation } from "react-router-dom";
import Timer from "./components/Timer";
import ShowsPage from "./pages/ShowsPage";
import TimeSyncNotice from "./components/TimeSyncNotice";
import { ToastViewport } from "./components/Toasts";
import DebugOverlay from "./components/DebugOverlay";
import AuthStatus from "./components/AuthStatus";
import "./index.css";
const TIME_SYNC_OFF_THRESHOLD = 100;
export default function App() {
const [open, setOpen] = React.useState(false);
const loc = useLocation();
// close sidebar on route change
React.useEffect(() => setOpen(false), [loc.pathname]);
// ESC to close
React.useEffect(() => {
const onKey = (e: KeyboardEvent) => e.key === "Escape" && setOpen(false);
window.addEventListener("keydown", onKey);
return () => window.removeEventListener("keydown", onKey);
}, []);
return (
<div className={`site ${open ? "sidebar-open" : ""}`}>
{/* Top-left header (outside the card) */}
<header className="site-header">
<button className="burger" aria-label="メニュー" onClick={() => setOpen(v => !v)}>
<svg className="burger-icon" viewBox="0 0 24 24" width="22" height="22" aria-hidden="true">
<path d="M4 7h16M4 12h16M4 17h16"
stroke="currentColor" strokeWidth="2"
strokeLinecap="round" strokeLinejoin="round" fill="none" />
</svg>
</button>
<Link to="/" className="brand">Watch Party</Link>
</header>
{/* Time sync banner (checks every 5 min; shows if |skew| > 500ms) */}
<TimeSyncNotice thresholdMs={TIME_SYNC_OFF_THRESHOLD} lang="ja" />
{/* Sidebar (full-height) */}
<aside className={`sidebar ${open ? "open" : ""}`} aria-hidden={!open}>
<nav className="sidebar-nav" onClick={() => setOpen(false)}>
<NavLink end to="/" className="navlink">Timer</NavLink>
<NavLink to="/shows" className="navlink">Shows</NavLink>
</nav>
<div className="sidebar-auth">
<div className="sidebar-auth-title"></div>
<div className="sidebar-auth-note"></div>
<AuthStatus />
</div>
</aside>
{/* Backdrop for overlap mode or to close on click */}
<button
className={`backdrop ${open ? "open" : ""}`}
aria-hidden={!open}
onClick={() => setOpen(false)}
tabIndex={-1}
/>
{/* Main content area (card inside) */}
<main className="site-content">
<div className="card">
<Routes>
<Route path="/" element={<Timer />} />
<Route path="/shows" element={<ShowsPage />} />
</Routes>
<div className="footer">
Built by{" "}
<a href="https://x.com/nik4nao" target="_blank" rel="noopener noreferrer">
@nik4nao
</a>{" "}
contact for inquiries or requirements.
</div>
</div>
</main>
<ToastViewport />
<DebugOverlay />
</div>
);
}