Some checks are pending
E2E / Playwright e2e (push) Waiting to run
- Introduced turbopack alias for canvas in next.config.mjs. - Updated package.json scripts for development and branding tasks. - Added new dependencies for Tiptap extensions. - Implemented new demo layouts for agenda, contacts, drive, and mail applications. - Enhanced globals.css for improved theming and splash screen animations. - Added OAuth callback handling for drive mounts. - Updated layout components to integrate new demo shells and improve structure.
96 lines
3.1 KiB
TypeScript
96 lines
3.1 KiB
TypeScript
"use client"
|
|
|
|
import { useCallback, useEffect } from "react"
|
|
import { usePathname } from "next/navigation"
|
|
import { Icon } from "@iconify/react"
|
|
import {
|
|
AlertDialog,
|
|
AlertDialogAction,
|
|
AlertDialogContent,
|
|
AlertDialogDescription,
|
|
AlertDialogFooter,
|
|
AlertDialogHeader,
|
|
AlertDialogTitle,
|
|
} from "@/components/ui/alert-dialog"
|
|
import { Button } from "@/components/ui/button"
|
|
import { useSessionGuardStore } from "@/lib/auth/session-guard-store"
|
|
import { tryRefreshSession } from "@/lib/auth/session-sync"
|
|
import { isAuthPublicPath } from "@/lib/auth/public-paths"
|
|
import { cn } from "@/lib/utils"
|
|
|
|
function isPublicPath(pathname: string) {
|
|
return isAuthPublicPath(pathname)
|
|
}
|
|
|
|
export function SessionGuard() {
|
|
const pathname = usePathname()
|
|
const status = useSessionGuardStore((s) => s.status)
|
|
|
|
const returnTo = pathname.startsWith("/") ? pathname : "/mail/inbox"
|
|
const loginHref = `/api/auth/login?returnTo=${encodeURIComponent(returnTo)}`
|
|
|
|
const retrySession = useCallback(async () => {
|
|
if (typeof navigator !== "undefined" && !navigator.onLine) return
|
|
await tryRefreshSession()
|
|
}, [])
|
|
|
|
useEffect(() => {
|
|
if (status !== "offline") return
|
|
|
|
const onOnline = () => {
|
|
void retrySession()
|
|
}
|
|
|
|
window.addEventListener("online", onOnline)
|
|
return () => window.removeEventListener("online", onOnline)
|
|
}, [status, retrySession])
|
|
|
|
if (isPublicPath(pathname)) return null
|
|
|
|
return (
|
|
<>
|
|
<div
|
|
className={cn(
|
|
"pointer-events-none fixed inset-x-0 top-0 z-[60] overflow-hidden transition-all duration-300",
|
|
status === "offline" ? "max-h-10 opacity-100" : "max-h-0 opacity-0"
|
|
)}
|
|
aria-live="polite"
|
|
>
|
|
<div className="pointer-events-auto flex h-10 items-center justify-center gap-2 bg-amber-50 px-4 text-xs font-medium text-amber-900 dark:bg-amber-950/60 dark:text-amber-100">
|
|
<Icon icon="mdi:wifi-off" className="size-3.5 shrink-0" />
|
|
<span>Pas de connexion internet — session non vérifiable pour le moment.</span>
|
|
<Button
|
|
type="button"
|
|
variant="outline"
|
|
size="sm"
|
|
className="h-7 border-amber-300 bg-transparent text-xs dark:border-amber-800"
|
|
onClick={() => void retrySession()}
|
|
>
|
|
Réessayer
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
<AlertDialog open={status === "expired"}>
|
|
<AlertDialogContent
|
|
onEscapeKeyDown={(event) => event.preventDefault()}
|
|
onPointerDownOutside={(event) => event.preventDefault()}
|
|
>
|
|
<AlertDialogHeader>
|
|
<AlertDialogTitle>Vous avez été déconnecté</AlertDialogTitle>
|
|
<AlertDialogDescription>
|
|
Votre session a expiré ou n'est plus valide. Reconnectez-vous
|
|
pour continuer à utiliser Ultimail.
|
|
</AlertDialogDescription>
|
|
</AlertDialogHeader>
|
|
<AlertDialogFooter>
|
|
<AlertDialogAction asChild>
|
|
<a href={loginHref}>Se reconnecter</a>
|
|
</AlertDialogAction>
|
|
</AlertDialogFooter>
|
|
</AlertDialogContent>
|
|
</AlertDialog>
|
|
</>
|
|
)
|
|
}
|