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.
109 lines
3.2 KiB
TypeScript
109 lines
3.2 KiB
TypeScript
"use client"
|
|
|
|
import { useEffect, useState } from "react"
|
|
import { useRouter, useSearchParams } from "next/navigation"
|
|
import { Loader2 } from "lucide-react"
|
|
import { MeetHeader } from "@/components/meet/meet-header"
|
|
import { MeetRoomFrame } from "@/components/meet/meet-room-frame"
|
|
import { Button } from "@/components/ui/button"
|
|
import { useMeetConfig, useMeetRoomToken } from "@/lib/api/hooks/use-meet-queries"
|
|
import { meetRoomPath, parseMeetUrl } from "@/lib/meet/meet-url"
|
|
|
|
export function MeetJoinClient() {
|
|
const router = useRouter()
|
|
const searchParams = useSearchParams()
|
|
const rawUrl = searchParams.get("u") ?? ""
|
|
const parsed = parseMeetUrl(rawUrl)
|
|
const { data: config, isLoading: configLoading } = useMeetConfig()
|
|
const roomToken = useMeetRoomToken()
|
|
const [embedUrl, setEmbedUrl] = useState<string | null>(null)
|
|
const [error, setError] = useState<string | null>(null)
|
|
|
|
useEffect(() => {
|
|
if (!parsed) {
|
|
setError("Lien de réunion invalide")
|
|
return
|
|
}
|
|
if (parsed.jwt) {
|
|
setEmbedUrl(parsed.embedUrl)
|
|
return
|
|
}
|
|
if (!config?.enabled) return
|
|
|
|
let cancelled = false
|
|
void roomToken
|
|
.mutateAsync(parsed.room)
|
|
.then((token) => {
|
|
if (!cancelled) setEmbedUrl(token.meet_url)
|
|
})
|
|
.catch(() => {
|
|
if (!cancelled) setError("Impossible de rejoindre la réunion")
|
|
})
|
|
|
|
return () => {
|
|
cancelled = true
|
|
}
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps -- join once when URL/config ready
|
|
}, [rawUrl, config?.enabled])
|
|
|
|
if (!rawUrl) {
|
|
return (
|
|
<div className="flex h-dvh flex-col">
|
|
<MeetHeader title="Rejoindre" />
|
|
<div className="flex flex-1 items-center justify-center px-6 text-sm text-muted-foreground">
|
|
Lien de réunion manquant.
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
if (configLoading || (!embedUrl && !error && parsed && !parsed.jwt)) {
|
|
return (
|
|
<div className="flex h-dvh flex-col">
|
|
<MeetHeader title="Connexion…" />
|
|
<div className="flex flex-1 items-center justify-center text-sm text-muted-foreground">
|
|
<Loader2 className="mr-2 size-4 animate-spin" aria-hidden />
|
|
Préparation de la salle…
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
if (error || !config?.enabled) {
|
|
return (
|
|
<div className="flex h-dvh flex-col">
|
|
<MeetHeader />
|
|
<div className="flex flex-1 flex-col items-center justify-center gap-3 px-6 text-center">
|
|
<p className="text-sm text-muted-foreground">
|
|
{error ?? "UltiMeet n'est pas activé sur cette instance."}
|
|
</p>
|
|
<Button variant="outline" className="rounded-full" onClick={() => router.push("/meet")}>
|
|
Retour au lobby
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
if (!embedUrl || !parsed) return null
|
|
|
|
return (
|
|
<div className="flex h-dvh flex-col">
|
|
<MeetHeader
|
|
title={parsed.room}
|
|
trailing={
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
className="hidden rounded-full sm:inline-flex"
|
|
onClick={() => router.push(meetRoomPath(parsed.room))}
|
|
>
|
|
Ouvrir en plein écran
|
|
</Button>
|
|
}
|
|
/>
|
|
<MeetRoomFrame meetUrl={embedUrl} />
|
|
</div>
|
|
)
|
|
}
|