setFailed(true)}
+ onError={() => setAttempt((i) => i + 1)}
/>
);
}
diff --git a/src/components/messageStream/bubbles/AlbumBubble.tsx b/src/components/messageStream/bubbles/AlbumBubble.tsx
index 40a22b5..2347969 100644
--- a/src/components/messageStream/bubbles/AlbumBubble.tsx
+++ b/src/components/messageStream/bubbles/AlbumBubble.tsx
@@ -1,17 +1,10 @@
-import { LoaderCircle, X } from "lucide-react";
-import { DownloadCloudIcon } from "../../icons/DownloadCloudIcon";
-import { useEffect, useState } from "react";
-import { createPortal } from "react-dom";
import { useI18n } from "../../../i18n";
-import type { Attachment, Post } from "../../../types/post";
+import type { Post } from "../../../types/post";
import { AttachmentDownloadPill } from "../AttachmentDownloadPill";
import { BubbleImage } from "../BubbleImage";
import { useLightbox } from "../overlays/ImageLightbox";
import { autolink } from "../utils/autolink";
-import { downloadAttachment } from "../utils/downloadFile";
-import { formatBytes } from "../utils/formatBytes";
import { postDisplayText } from "../utils/postText";
-import { useToast } from "../../Toast";
const MAX_VISIBLE = 4;
@@ -26,135 +19,9 @@ function albumItemClass(index: number, count: number) {
return "";
}
-function ImageListDownloadButton({
- postId,
- attachment,
-}: {
- postId: string;
- attachment: Attachment;
-}) {
- const { t } = useI18n();
- const { showToast } = useToast();
- const [isDownloading, setIsDownloading] = useState(false);
-
- const handleDownload = async () => {
- if (isDownloading) return;
- setIsDownloading(true);
- try {
- await downloadAttachment(postId, attachment.id, attachment.filename);
- showToast(t("downloadOk"));
- } catch {
- showToast(t("downloadFail"), "error");
- } finally {
- setIsDownloading(false);
- }
- };
-
- return (
-
- );
-}
-
-function ImageListDialog({
- postId,
- images,
- onClose,
- onPick,
-}: {
- postId: string;
- images: Attachment[];
- onClose: () => void;
- onPick: (index: number) => void;
-}) {
- useEffect(() => {
- const onKey = (event: KeyboardEvent) => {
- if (event.key === "Escape") onClose();
- };
- window.addEventListener("keydown", onKey);
- return () => window.removeEventListener("keydown", onKey);
- }, [onClose]);
-
- return createPortal(
-
-
e.stopPropagation()}
- >
-
-
- {images.map((image, index) => (
-
-
-
-
- ))}
-
-
-
,
- document.body,
- );
-}
-
export function AlbumBubble({ post }: { post: Post }) {
const { openLightbox } = useLightbox();
const { lang } = useI18n();
- const [listOpen, setListOpen] = useState(false);
const images = post.attachments;
const text = postDisplayText(post, lang);
const visible = images.slice(0, MAX_VISIBLE);
@@ -176,21 +43,23 @@ export function AlbumBubble({ post }: { post: Post }) {
>
);
}
diff --git a/src/components/messageStream/overlays/ImageLightbox.tsx b/src/components/messageStream/overlays/ImageLightbox.tsx
index eaacad2..c41657d 100644
--- a/src/components/messageStream/overlays/ImageLightbox.tsx
+++ b/src/components/messageStream/overlays/ImageLightbox.tsx
@@ -8,9 +8,14 @@ import {
type PropsWithChildren,
} from "react";
import { createPortal } from "react-dom";
-import { ChevronLeft, ChevronRight, X } from "lucide-react";
+import { ChevronLeft, ChevronRight, LoaderCircle, X } from "lucide-react";
import type { Attachment } from "../../../types/post";
+import { DownloadCloudIcon } from "../../icons/DownloadCloudIcon";
+import { useI18n } from "../../../i18n";
+import { useToast } from "../../Toast";
+import { BubbleImage } from "../BubbleImage";
import { autolink } from "../utils/autolink";
+import { downloadAttachment } from "../utils/downloadFile";
type LightboxState = {
images: Attachment[];
@@ -65,6 +70,7 @@ export function ImageLightboxProvider({ children }: PropsWithChildren) {
images={state.images}
startIndex={state.index}
caption={state.caption}
+ postId={state.postId}
onClose={closeLightbox}
/>
) : null}
@@ -72,15 +78,121 @@ export function ImageLightboxProvider({ children }: PropsWithChildren) {
);
}
+function LightboxDownloadButton({
+ postId,
+ attachment,
+}: {
+ postId: string;
+ attachment: Attachment;
+}) {
+ const { t } = useI18n();
+ const { showToast } = useToast();
+ const [isDownloading, setIsDownloading] = useState(false);
+
+ const handleDownload = async () => {
+ if (isDownloading) return;
+ setIsDownloading(true);
+ try {
+ await downloadAttachment(postId, attachment.id, attachment.filename);
+ showToast(t("downloadOk"));
+ } catch {
+ showToast(t("downloadFail"), "error");
+ } finally {
+ setIsDownloading(false);
+ }
+ };
+
+ return (
+