'use client';
import { isSupportAttachmentImage, type Passenger } from '@edr/types';
import { FileText, ImageOff } from 'lucide-react';
import { useAttachmentObjectUrl } from './useAttachmentObjectUrl';
type AttachmentDto = Passenger.PassengerSupportAttachmentDto;
/** Human-readable size — kept coarse; nobody needs bytes in a chat bubble. */
export function formatBytes(bytes: number): string {
if (bytes < 1024) return `${bytes} B`;
if (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)} KB`;
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
}
/**
* One image attachment. Its own component because the bytes are fetched through
* the authenticated client (see {@link useAttachmentObjectUrl}) and a hook can't
* be called from inside a `.map()`.
*/
function ImageAttachment({
a,
onView,
}: {
a: AttachmentDto;
onView: (a: AttachmentDto, src: string) => void;
}) {
const { src, failed } = useAttachmentObjectUrl(a.url);
if (failed) {
return (
Couldn't load {a.name}
);
}
if (!src) {
return (
);
}
return (
);
}
/**
* Attachments inside a message bubble: images as thumbnails, everything else as
* a labelled file row. Non-images are not fetched until opened — pulling every
* document in a thread just to draw a filename would be wasteful.
*/
export function MessageAttachments({
attachments,
mine,
onView,
onOpenFile,
}: {
attachments: AttachmentDto[];
mine: boolean;
onView: (a: AttachmentDto, src: string) => void;
onOpenFile: (a: AttachmentDto) => void;
}) {
if (attachments.length === 0) return null;
return (