feat: add video support to CarouselAnnuncio and integrate VideoPlayer component

This commit is contained in:
Marco Pedone 2025-09-01 15:59:08 +02:00
parent 1f1d9f3559
commit 0bc1a0915c
4 changed files with 79 additions and 17 deletions

View file

@ -20,6 +20,7 @@ import {
import { camereTesti, handleConsegna } from "~/lib/annuncio_details"; import { camereTesti, handleConsegna } from "~/lib/annuncio_details";
import { cn, formatCurrency } from "~/lib/utils"; import { cn, formatCurrency } from "~/lib/utils";
import { useTranslation } from "~/providers/I18nProvider"; import { useTranslation } from "~/providers/I18nProvider";
import { VideoPlayer } from "./videoPlayer";
type CardAnnuncioProps = { type CardAnnuncioProps = {
id: number; id: number;
@ -205,9 +206,11 @@ export const CardAnnuncio = ({
export const CarouselAnnuncio = ({ export const CarouselAnnuncio = ({
single, single,
immagini, immagini,
videos,
}: { }: {
single?: boolean; single?: boolean;
immagini: string[] | null; immagini: string[] | null;
videos: string[] | null;
}) => { }) => {
const [openModal, setOpenModal] = useState(false); const [openModal, setOpenModal] = useState(false);
const [idxModal, setIdxModal] = useState(0); const [idxModal, setIdxModal] = useState(0);
@ -215,7 +218,6 @@ export const CarouselAnnuncio = ({
setIdxModal(position); setIdxModal(position);
setOpenModal(true); setOpenModal(true);
} }
return ( return (
<> <>
<div className="mx-auto my-4 w-full rounded-md text-clip"> <div className="mx-auto my-4 w-full rounded-md text-clip">
@ -245,11 +247,43 @@ export const CarouselAnnuncio = ({
width={800} width={800}
/> />
<Maximize2 <Maximize2
className="absolute right-2 bottom-2 size-6 rounded-md bg-white/70 stroke-2 p-1 transition-opacity duration-150 group-hover:opacity-100 sm:opacity-0" className="absolute right-2 bottom-2 transition-all duration-300 hover:scale-150 origin-bottom-right ease-in-out cursor-pointer size-7 rounded-md bg-white/70 stroke-2 p-1 group-hover:opacity-100 sm:opacity-0"
onClick={() => handleOpenModal(idx)} onClick={() => handleOpenModal(idx)}
/> />
</CarouselItem> </CarouselItem>
))} ))}
{videos?.map((video, idx) => {
if (!video) return null;
if (video.includes("youtu")) {
return null;
} else {
return (
<CarouselItem
className={cn(
"group relative",
immagini && immagini.length === 1
? ""
: !single && "md:basis-1/2 lg:basis-1/3",
)}
key={`videoplayer-${video}`}
>
<VideoPlayer
className="h-72 "
coverImage={immagini?.[0] ? immagini[0] : ""}
key={`videoplayer-${idx}-${openModal}`}
videoSrc={video}
/>
<Maximize2
className="absolute right-2 bottom-2 transition-all duration-300 hover:scale-150 origin-bottom-right ease-in-out cursor-pointer size-7 rounded-md bg-white/70 stroke-2 p-1 group-hover:opacity-100 sm:opacity-0"
onClick={() =>
handleOpenModal(immagini?.length || 0 + idx)
}
/>
</CarouselItem>
);
}
})}
</CarouselContent> </CarouselContent>
<CarouselPrevious <CarouselPrevious
@ -284,6 +318,25 @@ export const CarouselAnnuncio = ({
/> />
</CarouselItem> </CarouselItem>
))} ))}
{videos?.map((video) => {
if (!video) return null;
if (video.includes("youtu")) {
return null;
} else {
return (
<CarouselItem
className="relative flex items-center justify-center"
key={`carousel-videoplayer-${video}`}
>
<VideoPlayer
className="h-[90vh] absolute w-full mx-auto max-w-fit rounded-md object-contain"
coverImage={immagini?.[0] ? immagini[0] : ""}
videoSrc={video}
/>
</CarouselItem>
);
}
})}
</CarouselContent> </CarouselContent>
<CarouselPrevious <CarouselPrevious

View file

@ -1,9 +1,9 @@
"use client"; "use client";
import { AnimatePresence, motion } from "framer-motion"; import { AnimatePresence, motion, useInView } from "framer-motion";
import { Play } from "lucide-react"; import { Play } from "lucide-react";
import Image from "next/image"; import Image from "next/image";
import { useState } from "react"; import { useEffect, useRef, useState } from "react";
import { cn } from "~/lib/utils"; import { cn } from "~/lib/utils";
interface VideoPlayerProps { interface VideoPlayerProps {
@ -22,6 +22,14 @@ export function VideoPlayer({
className, className,
}: VideoPlayerProps) { }: VideoPlayerProps) {
const [isPlaying, setIsPlaying] = useState(false); const [isPlaying, setIsPlaying] = useState(false);
const videoRef = useRef<HTMLDivElement>(null);
const inView = useInView(videoRef);
useEffect(() => {
if (!inView) {
setIsPlaying(false);
}
}, [inView]);
const aspectRatioClasses = { const aspectRatioClasses = {
square: "aspect-square", square: "aspect-square",
@ -36,6 +44,7 @@ export function VideoPlayer({
aspectRatioClasses[aspectRatio], aspectRatioClasses[aspectRatio],
className, className,
)} )}
ref={videoRef}
> >
<AnimatePresence mode="wait"> <AnimatePresence mode="wait">
{!isPlaying ? ( {!isPlaying ? (

View file

@ -454,18 +454,15 @@ export const AnnuncioEditForm = ({ data }: { data: Annunci }) => {
<CardTitle>Imagini Annuncio</CardTitle> <CardTitle>Imagini Annuncio</CardTitle>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
{data.url_immagini && data.url_immagini.length > 0 ? (
<CarouselAnnuncio <CarouselAnnuncio
immagini={data.url_immagini.map( immagini={
data.url_immagini?.map(
(v) => `/go-api/images/get/${v}`, (v) => `/go-api/images/get/${v}`,
)} ) || null
}
single single
videos={data.url_video}
/> />
) : (
<div className="text-center text-gray-500">
Nessuna immagine disponibile
</div>
)}
</CardContent> </CardContent>
</Card> </Card>
</div> </div>

View file

@ -133,7 +133,10 @@ const AnnuncioView = ({ cod, flag }: Omit<AnnuncioProps, "meta">) => {
<AnnuncioContext.Provider value={data}> <AnnuncioContext.Provider value={data}>
<TouchProvider> <TouchProvider>
<div className="mx-auto w-full px-2 sm:px-8"> <div className="mx-auto w-full px-2 sm:px-8">
<CarouselAnnuncio immagini={data.url_immagini} /> <CarouselAnnuncio
immagini={data.url_immagini}
videos={data.url_video}
/>
<div className="flex flex-col sm:py-8 md:flex-row-reverse md:gap-2"> <div className="flex flex-col sm:py-8 md:flex-row-reverse md:gap-2">
<div className="mb-6 flex flex-col md:mb-0 md:w-1/3"> <div className="mb-6 flex flex-col md:mb-0 md:w-1/3">