mirror of
https://github.com/get-drexa/drive.git
synced 2025-12-06 00:01:40 +00:00
43
packages/web/src/routes/__root.tsx
Normal file
43
packages/web/src/routes/__root.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import "@/styles/globals.css"
|
||||
import { ConvexProviderWithAuthKit } from "@convex-dev/workos"
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
|
||||
import { createRootRoute, Outlet } from "@tanstack/react-router"
|
||||
import { AuthKitProvider, useAuth } from "@workos-inc/authkit-react"
|
||||
import { ConvexReactClient } from "convex/react"
|
||||
import { toast } from "sonner"
|
||||
import { formatError } from "@/lib/error"
|
||||
|
||||
export const Route = createRootRoute({
|
||||
component: RootLayout,
|
||||
})
|
||||
|
||||
const convexClient = new ConvexReactClient(process.env.BUN_PUBLIC_CONVEX_URL!)
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
mutations: {
|
||||
onError: (error) => {
|
||||
console.log(error)
|
||||
toast.error(formatError(error))
|
||||
},
|
||||
throwOnError: false,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
function RootLayout() {
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<AuthKitProvider
|
||||
clientId={process.env.BUN_PUBLIC_WORKOS_CLIENT_ID!}
|
||||
redirectUri={process.env.BUN_PUBLIC_WORKOS_REDIRECT_URI!}
|
||||
>
|
||||
<ConvexProviderWithAuthKit
|
||||
client={convexClient}
|
||||
useAuth={useAuth}
|
||||
>
|
||||
<Outlet />
|
||||
</ConvexProviderWithAuthKit>
|
||||
</AuthKitProvider>
|
||||
</QueryClientProvider>
|
||||
)
|
||||
}
|
||||
65
packages/web/src/routes/_authenticated.tsx
Normal file
65
packages/web/src/routes/_authenticated.tsx
Normal file
@@ -0,0 +1,65 @@
|
||||
import {
|
||||
createFileRoute,
|
||||
Navigate,
|
||||
Outlet,
|
||||
useLocation,
|
||||
} from "@tanstack/react-router"
|
||||
import { useAuth } from "@workos-inc/authkit-react"
|
||||
import {
|
||||
Authenticated,
|
||||
AuthLoading,
|
||||
Unauthenticated,
|
||||
useConvexAuth,
|
||||
} from "convex/react"
|
||||
import { useEffect, useState } from "react"
|
||||
import { LoadingSpinner } from "@/components/ui/loading-spinner"
|
||||
|
||||
export const Route = createFileRoute("/_authenticated")({
|
||||
component: AuthenticatedLayout,
|
||||
})
|
||||
|
||||
function AuthenticatedLayout() {
|
||||
const { search } = useLocation()
|
||||
const { isLoading } = useConvexAuth()
|
||||
const { isLoading: authKitLoading } = useAuth()
|
||||
const [hasProcessedAuth, setHasProcessedAuth] = useState(false)
|
||||
|
||||
// Check if we're in the middle of processing an auth code
|
||||
const hasAuthCode = search && typeof search === "object" && "code" in search
|
||||
|
||||
// Track when auth processing is complete
|
||||
useEffect(() => {
|
||||
if (!authKitLoading && !isLoading) {
|
||||
// Delay to ensure auth state is fully synchronized
|
||||
const timer = setTimeout(() => {
|
||||
setHasProcessedAuth(true)
|
||||
}, 500)
|
||||
return () => clearTimeout(timer)
|
||||
}
|
||||
}, [authKitLoading, isLoading])
|
||||
|
||||
// Show loading during auth code processing or while auth state is syncing
|
||||
if (hasAuthCode || authKitLoading || isLoading || !hasProcessedAuth) {
|
||||
return (
|
||||
<div className="flex h-screen w-full items-center justify-center">
|
||||
<LoadingSpinner className="size-10" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Authenticated>
|
||||
<Outlet />
|
||||
</Authenticated>
|
||||
<Unauthenticated>
|
||||
<Navigate replace to="/login" />
|
||||
</Unauthenticated>
|
||||
<AuthLoading>
|
||||
<div className="flex h-screen w-full items-center justify-center">
|
||||
<LoadingSpinner className="size-10" />
|
||||
</div>
|
||||
</AuthLoading>
|
||||
</>
|
||||
)
|
||||
}
|
||||
22
packages/web/src/routes/_authenticated/_sidebar-layout.tsx
Normal file
22
packages/web/src/routes/_authenticated/_sidebar-layout.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import { createFileRoute, Outlet } from "@tanstack/react-router"
|
||||
import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar"
|
||||
import { Toaster } from "@/components/ui/sonner"
|
||||
import { DashboardSidebar } from "@/dashboard/dashboard-sidebar"
|
||||
|
||||
export const Route = createFileRoute("/_authenticated/_sidebar-layout")({
|
||||
component: RouteComponent,
|
||||
})
|
||||
|
||||
function RouteComponent() {
|
||||
return (
|
||||
<SidebarProvider>
|
||||
<div className="flex h-screen w-full">
|
||||
<DashboardSidebar />
|
||||
<SidebarInset>
|
||||
<Outlet />
|
||||
</SidebarInset>
|
||||
</div>
|
||||
<Toaster />
|
||||
</SidebarProvider>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
import { createFileRoute } from "@tanstack/react-router"
|
||||
import { FilesPage } from "@/files/files-page"
|
||||
|
||||
export const Route = createFileRoute("/_authenticated/_sidebar-layout/files")({
|
||||
component: FilesPage,
|
||||
})
|
||||
9
packages/web/src/routes/_authenticated/index.tsx
Normal file
9
packages/web/src/routes/_authenticated/index.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import { createFileRoute, Navigate } from "@tanstack/react-router"
|
||||
|
||||
export const Route = createFileRoute("/_authenticated/")({
|
||||
component: RouteComponent,
|
||||
})
|
||||
|
||||
function RouteComponent() {
|
||||
return <Navigate replace to="/files" />
|
||||
}
|
||||
17
packages/web/src/routes/login.tsx
Normal file
17
packages/web/src/routes/login.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import { createFileRoute } from "@tanstack/react-router"
|
||||
import { useAuth } from "@workos-inc/authkit-react"
|
||||
import { Button } from "../components/ui/button"
|
||||
|
||||
export const Route = createFileRoute("/login")({
|
||||
component: RouteComponent,
|
||||
})
|
||||
|
||||
function RouteComponent() {
|
||||
const { signIn } = useAuth()
|
||||
|
||||
return (
|
||||
<div className="flex h-screen w-full items-center justify-center">
|
||||
<Button onClick={() => signIn()}>Login</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
45
packages/web/src/routes/login_.callback.tsx
Normal file
45
packages/web/src/routes/login_.callback.tsx
Normal file
@@ -0,0 +1,45 @@
|
||||
import { api } from "@convex/_generated/api"
|
||||
import { useMutation } from "@tanstack/react-query"
|
||||
import { createFileRoute, useNavigate } from "@tanstack/react-router"
|
||||
import { useConvexAuth, useMutation as useConvexMutation } from "convex/react"
|
||||
import { useEffect } from "react"
|
||||
import { LoadingSpinner } from "@/components/ui/loading-spinner"
|
||||
|
||||
export const Route = createFileRoute("/login_/callback")({
|
||||
component: RouteComponent,
|
||||
})
|
||||
|
||||
function RouteComponent() {
|
||||
const { isLoading: isLoadingConvexAuth, isAuthenticated } = useConvexAuth()
|
||||
const { mutate: syncUser, isPending: isSyncingUser } = useMutation({
|
||||
mutationFn: useConvexMutation(api.users.syncUser),
|
||||
retry: true,
|
||||
})
|
||||
const navigate = useNavigate()
|
||||
|
||||
useEffect(() => {
|
||||
if (!isLoadingConvexAuth && isAuthenticated && !isSyncingUser) {
|
||||
console.log({ isLoadingConvexAuth, isAuthenticated, isSyncingUser })
|
||||
syncUser(undefined, {
|
||||
onSuccess: () => {
|
||||
navigate({
|
||||
to: "/",
|
||||
replace: true,
|
||||
})
|
||||
},
|
||||
})
|
||||
}
|
||||
}, [
|
||||
isLoadingConvexAuth,
|
||||
isAuthenticated,
|
||||
syncUser,
|
||||
isSyncingUser,
|
||||
navigate,
|
||||
])
|
||||
|
||||
return (
|
||||
<div className="flex h-screen w-full items-center justify-center">
|
||||
<LoadingSpinner className="size-10" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user