diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx index f40ce16..764417d 100644 --- a/frontend/src/app/layout.tsx +++ b/frontend/src/app/layout.tsx @@ -1,6 +1,7 @@ import type { Metadata } from "next"; import { Ubuntu_Mono, Ubuntu_Sans } from "next/font/google"; import { Toaster } from "@/components/ui/sonner"; +import { AudioProvider } from "@/providers/audio-provider"; import { AuthProvider } from "@/providers/auth-provider"; import { ThemeProvider } from "@/providers/theme-provider"; import "./globals.css"; @@ -71,8 +72,10 @@ export default function RootLayout({ disableTransitionOnChange > - {children} - + + {children} + + diff --git a/frontend/src/components/content-card.tsx b/frontend/src/components/content-card.tsx index 30794d9..50479b7 100644 --- a/frontend/src/components/content-card.tsx +++ b/frontend/src/components/content-card.tsx @@ -1,13 +1,21 @@ "use client"; -import { Edit, Eye, Heart, MoreHorizontal, Share2, Trash2 } from "lucide-react"; +import { + Edit, + Eye, + Heart, + MoreHorizontal, + Share2, + Trash2, + Volume2, + VolumeX, +} from "lucide-react"; import Image from "next/image"; import Link from "next/link"; import { useRouter } from "next/navigation"; import * as React from "react"; import { toast } from "sonner"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; -import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card, @@ -21,12 +29,7 @@ import { DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; -import { - Tooltip, - TooltipContent, - TooltipProvider, - TooltipTrigger, -} from "@/components/ui/tooltip"; +import { useAudio } from "@/providers/audio-provider"; import { useAuth } from "@/providers/auth-provider"; import { ContentService } from "@/services/content.service"; import { FavoriteService } from "@/services/favorite.service"; @@ -40,12 +43,20 @@ interface ContentCardProps { export function ContentCard({ content, onUpdate }: ContentCardProps) { const { isAuthenticated, user } = useAuth(); + const { isGlobalMuted, activeVideoId, toggleGlobalMute, setActiveVideo } = + useAudio(); const router = useRouter(); const [isLiked, setIsLiked] = React.useState(content.isLiked || false); const [likesCount, setLikesCount] = React.useState(content.favoritesCount); const [editDialogOpen, setEditDialogOpen] = React.useState(false); const isAuthor = user?.uuid === content.authorId; + const isVideo = !content.mimeType.startsWith("image/"); + const isThisVideoActive = activeVideoId === content.id; + const isMuted = isGlobalMuted || (isVideo && !isThisVideoActive); + + const aspectRatio = + content.width && content.height ? content.width / content.height : 1; React.useEffect(() => { setIsLiked(content.isLiked || false); @@ -77,6 +88,18 @@ export function ContentCard({ content, onUpdate }: ContentCardProps) { } }; + const handleToggleMute = (e: React.MouseEvent) => { + e.preventDefault(); + e.stopPropagation(); + if (isGlobalMuted) { + setActiveVideo(content.id); + } else if (isThisVideoActive) { + toggleGlobalMute(); + } else { + setActiveVideo(content.id); + } + }; + const handleUse = async (e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); @@ -107,9 +130,9 @@ export function ContentCard({ content, onUpdate }: ContentCardProps) { return ( <> - - - + + + {content.author.username[0].toUpperCase()} @@ -118,13 +141,10 @@ export function ContentCard({ content, onUpdate }: ContentCardProps) {
- {content.author.displayName || content.author.username} + {content.author.username} - - {new Date(content.createdAt).toLocaleDateString("fr-FR")} -
@@ -158,14 +178,17 @@ export function ContentCard({ content, onUpdate }: ContentCardProps) {
- + {content.mimeType.startsWith("image/") ? ( {content.title} @@ -174,99 +197,95 @@ export function ContentCard({ content, onUpdate }: ContentCardProps) { src={content.url} controls={false} autoPlay - muted + muted={isMuted} loop playsInline - className="w-full h-full object-contain" + className="w-full h-full object-cover" /> )} + {isVideo && ( + + )} - +
-
- - - - - - Liker - - - - - - - Vues - - - - - - - Partager - - +
+ +
+ + {content.views} +
+
-
- -

+
+

{likesCount} J'aime

+
+ + {content.author.username} + + {content.title} -

- -
- {content.category && ( - - {content.category.name} - - )} - {content.tags.slice(0, 3).map((tag, _i) => ( - +
+
+ {content.tags.slice(0, 5).map((tag, _i) => ( + #{typeof tag === "string" ? tag : tag.name} - + ))}
+

+ {new Date(content.createdAt).toLocaleDateString("fr-FR", { + day: "numeric", + month: "long", + })} +

diff --git a/frontend/src/components/content-list.tsx b/frontend/src/components/content-list.tsx index 1410f96..b94d1f8 100644 --- a/frontend/src/components/content-list.tsx +++ b/frontend/src/components/content-list.tsx @@ -70,9 +70,11 @@ export function ContentList({ fetchFn, title }: ContentListProps) { return (
{title &&

{title}

} -
+
{contents.map((content) => ( - +
+ +
))}