diff --git a/web/src/components/AttachmentLibrary/AttachmentMediaGrid.tsx b/web/src/components/AttachmentLibrary/AttachmentMediaGrid.tsx index 37cf475e..6c7463cc 100644 --- a/web/src/components/AttachmentLibrary/AttachmentMediaGrid.tsx +++ b/web/src/components/AttachmentLibrary/AttachmentMediaGrid.tsx @@ -4,7 +4,7 @@ import { Badge } from "@/components/ui/badge"; import VideoPoster from "@/components/VideoPoster"; import type { AttachmentLibraryMediaItem, AttachmentLibraryMonthGroup } from "@/hooks/useAttachmentLibrary"; import { useTranslate } from "@/utils/i18n"; -import { AttachmentMetadataLine, AttachmentOpenButton } from "./AttachmentLibraryPrimitives"; +import { AttachmentMetadataLine, AttachmentOpenButton, AttachmentSourceChip } from "./AttachmentLibraryPrimitives"; interface AttachmentMediaGridProps { groups: AttachmentLibraryMonthGroup[]; @@ -60,12 +60,12 @@ const AttachmentMediaCard = ({ item, onPreview }: { item: AttachmentLibraryMedia )} +
- - +
diff --git a/web/src/hooks/useAttachmentLibrary.ts b/web/src/hooks/useAttachmentLibrary.ts index e475b93b..a9679c59 100644 --- a/web/src/hooks/useAttachmentLibrary.ts +++ b/web/src/hooks/useAttachmentLibrary.ts @@ -37,6 +37,7 @@ export interface AttachmentLibraryMediaItem extends AttachmentVisualItem { createdAt?: Date; createdLabel: string; fileTypeLabel: string; + memoName?: string; } export interface AttachmentLibraryMonthGroup { @@ -96,6 +97,7 @@ const toLibraryMediaItem = (item: AttachmentVisualItem, locale: string, livePhot createdAt, createdLabel: formatCreatedAt(createdAt, locale), fileTypeLabel: item.kind === "motion" ? livePhotoLabel : fileTypeLabel, + memoName: primaryAttachment.memo, }; }; diff --git a/web/tests/attachment-library-source-link.test.tsx b/web/tests/attachment-library-source-link.test.tsx new file mode 100644 index 00000000..2f961969 --- /dev/null +++ b/web/tests/attachment-library-source-link.test.tsx @@ -0,0 +1,65 @@ +import { create } from "@bufbuild/protobuf"; +import { fireEvent, render, screen } from "@testing-library/react"; +import { MemoryRouter } from "react-router-dom"; +import { describe, expect, it, vi } from "vitest"; +import AttachmentMediaGrid from "@/components/AttachmentLibrary/AttachmentMediaGrid"; +import type { AttachmentLibraryMonthGroup } from "@/hooks/useAttachmentLibrary"; +import { AttachmentSchema } from "@/types/proto/api/v1/attachment_service_pb"; + +vi.mock("@/utils/i18n", () => ({ useTranslate: () => (key: string) => key })); + +describe("", () => { + it("links a media attachment to its source memo without replacing the direct file action", () => { + const attachment = create(AttachmentSchema, { + name: "attachments/photo", + filename: "photo.jpg", + type: "image/jpeg", + memo: "memos/source-memo", + }); + const groups: AttachmentLibraryMonthGroup[] = [ + { + key: "2026-07", + label: "Jul 2026", + items: [ + { + id: attachment.name, + kind: "image", + filename: attachment.filename, + posterUrl: "/file/attachments/photo/photo.jpg?thumbnail=1", + sourceUrl: "/file/attachments/photo/photo.jpg", + attachmentNames: [attachment.name], + attachments: [attachment], + previewItem: { + id: attachment.name, + kind: "image", + filename: attachment.filename, + sourceUrl: "/file/attachments/photo/photo.jpg", + }, + mimeType: attachment.type, + primaryAttachment: attachment, + createdLabel: "Jul 29, 2026", + fileTypeLabel: "JPEG", + memoName: attachment.memo, + }, + ], + }, + ]; + const onPreview = vi.fn(); + + render( + + + , + ); + + const memoLink = screen.getByRole("link", { name: "attachment-library.labels.memo" }); + expect(memoLink).toHaveAttribute("href", "/memos/source-memo"); + + const fileLink = screen.getByRole("link", { name: "attachment-library.actions.open" }); + expect(fileLink).toHaveAttribute("href", "/file/attachments/photo/photo.jpg"); + expect(fileLink).toHaveAttribute("target", "_blank"); + + fireEvent.click(memoLink); + expect(onPreview).not.toHaveBeenCalled(); + }); +});