11import { useEffect , useRef , useState } from "react"
22
3- import { cn } from "@/lib/utils"
43import { Dialog , DialogContent , DialogTrigger } from "@/components/ui/dialog"
5- import { shortHash } from "@/components/chat/bubbles/payload-utils"
6- import { BubbleTail } from "@/components/chat/bubbles/BubbleTail"
74
85interface ImageBubbleProps {
96 align : "left" | "right"
@@ -15,12 +12,8 @@ interface ImageBubbleProps {
1512}
1613
1714export function ImageBubble ( {
18- align,
1915 imageUrl,
20- timestamp,
2116 provisional,
22- attachmentManifestHash,
23- showTail = true ,
2417} : ImageBubbleProps ) {
2518 const containerRef = useRef < HTMLDivElement | null > ( null )
2619 const [ inView , setInView ] = useState ( false )
@@ -55,57 +48,32 @@ export function ImageBubble({
5548
5649 const shouldLoad = inView || ! imageUrl
5750
58- const self = align === "right"
51+ if ( ! imageUrl ) {
52+ return null
53+ }
54+
5955 return (
60- < div className = "relative w-fit" >
6156 < div
6257 ref = { containerRef }
63- className = { cn (
64- "w-fit overflow-hidden rounded-[18px]" ,
65- self
66- ? "bg-[var(--chat-bubble-self)]"
67- : "bg-[var(--chat-bubble-peer)]" ,
68- showTail && self && "rounded-br-[4px]" ,
69- showTail && ! self && "rounded-bl-[4px]" ,
70- provisional ? "opacity-60" : "" ,
71- ) }
58+ className = { provisional ? "opacity-60" : "" }
7259 >
73- { imageUrl && shouldLoad ? (
74- < Dialog >
75- < DialogTrigger asChild >
76- < img
77- src = { imageUrl }
78- alt = "attachment"
79- loading = "lazy"
80- className = { cn (
81- "max-h-72 w-full cursor-zoom-in object-cover" ,
82- align === "right" ? "rounded-lg" : "rounded-sm" ,
83- ) }
84- />
85- </ DialogTrigger >
86- < DialogContent className = "max-w-4xl bg-black/95 p-2" >
87- < img src = { imageUrl } alt = "attachment-full" className = "max-h-[85vh] w-full rounded object-contain" />
88- </ DialogContent >
89- </ Dialog >
90- ) : imageUrl ? (
91- < div className = "flex min-h-28 min-w-48 items-center justify-center rounded-lg border border-dashed border-border text-xs text-muted-foreground" >
92- Image hidden until visible
93- </ div >
94- ) : (
95- < div className = "flex min-h-28 min-w-48 items-center justify-center rounded-lg border border-dashed border-border text-xs text-muted-foreground" >
96- Image preview unavailable
97- </ div >
98- ) }
99- { attachmentManifestHash ? (
100- < p className = "mt-1 text-[10px] text-muted-foreground" >
101- attachment: { shortHash ( attachmentManifestHash , 8 ) }
102- </ p >
103- ) : null }
104- < p className = "mt-1 text-[10px] text-muted-foreground" >
105- { new Date ( timestamp ) . toLocaleTimeString ( [ ] , { hour : "2-digit" , minute : "2-digit" } ) }
106- </ p >
107- </ div >
108- { showTail && < BubbleTail align = { align } /> }
60+ { shouldLoad ? (
61+ < Dialog >
62+ < DialogTrigger asChild >
63+ < img
64+ src = { imageUrl }
65+ alt = "image"
66+ loading = "lazy"
67+ className = "block max-h-72 max-w-[280px] cursor-zoom-in rounded-[4px] object-cover"
68+ />
69+ </ DialogTrigger >
70+ < DialogContent className = "max-w-4xl bg-black/95 p-2" >
71+ < img src = { imageUrl } alt = "image-full" className = "max-h-[85vh] w-full rounded object-contain" />
72+ </ DialogContent >
73+ </ Dialog >
74+ ) : (
75+ < div className = "h-48 w-[280px]" />
76+ ) }
10977 </ div >
11078 )
11179}
0 commit comments