import { LoaderCircle } from "lucide-react"; import { DownloadCloudIcon } from "../icons/DownloadCloudIcon"; import { useState, type MouseEvent } from "react"; import { useI18n } from "../../i18n"; import type { Attachment } from "../../types/post"; import { downloadAttachment, pauseActiveVideos } from "./utils/downloadFile"; import { formatBytes } from "./utils/formatBytes"; import { mediaSaveKindFromAttachment, useSaveToAlbumGuide, } from "../SaveToAlbumGuide"; import { useToast } from "../Toast"; type AttachmentDownloadPillProps = { postId: string; attachment: Attachment; leadingLabel?: string; className?: string; /** * When true, the pill scales with its host container's width via container- * query units (clamped 22-30px). The host element must establish a query * container with `style={{ containerType: "inline-size" }}`. Use this on * album tiles so the pill shrinks on small thumbnails in mixed layouts. * Defaults to false (fixed 30px) for standalone images/videos. */ adaptive?: boolean; /** * Visual size. `sm` (default, 30 px tall) for inline tiles where space is * tight. `lg` (44 px tall with a 24 px icon and 14 px text) for overlay * surfaces like the image lightbox or fullscreen video player where the * affordance can breathe and should feel touch-friendly. * Ignored when `adaptive` is set. */ size?: "sm" | "lg"; }; export function AttachmentDownloadPill({ postId, attachment, leadingLabel, className = "absolute left-2 top-2", adaptive = false, size = "sm", }: AttachmentDownloadPillProps) { const { t } = useI18n(); const { showToast } = useToast(); const { showSaveToAlbumGuide } = useSaveToAlbumGuide(); const [isDownloading, setIsDownloading] = useState(false); const handleDownload = async (e: MouseEvent) => { e.stopPropagation(); if (isDownloading) return; pauseActiveVideos(); setIsDownloading(true); try { await downloadAttachment(postId, attachment.id, attachment.filename); const mediaKind = mediaSaveKindFromAttachment(attachment); if (mediaKind) showSaveToAlbumGuide(mediaKind); } catch { showToast(t("downloadFail"), "error"); } finally { setIsDownloading(false); } }; const isLg = !adaptive && size === "lg"; const fontCls = adaptive ? "text-[clamp(10px,7cqw,12px)]" : isLg ? "text-[14px] font-medium" : "text-[12px]"; const squareCls = adaptive ? "h-[clamp(22px,18cqw,30px)] w-[clamp(22px,18cqw,30px)]" : isLg ? "h-[44px] w-[44px]" : "h-[30px] w-[30px]"; const iconCls = adaptive ? "h-[clamp(13px,11cqw,18px)] w-[clamp(13px,11cqw,18px)]" : isLg ? "h-[22px] w-[22px]" : "h-[18px] w-[18px]"; const textBoxCls = adaptive ? "h-[clamp(22px,18cqw,30px)] px-[clamp(6px,6cqw,10px)]" : isLg ? "h-[44px] px-4" : "h-[30px] px-2.5"; return ( ); }