diff --git a/src/components/messageStream/MessageBubble.tsx b/src/components/messageStream/MessageBubble.tsx
index 00fafad..5e1fb73 100644
--- a/src/components/messageStream/MessageBubble.tsx
+++ b/src/components/messageStream/MessageBubble.tsx
@@ -27,20 +27,29 @@ export function MessageBubble({ post }: { post: Post }) {
const { lang } = useI18n();
const Bubble = pickBubble(post);
const isTextOnly = post.attachments.length === 0;
+ const isVisual = post.attachments.some(
+ (a) => a.kind === "image" || a.kind === "video",
+ );
return (
-
-
-
-
-
+
+
+
+
+
);
}
diff --git a/src/components/messageStream/utils/downloadFile.ts b/src/components/messageStream/utils/downloadFile.ts
index 99d2fdf..c9050a4 100644
--- a/src/components/messageStream/utils/downloadFile.ts
+++ b/src/components/messageStream/utils/downloadFile.ts
@@ -1,27 +1,5 @@
import { assetUrl } from "../../../api";
-type SaveFilePicker = (options?: {
- suggestedName?: string;
- types?: Array<{
- description?: string;
- accept: Record;
- }>;
-}) => Promise<{
- createWritable: () => Promise<{
- write: (data: Blob) => Promise;
- close: () => Promise;
- }>;
-}>;
-
-type NavigatorWithFileShare = Navigator & {
- canShare?: (data: { files?: File[] }) => boolean;
- share?: (data: { files?: File[]; title?: string }) => Promise;
-};
-
-type WindowWithSavePicker = Window & {
- showSaveFilePicker?: SaveFilePicker;
-};
-
export function attachmentDownloadUrl(postId: string, attachmentId: string) {
return assetUrl(
`/api/posts/${encodeURIComponent(postId)}/attachments/${encodeURIComponent(
@@ -39,45 +17,7 @@ export async function downloadAttachment(
}
export async function downloadFile(url: string, filename: string) {
- const res = await fetch(url, { credentials: "include" });
- if (!res.ok) throw new Error(await res.text());
-
- const blob = await res.blob();
- const safeName = filename || "download";
-
- if (window.isSecureContext) {
- const picker = (window as WindowWithSavePicker).showSaveFilePicker;
- if (picker) {
- const handle = await picker({
- suggestedName: safeName,
- types: blob.type
- ? [
- {
- description: "File",
- accept: { [blob.type]: [extensionFromName(safeName)] },
- },
- ]
- : undefined,
- });
- const writable = await handle.createWritable();
- await writable.write(blob);
- await writable.close();
- return;
- }
- }
-
- const file = new File([blob], safeName, {
- type: blob.type || "application/octet-stream",
- });
- const nav = navigator as NavigatorWithFileShare;
- if (nav.canShare?.({ files: [file] }) && nav.share) {
- await nav.share({ files: [file], title: safeName });
- return;
- }
-
- const objectUrl = URL.createObjectURL(blob);
- triggerDownload(objectUrl, safeName);
- window.setTimeout(() => URL.revokeObjectURL(objectUrl), 30_000);
+ triggerDownload(url, filename || "download");
}
function triggerDownload(url: string, filename: string) {
@@ -89,8 +29,3 @@ function triggerDownload(url: string, filename: string) {
a.click();
a.remove();
}
-
-function extensionFromName(filename: string) {
- const match = /\.[^.]+$/.exec(filename);
- return match?.[0] || ".bin";
-}