This commit is contained in:
commit
59bb5a38a3
132 files changed
+19435
No files matched your search
@@ -0,0 +1,88 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import type { Media } from "@/content/site";
|
||||
import { MediaFrame } from "./MediaFrame";
|
||||
|
||||
type Slot = { name: string | null; caption: string | null; poster: Media | null; src: string | null; externalId: string | null };
|
||||
|
||||
const EXTERNAL = {
|
||||
"youtube-nocookie": { label: "YouTube", url: (id: string) => `https://www.youtube-nocookie.com/embed/${encodeURIComponent(id)}?autoplay=1&rel=0` },
|
||||
vimeo: { label: "Vimeo", url: (id: string) => `https://player.vimeo.com/video/${encodeURIComponent(id)}?autoplay=1&dnt=1` },
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Ein Feedback-Video. Geladen wird erst nach Klick (preload="none"); es
|
||||
* startet nie von selbst mit Ton. Externe Player (YouTube/Vimeo) werden nur
|
||||
* geladen, wenn das Datenschutzkonzept sie erlaubt (EXTERNAL_VIDEO_PROVIDER)
|
||||
* und die Besucherin dem Laden ausdruecklich zustimmt.
|
||||
*/
|
||||
export function VideoSlot({ slot, index, provider, showDrafts }: { slot: Slot; index: number; provider: "none" | "youtube-nocookie" | "vimeo"; showDrafts: boolean }) {
|
||||
const [stage, setStage] = useState<"poster" | "consent" | "playing">("poster");
|
||||
const label = slot.name ? `Video von ${slot.name} abspielen` : `Feedback-Video ${index + 1} abspielen`;
|
||||
const external = slot.externalId && provider !== "none" ? EXTERNAL[provider] : null;
|
||||
const playable = Boolean(slot.src) || Boolean(external);
|
||||
const poster: Media = slot.poster ?? { kind: "placeholder", label: `Feedback-Video ${index + 1} folgt`, tone: "studio" };
|
||||
|
||||
return (
|
||||
<figure>
|
||||
<div className="relative aspect-[4/5] overflow-hidden bg-ink">
|
||||
{stage === "playing" && slot.src && (
|
||||
<video src={slot.src} controls autoPlay playsInline preload="none" className="h-full w-full object-cover">
|
||||
<track kind="captions" />
|
||||
</video>
|
||||
)}
|
||||
{stage === "playing" && !slot.src && external && slot.externalId && (
|
||||
<iframe
|
||||
src={external.url(slot.externalId)}
|
||||
title={slot.name ? `Video von ${slot.name}` : `Feedback-Video ${index + 1}`}
|
||||
allow="autoplay; fullscreen; picture-in-picture"
|
||||
allowFullScreen
|
||||
className="h-full w-full"
|
||||
/>
|
||||
)}
|
||||
{stage !== "playing" && (
|
||||
<>
|
||||
<MediaFrame media={poster} aspect="h-full w-full" sizes="(min-width: 768px) 33vw, 100vw" showLabel={showDrafts} />
|
||||
{playable && stage === "poster" && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setStage(external && !slot.src ? "consent" : "playing")}
|
||||
aria-label={label}
|
||||
className="absolute inset-0 m-auto flex h-20 w-20 items-center justify-center rounded-full bg-paper/90 text-ink transition-transform hover:scale-105"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" className="ml-1 h-8 w-8" aria-hidden>
|
||||
<path d="M7 4.5v15l12-7.5z" fill="currentColor" />
|
||||
</svg>
|
||||
</button>
|
||||
)}
|
||||
{stage === "consent" && external && (
|
||||
<div className="absolute inset-0 flex flex-col justify-end gap-3 bg-ink/85 p-5 text-paper">
|
||||
<p className="text-[0.9375rem]">
|
||||
Beim Abspielen wird eine Verbindung zu {external.label} hergestellt und Daten werden dorthin übertragen. Details in der{" "}
|
||||
<a href="/datenschutz" className="underline">Datenschutzerklärung</a>.
|
||||
</p>
|
||||
<div className="flex gap-2">
|
||||
<button type="button" className="btn bg-paper text-ink" onClick={() => setStage("playing")}>
|
||||
Video laden
|
||||
</button>
|
||||
<button type="button" className="btn btn-quiet text-paper" onClick={() => setStage("poster")}>
|
||||
Abbrechen
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<figcaption className="mt-4">
|
||||
{slot.name ? (
|
||||
<span className="block font-semibold">{slot.name}</span>
|
||||
) : (
|
||||
showDrafts && <span className="draft-flag">Name und Freigabe folgen</span>
|
||||
)}
|
||||
{slot.caption && <span className="mt-1 block text-ink/80">{slot.caption}</span>}
|
||||
</figcaption>
|
||||
</figure>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user