88 lines
2.0 KiB
TypeScript
88 lines
2.0 KiB
TypeScript
import {
|
|
createContext,
|
|
useContext,
|
|
useEffect,
|
|
type ReactNode,
|
|
} from 'react'
|
|
import { useNavigate, useRouterState } from '@tanstack/react-router'
|
|
import { Center, Loader } from '@pikku/mantine/core'
|
|
import { usePikkuQuery } from '@germantax/functions-sdk/pikku/api.gen'
|
|
|
|
export interface Session {
|
|
userId: string
|
|
email: string
|
|
displayName: string
|
|
}
|
|
|
|
interface SessionContextValue {
|
|
session: Session
|
|
refetch: () => void
|
|
}
|
|
|
|
const SessionContext = createContext<SessionContextValue | null>(null)
|
|
|
|
const PUBLIC_ROUTES = new Set<string>(['/login', '/logout'])
|
|
|
|
const isPublicRoute = (pathname: string) => PUBLIC_ROUTES.has(pathname)
|
|
|
|
export function SessionProvider({ children }: { children: ReactNode }) {
|
|
const navigate = useNavigate()
|
|
const pathname = useRouterState({ select: (s) => s.location.pathname })
|
|
const onPublicRoute = isPublicRoute(pathname)
|
|
|
|
const me = usePikkuQuery('getMe', undefined as never, {
|
|
enabled: !onPublicRoute,
|
|
retry: false,
|
|
staleTime: 60_000,
|
|
})
|
|
|
|
useEffect(() => {
|
|
if (onPublicRoute) return
|
|
if (me.error) {
|
|
navigate({
|
|
to: '/login',
|
|
search: { redirect: pathname } as never,
|
|
replace: true,
|
|
})
|
|
}
|
|
}, [me.error, onPublicRoute, navigate, pathname])
|
|
|
|
if (onPublicRoute) {
|
|
return <>{children}</>
|
|
}
|
|
|
|
if (me.isLoading || me.error || !me.data) {
|
|
return (
|
|
<Center mih="100vh">
|
|
<Loader size="sm" />
|
|
</Center>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<SessionContext.Provider
|
|
value={{ session: me.data, refetch: () => me.refetch() }}
|
|
>
|
|
{children}
|
|
</SessionContext.Provider>
|
|
)
|
|
}
|
|
|
|
export function useSession(): Session {
|
|
const ctx = useContext(SessionContext)
|
|
if (!ctx) {
|
|
throw new Error('useSession must be used inside <SessionProvider>')
|
|
}
|
|
return ctx.session
|
|
}
|
|
|
|
export function useOptionalSession(): Session | null {
|
|
const ctx = useContext(SessionContext)
|
|
return ctx?.session ?? null
|
|
}
|
|
|
|
export function useSessionRefetch(): () => void {
|
|
const ctx = useContext(SessionContext)
|
|
return ctx?.refetch ?? (() => {})
|
|
}
|