diff --git a/src/components/messageStream/AttachmentDownloadPill.tsx b/src/components/messageStream/AttachmentDownloadPill.tsx new file mode 100644 index 0000000..702a8fe --- /dev/null +++ b/src/components/messageStream/AttachmentDownloadPill.tsx @@ -0,0 +1,68 @@ +import { ArrowDownToLine, LoaderCircle } from "lucide-react"; +import { useState, type MouseEvent } from "react"; +import { useI18n } from "../../i18n"; +import type { Attachment } from "../../types/post"; +import { downloadAttachment } from "./utils/downloadFile"; +import { formatBytes } from "./utils/formatBytes"; + +type AttachmentDownloadPillProps = { + postId: string; + attachment: Attachment; + leadingLabel?: string; + className?: string; +}; + +export function AttachmentDownloadPill({ + postId, + attachment, + leadingLabel, + className = "absolute left-2 top-2", +}: AttachmentDownloadPillProps) { + const { t } = useI18n(); + const [isDownloading, setIsDownloading] = useState(false); + + const handleDownload = (e: MouseEvent) => { + e.stopPropagation(); + if (isDownloading) return; + setIsDownloading(true); + void downloadAttachment(postId, attachment.id, attachment.filename) + .finally(() => setIsDownloading(false)) + .catch(() => {}); + }; + + return ( + + ); +} diff --git a/src/components/messageStream/bubbles/ImageBubble.tsx b/src/components/messageStream/bubbles/ImageBubble.tsx index 6b81804..314a891 100644 --- a/src/components/messageStream/bubbles/ImageBubble.tsx +++ b/src/components/messageStream/bubbles/ImageBubble.tsx @@ -1,4 +1,5 @@ import type { Post } from "../../../types/post"; +import { AttachmentDownloadPill } from "../AttachmentDownloadPill"; import { useLightbox } from "../overlays/ImageLightbox"; export function ImageBubble({ post }: { post: Post }) { @@ -9,19 +10,22 @@ export function ImageBubble({ post }: { post: Post }) { att.width && att.height ? `${att.width} / ${att.height}` : "4 / 3"; return ( - +
+ + +
); } diff --git a/src/components/messageStream/bubbles/ImageWithTextBubble.tsx b/src/components/messageStream/bubbles/ImageWithTextBubble.tsx index 8ec7af8..966679a 100644 --- a/src/components/messageStream/bubbles/ImageWithTextBubble.tsx +++ b/src/components/messageStream/bubbles/ImageWithTextBubble.tsx @@ -1,17 +1,13 @@ -import { ArrowDownToLine, LoaderCircle } from "lucide-react"; -import { useState } from "react"; import { useI18n } from "../../../i18n"; import type { Post } from "../../../types/post"; import { useLightbox } from "../overlays/ImageLightbox"; +import { AttachmentDownloadPill } from "../AttachmentDownloadPill"; import { autolink } from "../utils/autolink"; -import { downloadAttachment } from "../utils/downloadFile"; -import { formatBytes } from "../utils/formatBytes"; import { postDisplayText } from "../utils/postText"; export function ImageWithTextBubble({ post }: { post: Post }) { const { openLightbox } = useLightbox(); - const { lang, t } = useI18n(); - const [isDownloading, setIsDownloading] = useState(false); + const { lang } = useI18n(); const att = post.attachments[0]; const text = postDisplayText(post, lang); if (!att) return null; @@ -34,34 +30,7 @@ export function ImageWithTextBubble({ post }: { post: Post }) { style={{ aspectRatio: ratio }} /> - + {text ? (
diff --git a/src/components/messageStream/bubbles/VideoBubble.tsx b/src/components/messageStream/bubbles/VideoBubble.tsx index 81413ee..314eb60 100644 --- a/src/components/messageStream/bubbles/VideoBubble.tsx +++ b/src/components/messageStream/bubbles/VideoBubble.tsx @@ -1,11 +1,10 @@ -import { ArrowDownToLine, LoaderCircle, Play } from "lucide-react"; +import { Play } from "lucide-react"; import { useRef, useState } from "react"; import { useI18n } from "../../../i18n"; import type { Post } from "../../../types/post"; import { useVideoPlayer } from "../overlays/VideoPlayer"; +import { AttachmentDownloadPill } from "../AttachmentDownloadPill"; import { autolink } from "../utils/autolink"; -import { downloadAttachment } from "../utils/downloadFile"; -import { formatBytes } from "../utils/formatBytes"; import { postDisplayText } from "../utils/postText"; function formatDuration(sec: number | undefined): string { @@ -17,10 +16,9 @@ function formatDuration(sec: number | undefined): string { export function VideoBubble({ post }: { post: Post }) { const { openVideo } = useVideoPlayer(); - const { lang, t } = useI18n(); + const { lang } = useI18n(); const att = post.attachments[0]; const [playing, setPlaying] = useState(false); - const [isDownloading, setIsDownloading] = useState(false); const videoRef = useRef(null); const text = postDisplayText(post, lang); if (!att) return null; @@ -70,49 +68,11 @@ export function VideoBubble({ post }: { post: Post }) { aria-hidden="true" /> )} - +