diff --git a/src/components/landing/index.ts b/src/components/landing/index.ts index d1f9fc1..f3693c7 100644 --- a/src/components/landing/index.ts +++ b/src/components/landing/index.ts @@ -1,7 +1,9 @@ import type { ComponentType } from "react"; import { LinksLanding } from "@/components/landing/links-landing"; +import { SocialLanding } from "@/components/landing/social-landing"; import type { LandingProps } from "@/components/landing/types"; +import { VideoLanding } from "@/components/landing/video-landing"; import type { QrTypeId } from "@/lib/qr/type-module"; /** @@ -14,6 +16,8 @@ import type { QrTypeId } from "@/lib/qr/type-module"; export const qrLandings: Partial>> = { links: LinksLanding, + social: SocialLanding, + video: VideoLanding, }; export type { LandingProps }; diff --git a/src/components/landing/social-landing.tsx b/src/components/landing/social-landing.tsx new file mode 100644 index 0000000..bc9b598 --- /dev/null +++ b/src/components/landing/social-landing.tsx @@ -0,0 +1,52 @@ +import { Icon } from "@/components/icons"; +import { LandingShell } from "@/components/landing/shell"; +import type { LandingProps } from "@/components/landing/types"; +import { + getSocialNetwork, + socialHandleLabel, +} from "@/lib/qr/social-networks"; +import { socialDataSchema } from "@/lib/qr/types/social"; + +export function SocialLanding({ data }: LandingProps) { + const { title, description, profiles } = socialDataSchema.parse(data); + + return ( + + + + ); +} diff --git a/src/components/landing/video-landing.tsx b/src/components/landing/video-landing.tsx new file mode 100644 index 0000000..23b03d3 --- /dev/null +++ b/src/components/landing/video-landing.tsx @@ -0,0 +1,67 @@ +import { Icon } from "@/components/icons"; +import { LandingShell } from "@/components/landing/shell"; +import type { LandingProps } from "@/components/landing/types"; +import { EmbeddedVideo } from "@/components/landing/video-player"; +import { resolveVideoSource, videoDataSchema } from "@/lib/qr/types/video"; + +export function VideoLanding({ data }: LandingProps) { + const { url, title, description } = videoDataSchema.parse(data); + const quelle = resolveVideoSource(url); + + return ( + + Bei {quelle.label} öffnen + + ) : null + } + > + {quelle.kind === "youtube" || quelle.kind === "vimeo" ? ( + + ) : quelle.kind === "file" ? ( + + ) : ( + + + + + + Video ansehen + + {quelle.label} + + + + )} + + ); +} diff --git a/src/components/landing/video-player.tsx b/src/components/landing/video-player.tsx new file mode 100644 index 0000000..5ca1b6a --- /dev/null +++ b/src/components/landing/video-player.tsx @@ -0,0 +1,56 @@ +"use client"; + +import { useState } from "react"; + +import { Icon } from "@/components/icons"; + +/** + * Eingebettetes Video mit Zwei-Klick-Lösung. + * + * Ein Rahmen von YouTube oder Vimeo baut beim Laden eine Verbindung zum + * Anbieter auf — auf einer Seite, die Fremde nach einem Scan zu sehen + * bekommen, ist das ohne Zustimmung nicht in Ordnung. Deshalb erscheint der + * Rahmen erst nach einem Klick, und es steht vorher da, was dabei passiert. + */ +export function EmbeddedVideo({ + embedUrl, + provider, + title, +}: { + embedUrl: string; + provider: string; + title: string; +}) { + const [gestartet, setGestartet] = useState(false); + + if (gestartet) { + return ( +
+