Skip to content

Commit c4176b4

Browse files
committed
fix: videos attachment
1 parent 81ef53b commit c4176b4

File tree

1 file changed

+46
-27
lines changed

1 file changed

+46
-27
lines changed

web/src/components/MemoView/components/metadata/AttachmentList.tsx

Lines changed: 46 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { FileIcon, PaperclipIcon } from "lucide-react";
2-
import { useState } from "react";
2+
import { useMemo, useState } from "react";
33
import type { Attachment } from "@/types/proto/api/v1/attachment_service_pb";
44
import { getAttachmentType, getAttachmentUrl } from "@/utils/attachment";
55
import { formatFileSize, getFileTypeLabel } from "@/utils/format";
@@ -11,13 +11,18 @@ interface AttachmentListProps {
1111
attachments: Attachment[];
1212
}
1313

14+
// Type guards for attachment types
15+
const isImageAttachment = (attachment: Attachment): boolean => getAttachmentType(attachment) === "image/*";
16+
const isVideoAttachment = (attachment: Attachment): boolean => getAttachmentType(attachment) === "video/*";
17+
const isMediaAttachment = (attachment: Attachment): boolean => isImageAttachment(attachment) || isVideoAttachment(attachment);
18+
19+
// Separate attachments into media (images/videos) and documents
1420
const separateMediaAndDocs = (attachments: Attachment[]): { media: Attachment[]; docs: Attachment[] } => {
1521
const media: Attachment[] = [];
1622
const docs: Attachment[] = [];
1723

1824
for (const attachment of attachments) {
19-
const attachmentType = getAttachmentType(attachment);
20-
if (attachmentType === "image/*" || attachmentType === "video/*") {
25+
if (isMediaAttachment(attachment)) {
2126
media.push(attachment);
2227
} else {
2328
docs.push(attachment);
@@ -55,27 +60,39 @@ const DocumentItem = ({ attachment }: { attachment: Attachment }) => {
5560
);
5661
};
5762

58-
const MediaGrid = ({ attachments, onImageClick }: { attachments: Attachment[]; onImageClick: (url: string) => void }) => (
63+
interface MediaItemProps {
64+
attachment: Attachment;
65+
onImageClick: (url: string) => void;
66+
}
67+
68+
const MediaItem = ({ attachment, onImageClick }: MediaItemProps) => {
69+
const isImage = isImageAttachment(attachment);
70+
71+
const handleClick = () => {
72+
if (isImage) {
73+
onImageClick(getAttachmentUrl(attachment));
74+
}
75+
};
76+
77+
return (
78+
<div
79+
className="aspect-square rounded-lg overflow-hidden bg-muted/40 border border-border hover:border-accent/50 transition-all cursor-pointer group"
80+
onClick={handleClick}
81+
>
82+
<AttachmentCard attachment={attachment} className="rounded-none" />
83+
</div>
84+
);
85+
};
86+
87+
interface MediaGridProps {
88+
attachments: Attachment[];
89+
onImageClick: (url: string) => void;
90+
}
91+
92+
const MediaGrid = ({ attachments, onImageClick }: MediaGridProps) => (
5993
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-2">
6094
{attachments.map((attachment) => (
61-
<div
62-
key={attachment.name}
63-
className="aspect-square rounded-lg overflow-hidden bg-muted/40 border border-border hover:border-accent/50 transition-all cursor-pointer group"
64-
onClick={() => onImageClick(getAttachmentUrl(attachment))}
65-
>
66-
<div className="w-full h-full relative">
67-
<AttachmentCard attachment={attachment} className="rounded-none" />
68-
{getAttachmentType(attachment) === "video/*" && (
69-
<div className="absolute inset-0 flex items-center justify-center bg-black/30 group-hover:bg-black/40 transition-colors">
70-
<div className="w-8 h-8 rounded-full bg-white/80 flex items-center justify-center">
71-
<svg className="w-5 h-5 text-black fill-current ml-0.5" viewBox="0 0 24 24">
72-
<path d="M8 5v14l11-7z" />
73-
</svg>
74-
</div>
75-
</div>
76-
)}
77-
</div>
78-
</div>
95+
<MediaItem key={attachment.name} attachment={attachment} onImageClick={onImageClick} />
7996
))}
8097
</div>
8198
);
@@ -98,18 +115,20 @@ const AttachmentList = ({ attachments }: AttachmentListProps) => {
98115
mimeType: undefined,
99116
});
100117

101-
const { media: mediaItems, docs: docItems } = separateMediaAndDocs(attachments);
118+
const { media: mediaItems, docs: docItems } = useMemo(() => separateMediaAndDocs(attachments), [attachments]);
119+
120+
// Pre-compute image URLs for preview dialog to avoid filtering on every click
121+
const imageAttachments = useMemo(() => mediaItems.filter(isImageAttachment), [mediaItems]);
122+
const imageUrls = useMemo(() => imageAttachments.map(getAttachmentUrl), [imageAttachments]);
102123

103124
if (attachments.length === 0) {
104125
return null;
105126
}
106127

107128
const handleImageClick = (imgUrl: string) => {
108-
const imageAttachments = mediaItems.filter((a) => getAttachmentType(a) === "image/*");
109-
const imgUrls = imageAttachments.map((a) => getAttachmentUrl(a));
110-
const index = imgUrls.findIndex((url) => url === imgUrl);
129+
const index = imageUrls.findIndex((url) => url === imgUrl);
111130
const mimeType = imageAttachments[index]?.type;
112-
setPreviewImage({ open: true, urls: imgUrls, index, mimeType });
131+
setPreviewImage({ open: true, urls: imageUrls, index, mimeType });
113132
};
114133

115134
return (

0 commit comments

Comments
 (0)