feat(web): use blob download flow in chat info attachment menu
Some checks failed
CI / test (push) Has been cancelled
Some checks failed
CI / test (push) Has been cancelled
This commit is contained in:
@@ -22,7 +22,7 @@ Legend:
|
|||||||
13. Reactions - `DONE`
|
13. Reactions - `DONE`
|
||||||
14. Delivery Status - `DONE` (sent/delivered/read + reconnect reconciliation after backend restarts)
|
14. Delivery Status - `DONE` (sent/delivered/read + reconnect reconciliation after backend restarts)
|
||||||
15. Typing Realtime - `DONE` (web: typing start/stop + recording voice start/stop; `recording_video_*` remains for mobile circle-video clients)
|
15. Typing Realtime - `DONE` (web: typing start/stop + recording voice start/stop; `recording_video_*` remains for mobile circle-video clients)
|
||||||
16. Media & Attachments - `DONE` (upload/preview/download/gallery; sticker/GIF inline media no longer opens photo viewer on click)
|
16. Media & Attachments - `DONE` (upload/preview/download/gallery; sticker/GIF inline media no longer opens photo viewer on click; Chat Info and message context menus use blob-download flow with success/error toasts)
|
||||||
17. Voice Messages - `PARTIAL` (record/send/play/seek + global speed 1x/1.5x/2x; recorder uses improved mime priority for better duration metadata; backend media allowlist includes `audio/mp4`/`audio/x-m4a`; audio store tracks duration via `durationchange/seekable` fallback; websocket send/recorder stop race on fast chat switch is guarded; UX still being polished)
|
17. Voice Messages - `PARTIAL` (record/send/play/seek + global speed 1x/1.5x/2x; recorder uses improved mime priority for better duration metadata; backend media allowlist includes `audio/mp4`/`audio/x-m4a`; audio store tracks duration via `durationchange/seekable` fallback; websocket send/recorder stop race on fast chat switch is guarded; UX still being polished)
|
||||||
18. Circle Video Messages - `PARTIAL` (mobile-priority only: backend type/realtime supported; web composer intentionally does not send circles)
|
18. Circle Video Messages - `PARTIAL` (mobile-priority only: backend type/realtime supported; web composer intentionally does not send circles)
|
||||||
19. Stickers - `PARTIAL` (web sticker picker with preset pack + favorites)
|
19. Stickers - `PARTIAL` (web sticker picker with preset pack + favorites)
|
||||||
|
|||||||
@@ -961,9 +961,22 @@ export function ChatInfoPanel({ chatId, open, onClose }: Props) {
|
|||||||
<a className="block rounded px-2 py-1.5 text-sm hover:bg-slate-800" href={attachmentCtx.url} rel="noreferrer" target="_blank">
|
<a className="block rounded px-2 py-1.5 text-sm hover:bg-slate-800" href={attachmentCtx.url} rel="noreferrer" target="_blank">
|
||||||
Open
|
Open
|
||||||
</a>
|
</a>
|
||||||
<a className="block rounded px-2 py-1.5 text-sm hover:bg-slate-800" href={attachmentCtx.url} download>
|
<button
|
||||||
|
className="block w-full rounded px-2 py-1.5 text-left text-sm hover:bg-slate-800"
|
||||||
|
onClick={async () => {
|
||||||
|
try {
|
||||||
|
await downloadFileFromUrl(attachmentCtx.url);
|
||||||
|
showToast("File downloaded");
|
||||||
|
} catch {
|
||||||
|
showToast("Download failed");
|
||||||
|
} finally {
|
||||||
|
setAttachmentCtx(null);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
Download
|
Download
|
||||||
</a>
|
</button>
|
||||||
<button
|
<button
|
||||||
className="block w-full rounded px-2 py-1.5 text-left text-sm hover:bg-slate-800"
|
className="block w-full rounded px-2 py-1.5 text-left text-sm hover:bg-slate-800"
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
@@ -1299,3 +1312,20 @@ function shortLink(url: string): string {
|
|||||||
return url;
|
return url;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function downloadFileFromUrl(url: string): Promise<void> {
|
||||||
|
const response = await fetch(url, { mode: "cors" });
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error("Download failed");
|
||||||
|
}
|
||||||
|
const blob = await response.blob();
|
||||||
|
const filename = extractFileName(url);
|
||||||
|
const blobUrl = window.URL.createObjectURL(blob);
|
||||||
|
const link = document.createElement("a");
|
||||||
|
link.href = blobUrl;
|
||||||
|
link.download = filename;
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
link.remove();
|
||||||
|
window.URL.revokeObjectURL(blobUrl);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user