allow download of attachments

This commit is contained in:
talksik
2026-03-30 10:29:23 -07:00
parent d0da400b29
commit 37bf210026
@@ -1,8 +1,9 @@
import { Download, FileIcon } from "lucide-react"; import { Download, ExternalLink, FileIcon } from "lucide-react";
import type { Particle } from "@/api/types"; import type { Particle } from "@/api/types";
import { useDownloadUrl } from "@/hooks/use-download-url"; import { useDownloadUrl } from "@/hooks/use-download-url";
import { Skeleton } from "@/components/ui/skeleton"; import { Skeleton } from "@/components/ui/skeleton";
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area"; import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
import { Button } from "@/components/ui/button";
type FileParticle = Extract<Particle, { type: "file" }>; type FileParticle = Extract<Particle, { type: "file" }>;
@@ -23,22 +24,37 @@ function ImageAttachment({ particle }: { particle: FileParticle }) {
return <Skeleton className="h-20 w-20 shrink-0 rounded-lg bg-white/10" />; return <Skeleton className="h-20 w-20 shrink-0 rounded-lg bg-white/10" />;
} }
const handleDownload = (e: React.MouseEvent) => {
e.stopPropagation();
e.preventDefault();
const a = document.createElement("a");
a.href = url;
a.download = particle.properties.filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
};
return ( return (
<a <a
href={url} href={url}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
onClick={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()}
className="group relative block h-20 w-20 shrink-0 overflow-hidden rounded-lg bg-white/10" className="group relative block h-30 w-40 shrink-0 overflow-hidden rounded-lg bg-white/10"
> >
<img <img
src={url} src={url}
alt={particle.properties.filename} alt={particle.properties.filename}
className="h-full w-full object-cover transition-opacity group-hover:opacity-80" className="h-full w-full object-cover"
/> />
<div className="absolute inset-0 flex items-center justify-center opacity-0 transition-opacity group-hover:opacity-100"> <button
<Download className="size-5 text-white drop-shadow-md" /> type="button"
</div> onClick={handleDownload}
className="absolute bottom-1 right-1 rounded-full bg-black/60 p-1 text-white/70 opacity-0 transition-opacity hover:text-white group-hover:opacity-100"
>
<Download className="size-3.5" />
</button>
</a> </a>
); );
} }
@@ -46,10 +62,30 @@ function ImageAttachment({ particle }: { particle: FileParticle }) {
function FileAttachment({ particle }: { particle: FileParticle }) { function FileAttachment({ particle }: { particle: FileParticle }) {
const { data: url } = useDownloadUrl(particle.properties.object_id); const { data: url } = useDownloadUrl(particle.properties.object_id);
const inner = ( const handleOpen = (e: React.MouseEvent) => {
<div className="flex h-20 shrink-0 items-center gap-2.5 rounded-lg bg-white/10 px-3 transition-colors hover:bg-white/15"> e.stopPropagation();
<FileIcon className="size-5 shrink-0 text-white/60" /> if (url) window.electronLink.openExternal(url);
<div className="flex min-w-0 flex-col gap-0.5"> };
const handleDownload = (e: React.MouseEvent) => {
e.stopPropagation();
if (!url) return;
const a = document.createElement("a");
a.href = url;
a.download = particle.properties.filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
};
return (
<div
role="button"
onClick={handleOpen}
className="flex shrink-0 cursor-pointer flex-col gap-1.5 rounded-lg bg-white/10 px-3 py-2 transition-colors hover:bg-white/15"
>
<div className="flex items-center gap-2">
<FileIcon className="size-4 shrink-0 text-white/60" />
<span className="max-w-[10rem] truncate text-xs font-medium text-white/90"> <span className="max-w-[10rem] truncate text-xs font-medium text-white/90">
{particle.properties.filename} {particle.properties.filename}
</span> </span>
@@ -57,24 +93,28 @@ function FileAttachment({ particle }: { particle: FileParticle }) {
{formatFileSize(particle.properties.size_bytes)} {formatFileSize(particle.properties.size_bytes)}
</span> </span>
</div> </div>
<Download className="size-3.5 shrink-0 text-white/30" /> <div className="flex gap-2">
<Button
variant="ghost"
size="xs"
className="text-white/70 hover:bg-white/10 hover:text-white"
onClick={handleOpen}
>
<ExternalLink data-icon="inline-start" />
Open
</Button>
<Button
variant="ghost"
size="xs"
className="text-white/70 hover:bg-white/10 hover:text-white"
onClick={handleDownload}
>
<Download data-icon="inline-start" />
Download
</Button>
</div>
</div> </div>
); );
if (url) {
return (
<a
href={url}
target="_blank"
rel="noopener noreferrer"
onClick={(e) => e.stopPropagation()}
>
{inner}
</a>
);
}
return inner;
} }
export function ParticleAttachments({ attachments }: ParticleAttachmentsProps) { export function ParticleAttachments({ attachments }: ParticleAttachmentsProps) {