feat(web): render auth pages without app shell

This commit is contained in:
2026-04-05 17:09:17 +08:00
parent e8dd85ee65
commit aff645bc5d
+47 -44
View File
@@ -1,4 +1,4 @@
import { useEffect, useState } from "react";
import { useEffect, useState } from "react";
import type { LucideIcon } from "lucide-react";
import {
Bell,
@@ -14,7 +14,7 @@ import {
Sun,
X
} from "lucide-react";
import { Navigate, Route, Routes, useNavigate } from "react-router-dom";
import { Navigate, Route, Routes, useLocation, useNavigate } from "react-router-dom";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
import { EmailLoginPage } from "@/pages/email-login-page";
@@ -66,6 +66,10 @@ function App() {
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false);
const navigate = useNavigate();
const location = useLocation();
const isAuthPage =
location.pathname === "/login/email" || location.pathname.startsWith("/auth/callback/");
useEffect(() => {
applyThemeMode(themeMode);
@@ -95,6 +99,19 @@ function App() {
setThemeMode((currentTheme) => (currentTheme === "dark" ? "light" : "dark"));
}
function handleLoginSuccess(payload: EmailLoginResult): void {
const nextSession = toWebSession(payload);
saveSession(nextSession);
setSession(nextSession);
setMobileSidebarOpen(false);
navigate("/", { replace: true });
}
function handleBootstrapSession(nextSession: WebSession): void {
setSession(nextSession);
setMobileSidebarOpen(false);
}
function renderSidebarContent(options: { collapsed: boolean; mobile: boolean }) {
const { collapsed, mobile } = options;
@@ -124,9 +141,8 @@ function App() {
key={item.key}
type="button"
className={cn(
"group flex w-full items-center rounded-xl border border-transparent text-left transition-colors",
"hover:border-primary/25 hover:bg-primary/10",
"gap-3 px-3 py-2.5"
"group flex w-full items-center rounded-xl border border-transparent px-3 py-2.5 text-left transition-colors",
"gap-3 hover:border-primary/25 hover:bg-primary/10"
)}
>
<ItemIcon className="size-5 shrink-0 text-primary" />
@@ -150,10 +166,7 @@ function App() {
<Button
type="button"
variant="outline"
className={cn(
"w-full border-primary/25 text-primary hover:bg-primary/10",
"justify-start gap-2 px-3"
)}
className="w-full justify-start gap-2 border-primary/25 px-3 text-primary hover:bg-primary/10"
onClick={handleToggleTheme}
>
{themeMode === "dark" ? <Sun className="size-4" /> : <Moon className="size-4" />}
@@ -167,10 +180,7 @@ function App() {
<Button
type="button"
variant="outline"
className={cn(
"w-full border-primary/25 text-primary hover:bg-primary/10",
"justify-start gap-2 px-3"
)}
className="w-full justify-start gap-2 border-primary/25 px-3 text-primary hover:bg-primary/10"
onClick={handleLogout}
disabled={!session || loggingOut}
>
@@ -184,6 +194,28 @@ function App() {
);
}
if (isAuthPage) {
return (
<div className="min-h-dvh bg-background text-foreground md:min-h-screen">
<main className="flex min-h-dvh items-center justify-center px-4 py-8 md:min-h-screen md:px-6">
<div className="w-full max-w-md">
<Routes>
<Route
path="/login/email"
element={<EmailLoginPage onLoginSuccess={handleLoginSuccess} />}
/>
<Route
path="/auth/callback/:provider"
element={<OAuthCallbackPage onBootstrapSession={handleBootstrapSession} />}
/>
<Route path="*" element={<Navigate to={session ? "/" : "/login/email"} replace />} />
</Routes>
</div>
</main>
</div>
);
}
return (
<div className="h-dvh overflow-hidden bg-background text-foreground md:h-screen">
<header className="relative z-50 shrink-0 border-b border-border/70 bg-background/80 backdrop-blur-xl">
@@ -202,11 +234,7 @@ function App() {
alt="TodoList"
className="h-9 w-9 shrink-0 rounded-xl shadow-sm"
/>
<div className="flex flex-col">
<span className="text-base font-semibold tracking-tight text-foreground">
TodoList
</span>
</div>
<span className="text-base font-semibold tracking-tight text-foreground">TodoList</span>
</div>
<span className="hidden max-w-[280px] truncate text-sm text-muted-foreground md:block">
{session ? session.user.email : "未登录"}
@@ -232,7 +260,7 @@ function App() {
{renderSidebarContent({ collapsed: false, mobile: true })}
</aside>
<div className="flex min-h-0 h-[calc(100dvh-4rem)] md:h-[calc(100vh-4rem)]">
<div className="flex h-[calc(100dvh-4rem)] min-h-0 md:h-[calc(100vh-4rem)]">
<aside
className={cn(
"relative hidden h-full border-r border-border/80 bg-card/88 backdrop-blur-xl transition-[width] duration-300 md:flex md:flex-col",
@@ -264,31 +292,6 @@ function App() {
<main className="min-h-0 flex-1 overflow-y-auto px-4 py-6 md:px-6 md:py-8">
<div className="mx-auto w-full max-w-6xl">
<Routes>
<Route
path="/login/email"
element={
<EmailLoginPage
onLoginSuccess={(payload) => {
const nextSession = toWebSession(payload);
saveSession(nextSession);
setSession(nextSession);
setMobileSidebarOpen(false);
navigate("/");
}}
/>
}
/>
<Route
path="/auth/callback/:provider"
element={
<OAuthCallbackPage
onBootstrapSession={(nextSession) => {
setSession(nextSession);
setMobileSidebarOpen(false);
}}
/>
}
/>
<Route
path="/"
element={