10 changed files with 265 additions and 85 deletions
@ -0,0 +1,76 @@
@@ -0,0 +1,76 @@
|
||||
import { getCachedThreadContextEvents } from '@/lib/navigation-related-events' |
||||
import { toNote } from '@/lib/link' |
||||
import client from '@/services/client.service' |
||||
import { extractAllMediaFromEvent } from '@/services/media-extraction.service' |
||||
import { useSmartNoteNavigationOptional } from '@/PageManager' |
||||
import { Images, Music, Play } from 'lucide-react' |
||||
import { type Event } from 'nostr-tools' |
||||
import { useMemo } from 'react' |
||||
|
||||
export default function MediaGridItem({ event }: { event: Event }) { |
||||
const { navigateToNote } = useSmartNoteNavigationOptional() |
||||
|
||||
const media = useMemo(() => extractAllMediaFromEvent(event), [event]) |
||||
const first = media.all[0] |
||||
|
||||
const isVideo = |
||||
first?.m?.startsWith('video/') || event.kind === 21 || event.kind === 22 |
||||
const isAudio = first?.m?.startsWith('audio/') || event.kind === 1222 |
||||
const hasMultiple = media.all.length > 1 |
||||
|
||||
// For videos prefer the poster image; fall back to video URL (browser extracts frame)
|
||||
const displayUrl = isVideo |
||||
? (first?.image ?? first?.url) |
||||
: (first?.thumb ?? first?.url) |
||||
|
||||
const handleClick = () => { |
||||
client.addEventToCache(event) |
||||
navigateToNote(toNote(event), event, getCachedThreadContextEvents(event)) |
||||
} |
||||
|
||||
return ( |
||||
<div |
||||
className="relative aspect-square cursor-pointer overflow-hidden bg-muted" |
||||
onClick={handleClick} |
||||
> |
||||
{displayUrl ? ( |
||||
isVideo && !first?.image ? ( |
||||
<video |
||||
src={displayUrl} |
||||
className="h-full w-full object-cover" |
||||
muted |
||||
preload="metadata" |
||||
/> |
||||
) : ( |
||||
<img |
||||
src={displayUrl} |
||||
alt={first?.alt ?? ''} |
||||
className="h-full w-full object-cover" |
||||
loading="lazy" |
||||
/> |
||||
) |
||||
) : ( |
||||
<div className="flex h-full w-full items-center justify-center text-muted-foreground/40"> |
||||
{isAudio ? <Music className="size-8" /> : <Play className="size-8" />} |
||||
</div> |
||||
)} |
||||
|
||||
{/* Top-right badge */} |
||||
{isVideo && ( |
||||
<div className="absolute right-2 top-2 rounded bg-black/60 p-1"> |
||||
<Play className="size-6 fill-white text-white" /> |
||||
</div> |
||||
)} |
||||
{isAudio && ( |
||||
<div className="absolute right-2 top-2 rounded bg-black/60 p-1"> |
||||
<Music className="size-6 text-white" /> |
||||
</div> |
||||
)} |
||||
{hasMultiple && !isVideo && !isAudio && ( |
||||
<div className="absolute right-2 top-2 rounded bg-black/60 p-1"> |
||||
<Images className="size-6 text-white" /> |
||||
</div> |
||||
)} |
||||
</div> |
||||
) |
||||
} |
||||
Loading…
Reference in new issue