- Improved `ViewCounter` with visibility-based view increment using `IntersectionObserver` and 50% video progress tracking. - Added real-time file upload progress updates via Socket.io, including status and percentage feedback. - Integrated `ViewCounter` dynamically into `ContentCard` and removed redundant instances from static pages. - Updated backend upload logic to emit progress updates at different stages via the `EventsGateway`.
75 lines
1.9 KiB
TypeScript
75 lines
1.9 KiB
TypeScript
"use client";
|
|
|
|
import { type RefObject, useEffect, useRef } from "react";
|
|
import { ContentService } from "@/services/content.service";
|
|
|
|
interface ViewCounterProps {
|
|
contentId: string;
|
|
videoRef?: RefObject<HTMLVideoElement | null>;
|
|
}
|
|
|
|
export function ViewCounter({ contentId, videoRef }: ViewCounterProps) {
|
|
const hasIncremented = useRef(false);
|
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
|
|
useEffect(() => {
|
|
const increment = () => {
|
|
if (!hasIncremented.current) {
|
|
ContentService.incrementViews(contentId).catch((err) => {
|
|
console.error("Failed to increment views:", err);
|
|
});
|
|
hasIncremented.current = true;
|
|
}
|
|
};
|
|
|
|
// 1. Observer pour la visibilité (IntersectionObserver)
|
|
const observer = new IntersectionObserver(
|
|
(entries) => {
|
|
const entry = entries[0];
|
|
if (entry.isIntersecting) {
|
|
// Si c'est une image (pas de videoRef), on attend 3 secondes
|
|
if (!videoRef) {
|
|
const timer = setTimeout(() => {
|
|
increment();
|
|
}, 3000);
|
|
return () => clearTimeout(timer);
|
|
}
|
|
}
|
|
},
|
|
{ threshold: 0.5 },
|
|
);
|
|
|
|
if (containerRef.current) {
|
|
observer.observe(containerRef.current);
|
|
}
|
|
|
|
// 2. Logique pour la vidéo (> 50%)
|
|
let videoElement: HTMLVideoElement | null = null;
|
|
const handleTimeUpdate = () => {
|
|
if (videoElement && videoElement.duration > 0) {
|
|
const progress = videoElement.currentTime / videoElement.duration;
|
|
if (progress >= 0.5) {
|
|
increment();
|
|
videoElement.removeEventListener("timeupdate", handleTimeUpdate);
|
|
}
|
|
}
|
|
};
|
|
|
|
if (videoRef?.current) {
|
|
videoElement = videoRef.current;
|
|
videoElement.addEventListener("timeupdate", handleTimeUpdate);
|
|
}
|
|
|
|
return () => {
|
|
observer.disconnect();
|
|
if (videoElement) {
|
|
videoElement.removeEventListener("timeupdate", handleTimeUpdate);
|
|
}
|
|
};
|
|
}, [contentId, videoRef]);
|
|
|
|
return (
|
|
<div ref={containerRef} className="absolute inset-0 pointer-events-none" />
|
|
);
|
|
}
|