147 lines
4.4 KiB
TypeScript
147 lines
4.4 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
import Link from "next/link";
|
|
import { api } from "@/lib/api";
|
|
import { useAuth } from "@/lib/auth";
|
|
import { useI18n } from "@/lib/i18n";
|
|
import type { MeetupSession } from "@/lib/types";
|
|
import RingNext from "@/components/RingNext";
|
|
import { useRingSteps } from "@/lib/rings";
|
|
|
|
export default function MeetupLiveClient({ meetupId, title }: { meetupId: string; title: string }) {
|
|
const { token } = useAuth();
|
|
const { t } = useI18n();
|
|
const rings = useRingSteps();
|
|
const [session, setSession] = useState<MeetupSession | null>(null);
|
|
const [error, setError] = useState(false);
|
|
const [layout, setLayout] = useState<"split" | "video" | "chat">("split");
|
|
|
|
useEffect(() => {
|
|
setError(false);
|
|
setSession(null);
|
|
api
|
|
.getMeetupSession(meetupId, token || undefined)
|
|
.then(setSession)
|
|
.catch(() => {
|
|
setSession(null);
|
|
setError(true);
|
|
});
|
|
}, [meetupId, token]);
|
|
|
|
if (error) {
|
|
return (
|
|
<div className="live-page">
|
|
<div className="container">
|
|
<div className="live-gate reveal">
|
|
<h2>{t.live.unavailable}</h2>
|
|
<p>{t.live.unavailableDesc}</p>
|
|
<Link href="/meetups" className="btn btn-primary">
|
|
{t.live.back}
|
|
</Link>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (!session) {
|
|
return (
|
|
<div className="live-page">
|
|
<div className="container">
|
|
<p className="dest-empty">{t.live.loading}</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (!session.canJoin) {
|
|
const reason = session.lockReason || (session.mode === "offline" ? "offline" : "login_required");
|
|
const copy =
|
|
reason === "vip_required"
|
|
? t.live.vipRequired
|
|
: reason === "host_required"
|
|
? t.live.hostRequired
|
|
: reason === "offline"
|
|
? t.live.offlineOnly
|
|
: t.live.loginRequired;
|
|
const href =
|
|
reason === "vip_required"
|
|
? "/join"
|
|
: reason === "offline"
|
|
? "/meetups"
|
|
: `/login?next=/meetups/${meetupId}/live`;
|
|
const label =
|
|
reason === "vip_required" ? t.join.payBtn : reason === "offline" ? t.live.back : t.nav.login;
|
|
|
|
return (
|
|
<div className="live-page">
|
|
<div className="container">
|
|
<div className="live-gate reveal">
|
|
<h2>{title}</h2>
|
|
<p>{copy}</p>
|
|
<div className="dating-empty-actions">
|
|
<Link href={href} className="btn btn-primary">
|
|
{label}
|
|
</Link>
|
|
<Link href="/meetups" className="btn">
|
|
{t.live.back}
|
|
</Link>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="live-page">
|
|
<div className="live-toolbar">
|
|
<Link href="/meetups">← {t.live.back}</Link>
|
|
<strong>{title}</strong>
|
|
<div className="live-layout-btns">
|
|
{(["split", "video", "chat"] as const).map((m) => (
|
|
<button
|
|
key={m}
|
|
type="button"
|
|
className={`filter-btn${layout === m ? " active" : ""}`}
|
|
onClick={() => setLayout(m)}
|
|
>
|
|
{m === "split" ? t.live.layoutSplit : m === "video" ? t.live.layoutVideo : t.live.layoutChat}
|
|
</button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
<div className={`live-stage layout-${layout}`}>
|
|
{(layout === "split" || layout === "video") && session.videoUrl && (
|
|
<iframe
|
|
title="video"
|
|
src={session.videoUrl}
|
|
className="live-iframe live-video"
|
|
allow="camera; microphone; display-capture"
|
|
/>
|
|
)}
|
|
{(layout === "split" || layout === "chat") && session.chatUrl && (
|
|
<iframe title="chat" src={session.chatUrl} className="live-iframe live-chat" />
|
|
)}
|
|
{!session.videoUrl && !session.chatUrl && (
|
|
<div className="notif-empty">
|
|
<p className="dest-empty">{t.live.linksMissing}</p>
|
|
<div className="dating-empty-actions">
|
|
<Link href="/meetups" className="btn btn-primary">
|
|
{t.live.back}
|
|
</Link>
|
|
<Link href="/feedback" className="btn btn-ghost">
|
|
{t.nav.feedback}
|
|
</Link>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
<div className="container live-exit">
|
|
<RingNext steps={rings.afterMeetups} />
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|