From dc11213cb5bf3ef235d2244604edde2b327fdc75 Mon Sep 17 00:00:00 2001 From: lila Date: Thu, 30 Apr 2026 19:46:45 +0200 Subject: [PATCH] feat: replace login route with auth modal - Add AuthModal to root layout driven by ?modal=auth search param - Update multiplayer and play beforeLoad redirects to use modal - Update NavAuth and Hero links to use modal - Delete login route and NavLogin component --- apps/web/src/components/auth/AuthModal.tsx | 13 +++--- apps/web/src/components/landing/Hero.tsx | 6 ++- apps/web/src/components/navbar/NavAuth.tsx | 5 ++- apps/web/src/routeTree.gen.ts | 21 ---------- apps/web/src/routes/__root.tsx | 22 ++++++++++- apps/web/src/routes/login.tsx | 46 ---------------------- apps/web/src/routes/multiplayer.tsx | 5 ++- apps/web/src/routes/play.tsx | 2 +- 8 files changed, 41 insertions(+), 79 deletions(-) delete mode 100644 apps/web/src/routes/login.tsx diff --git a/apps/web/src/components/auth/AuthModal.tsx b/apps/web/src/components/auth/AuthModal.tsx index e4d1331..01b2c20 100644 --- a/apps/web/src/components/auth/AuthModal.tsx +++ b/apps/web/src/components/auth/AuthModal.tsx @@ -4,7 +4,7 @@ import { authClient } from "../../lib/auth-client"; type Tab = "login" | "register"; -type AuthModalProps = { onClose: () => void }; +type AuthModalProps = { onClose: () => void; onSuccess: () => void }; type LoginFormProps = { onSuccess: () => void }; @@ -142,11 +142,14 @@ const RegisterForm = ({ onSuccess }: RegisterFormProps) => { ); }; -const SocialButtons = () => { +type SocialButtonsProps = { onSuccess: () => void }; + +const SocialButtons = ({ onSuccess }: SocialButtonsProps) => { const handleSocial = (provider: "google" | "github") => { void authClient.signIn.social( { provider, callbackURL: window.location.origin }, { + onSuccess, onError: (ctx) => { toast.error(ctx.error.message ?? "Something went wrong."); }, @@ -179,7 +182,7 @@ const SocialButtons = () => { ); }; -export const AuthModal = ({ onClose }: AuthModalProps) => { +export const AuthModal = ({ onClose, onSuccess }: AuthModalProps) => { const [tab, setTab] = useState("login"); useEffect(() => { @@ -233,13 +236,13 @@ export const AuthModal = ({ onClose }: AuthModalProps) => { {tab === "login" ? ( - + ) : ( )} {/* Social */} - + ); diff --git a/apps/web/src/components/landing/Hero.tsx b/apps/web/src/components/landing/Hero.tsx index 81f7bba..238d313 100644 --- a/apps/web/src/components/landing/Hero.tsx +++ b/apps/web/src/components/landing/Hero.tsx @@ -66,13 +66,15 @@ const Hero = () => { ) : ( <> Get started Log in diff --git a/apps/web/src/components/navbar/NavAuth.tsx b/apps/web/src/components/navbar/NavAuth.tsx index 22b8479..f65f569 100644 --- a/apps/web/src/components/navbar/NavAuth.tsx +++ b/apps/web/src/components/navbar/NavAuth.tsx @@ -24,13 +24,14 @@ const NavAuth = () => { ) : ( - Sign in + Login )} diff --git a/apps/web/src/routeTree.gen.ts b/apps/web/src/routeTree.gen.ts index 61b893a..a85f2f2 100644 --- a/apps/web/src/routeTree.gen.ts +++ b/apps/web/src/routeTree.gen.ts @@ -12,7 +12,6 @@ import { Route as rootRouteImport } from './routes/__root' import { Route as ResetPasswordRouteImport } from './routes/reset-password' import { Route as PlayRouteImport } from './routes/play' import { Route as MultiplayerRouteImport } from './routes/multiplayer' -import { Route as LoginRouteImport } from './routes/login' import { Route as ForgotPasswordRouteImport } from './routes/forgot-password' import { Route as AboutRouteImport } from './routes/about' import { Route as IndexRouteImport } from './routes/index' @@ -35,11 +34,6 @@ const MultiplayerRoute = MultiplayerRouteImport.update({ path: '/multiplayer', getParentRoute: () => rootRouteImport, } as any) -const LoginRoute = LoginRouteImport.update({ - id: '/login', - path: '/login', - getParentRoute: () => rootRouteImport, -} as any) const ForgotPasswordRoute = ForgotPasswordRouteImport.update({ id: '/forgot-password', path: '/forgot-password', @@ -75,7 +69,6 @@ export interface FileRoutesByFullPath { '/': typeof IndexRoute '/about': typeof AboutRoute '/forgot-password': typeof ForgotPasswordRoute - '/login': typeof LoginRoute '/multiplayer': typeof MultiplayerRouteWithChildren '/play': typeof PlayRoute '/reset-password': typeof ResetPasswordRoute @@ -87,7 +80,6 @@ export interface FileRoutesByTo { '/': typeof IndexRoute '/about': typeof AboutRoute '/forgot-password': typeof ForgotPasswordRoute - '/login': typeof LoginRoute '/play': typeof PlayRoute '/reset-password': typeof ResetPasswordRoute '/multiplayer': typeof MultiplayerIndexRoute @@ -99,7 +91,6 @@ export interface FileRoutesById { '/': typeof IndexRoute '/about': typeof AboutRoute '/forgot-password': typeof ForgotPasswordRoute - '/login': typeof LoginRoute '/multiplayer': typeof MultiplayerRouteWithChildren '/play': typeof PlayRoute '/reset-password': typeof ResetPasswordRoute @@ -113,7 +104,6 @@ export interface FileRouteTypes { | '/' | '/about' | '/forgot-password' - | '/login' | '/multiplayer' | '/play' | '/reset-password' @@ -125,7 +115,6 @@ export interface FileRouteTypes { | '/' | '/about' | '/forgot-password' - | '/login' | '/play' | '/reset-password' | '/multiplayer' @@ -136,7 +125,6 @@ export interface FileRouteTypes { | '/' | '/about' | '/forgot-password' - | '/login' | '/multiplayer' | '/play' | '/reset-password' @@ -149,7 +137,6 @@ export interface RootRouteChildren { IndexRoute: typeof IndexRoute AboutRoute: typeof AboutRoute ForgotPasswordRoute: typeof ForgotPasswordRoute - LoginRoute: typeof LoginRoute MultiplayerRoute: typeof MultiplayerRouteWithChildren PlayRoute: typeof PlayRoute ResetPasswordRoute: typeof ResetPasswordRoute @@ -178,13 +165,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof MultiplayerRouteImport parentRoute: typeof rootRouteImport } - '/login': { - id: '/login' - path: '/login' - fullPath: '/login' - preLoaderRoute: typeof LoginRouteImport - parentRoute: typeof rootRouteImport - } '/forgot-password': { id: '/forgot-password' path: '/forgot-password' @@ -250,7 +230,6 @@ const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, AboutRoute: AboutRoute, ForgotPasswordRoute: ForgotPasswordRoute, - LoginRoute: LoginRoute, MultiplayerRoute: MultiplayerRouteWithChildren, PlayRoute: PlayRoute, ResetPasswordRoute: ResetPasswordRoute, diff --git a/apps/web/src/routes/__root.tsx b/apps/web/src/routes/__root.tsx index 826aec6..7df9998 100644 --- a/apps/web/src/routes/__root.tsx +++ b/apps/web/src/routes/__root.tsx @@ -1,18 +1,38 @@ -import { createRootRoute, Outlet } from "@tanstack/react-router"; +import { + createRootRoute, + Outlet, + useNavigate, + useSearch, +} from "@tanstack/react-router"; import { TanStackRouterDevtools } from "@tanstack/react-router-devtools"; import { Toaster } from "sonner"; import Navbar from "../components/navbar/NavBar"; import NotFound from "../components/NotFound"; import RootError from "../components/RootError"; +import { AuthModal } from "../components/auth/AuthModal"; import { AuthModalSearchSchema } from "@lila/shared"; const RootLayout = () => { + const navigate = useNavigate(); + const { modal, redirect } = useSearch({ from: "__root__" }); + + const handleClose = () => { + void navigate({ to: "/", search: {} }); + }; + + const handleSuccess = () => { + void navigate({ to: (redirect as string) ?? "/", search: {} }); + }; + return ( <>
+ {modal === "auth" && ( + + )} diff --git a/apps/web/src/routes/login.tsx b/apps/web/src/routes/login.tsx deleted file mode 100644 index 8451d41..0000000 --- a/apps/web/src/routes/login.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { createFileRoute, useNavigate } from "@tanstack/react-router"; -import { signIn, useSession } from "../lib/auth-client"; - -const LoginPage = () => { - const { data: session, isPending } = useSession(); - const navigate = useNavigate(); - - if (isPending) return
Loading...
; - - if (session) { - void navigate({ to: "/" }); - return null; - } - - return ( -
-

sign in to lila

- - -
- ); -}; - -export const Route = createFileRoute("/login")({ component: LoginPage }); diff --git a/apps/web/src/routes/multiplayer.tsx b/apps/web/src/routes/multiplayer.tsx index 7adffd6..0008b37 100644 --- a/apps/web/src/routes/multiplayer.tsx +++ b/apps/web/src/routes/multiplayer.tsx @@ -14,7 +14,10 @@ export const Route = createFileRoute("/multiplayer")({ beforeLoad: async () => { const { data: session } = await authClient.getSession(); if (!session) { - throw redirect({ to: "/login" }); + throw redirect({ + to: "/", + search: { modal: "auth", redirect: "/multiplayer" }, + }); } return { session }; }, diff --git a/apps/web/src/routes/play.tsx b/apps/web/src/routes/play.tsx index df4959d..bc4cde3 100644 --- a/apps/web/src/routes/play.tsx +++ b/apps/web/src/routes/play.tsx @@ -132,7 +132,7 @@ export const Route = createFileRoute("/play")({ beforeLoad: async () => { const { data: session } = await authClient.getSession(); if (!session) { - throw redirect({ to: "/login" }); + throw redirect({ to: "/", search: { modal: "auth", redirect: "/play" } }); } }, });