fix: request call permissions and simplify CTA

This commit is contained in:
2026-09-17 00:34:45 +02:00
parent 69f2653563
commit ba2147c717
2 changed files with 19 additions and 9 deletions
+1 -4
View File
@@ -1,5 +1,4 @@
import { Button } from "@/components/Button"; import { Button } from "@/components/Button";
import { OrganicBlob } from "@/components/OrganicBlob";
import { Container } from "@/components/Section"; import { Container } from "@/components/Section";
type CTAProps = { type CTAProps = {
@@ -20,9 +19,7 @@ export function CTA({
secondaryLabel = "Kontakt aufnehmen", secondaryLabel = "Kontakt aufnehmen",
}: CTAProps) { }: CTAProps) {
return ( return (
<section className="relative overflow-hidden bg-anouma-plum py-20 text-anouma-cream-light sm:py-24"> <section className="border-y border-anouma-cream-light/15 bg-anouma-mauve-dark py-16 text-anouma-cream-light sm:py-20">
<OrganicBlob tone="mauve" className="-left-20 -top-20 h-80 w-80 opacity-30" />
<OrganicBlob tone="rose" className="-bottom-24 -right-16 h-72 w-72 opacity-20" />
<Container className="relative text-center"> <Container className="relative text-center">
<h2 className="mx-auto max-w-2xl text-balance font-serif text-4xl font-medium leading-tight sm:text-5xl"> <h2 className="mx-auto max-w-2xl text-balance font-serif text-4xl font-medium leading-tight sm:text-5xl">
{title} {title}
+18 -5
View File
@@ -1,6 +1,6 @@
"use client"; "use client";
import { useEffect, useRef, useState } from "react"; import { useCallback, useEffect, useRef, useState } from "react";
import { createLocalTracks, Track, type LocalTrack } from "livekit-client"; import { createLocalTracks, Track, type LocalTrack } from "livekit-client";
export function PreJoinScreen({ onContinue }: { onContinue: (tracks: LocalTrack[]) => void }) { export function PreJoinScreen({ onContinue }: { onContinue: (tracks: LocalTrack[]) => void }) {
@@ -9,8 +9,10 @@ export function PreJoinScreen({ onContinue }: { onContinue: (tracks: LocalTrack[
const [micOn, setMicOn] = useState(true); const [micOn, setMicOn] = useState(true);
const [cameraOn, setCameraOn] = useState(true); const [cameraOn, setCameraOn] = useState(true);
const [speakerTested, setSpeakerTested] = useState(false); const [speakerTested, setSpeakerTested] = useState(false);
const [requestingDevices, setRequestingDevices] = useState(false);
const videoRef = useRef<HTMLVideoElement>(null); const videoRef = useRef<HTMLVideoElement>(null);
const handedOffRef = useRef(false); const handedOffRef = useRef(false);
const initialDeviceRequestRef = useRef(false);
const videoTrack = tracks.find((track) => track.kind === Track.Kind.Video); const videoTrack = tracks.find((track) => track.kind === Track.Kind.Video);
const audioTrack = tracks.find((track) => track.kind === Track.Kind.Audio); const audioTrack = tracks.find((track) => track.kind === Track.Kind.Audio);
@@ -30,9 +32,11 @@ export function PreJoinScreen({ onContinue }: { onContinue: (tracks: LocalTrack[
}; };
}, [tracks]); }, [tracks]);
async function requestDevices() { const requestDevices = useCallback(async () => {
if (requestingDevices) return;
tracks.forEach((track) => track.stop()); tracks.forEach((track) => track.stop());
setError(null); setError(null);
setRequestingDevices(true);
try { try {
const nextTracks = await createLocalTracks({ audio: true, video: true }); const nextTracks = await createLocalTracks({ audio: true, video: true });
setTracks(nextTracks); setTracks(nextTracks);
@@ -40,8 +44,16 @@ export function PreJoinScreen({ onContinue }: { onContinue: (tracks: LocalTrack[
setCameraOn(true); setCameraOn(true);
} catch { } catch {
setError("Kamera oder Mikrofon konnten nicht freigegeben werden. Du kannst trotzdem ohne sie beitreten."); setError("Kamera oder Mikrofon konnten nicht freigegeben werden. Du kannst trotzdem ohne sie beitreten.");
} finally {
setRequestingDevices(false);
} }
} }, [requestingDevices, tracks]);
useEffect(() => {
if (initialDeviceRequestRef.current) return;
initialDeviceRequestRef.current = true;
void requestDevices();
}, [requestDevices]);
async function toggleMic() { async function toggleMic() {
if (!audioTrack) return; if (!audioTrack) return;
@@ -104,9 +116,10 @@ export function PreJoinScreen({ onContinue }: { onContinue: (tracks: LocalTrack[
<button <button
type="button" type="button"
onClick={requestDevices} onClick={requestDevices}
className="rounded-full bg-white px-5 py-3 text-sm font-medium text-neutral-900 hover:bg-white/85" disabled={requestingDevices}
className="rounded-full bg-white px-5 py-3 text-sm font-medium text-neutral-900 hover:bg-white/85 disabled:cursor-wait disabled:opacity-75"
> >
{tracks.length > 0 ? "Geräte erneut prüfen" : "Kamera & Mikrofon aktivieren"} {requestingDevices ? "Berechtigung wird angefragt …" : tracks.length > 0 ? "Geräte erneut prüfen" : "Kamera & Mikrofon aktivieren"}
</button> </button>
<div className="grid grid-cols-2 gap-3"> <div className="grid grid-cols-2 gap-3">
<button type="button" onClick={() => void toggleMic()} disabled={!audioTrack} className="rounded-2xl bg-white/10 px-4 py-3 text-xs hover:bg-white/15 disabled:opacity-40"> <button type="button" onClick={() => void toggleMic()} disabled={!audioTrack} className="rounded-2xl bg-white/10 px-4 py-3 text-xs hover:bg-white/15 disabled:opacity-40">