From 5f7c4eea62c8906bfa3db4f8bba44b71c3b3ae6e Mon Sep 17 00:00:00 2001 From: TerryM Date: Sat, 30 May 2026 18:08:41 +0800 Subject: [PATCH] fix: show recommended card thumbnails --- src/utils/postResourceAdapter.test.ts | 93 +++++++++++++++++++++++++++ src/utils/postResourceAdapter.ts | 14 ++-- 2 files changed, 101 insertions(+), 6 deletions(-) create mode 100644 src/utils/postResourceAdapter.test.ts diff --git a/src/utils/postResourceAdapter.test.ts b/src/utils/postResourceAdapter.test.ts new file mode 100644 index 0000000..d024c81 --- /dev/null +++ b/src/utils/postResourceAdapter.test.ts @@ -0,0 +1,93 @@ +import { describe, expect, it } from "vitest"; +import type { Post } from "../types/post"; +import { postToResource } from "./postResourceAdapter"; + +const basePost: Post = { + id: "post-1", + postType: "link", + categoryId: 1, + categorySlug: "official-announcement", + language: "zh", + title: "Link post", + text: "https://example.com/article", + attachments: [], + isRecommended: true, + publishedAt: "2026-05-30T00:00:00Z", +}; + +describe("postToResource", () => { + it("uses link preview images as the resource cover when there is no attachment", () => { + const resource = postToResource( + { + ...basePost, + linkPreview: { + url: "https://example.com/article", + canonicalUrl: "https://example.com/article", + siteName: "Example", + title: "Example article", + description: "Example description", + imageUrl: "https://example.com/preview.jpg", + }, + }, + "zh-TW", + ); + + expect(resource.coverImage).toBe("https://example.com/preview.jpg"); + expect(resource.previewUrl).toBe("https://example.com/preview.jpg"); + }); + + it("uses document thumbnails before poster URLs", () => { + const resource = postToResource( + { + ...basePost, + postType: "pdf", + attachments: [ + { + id: "att-1", + kind: "document", + url: "/uploads/file.pdf", + mime: "application/pdf", + filename: "file.pdf", + sizeBytes: 123, + posterUrl: "/uploads/file.pdf", + thumbnailUrl: "/uploads/thumb.jpg", + }, + ], + }, + "zh-TW", + ); + + expect(resource.coverImage).toBe("/uploads/thumb.jpg"); + expect(resource.previewUrl).toBe("/uploads/thumb.jpg"); + }); + + it("keeps an attachment thumbnail ahead of a link preview image", () => { + const resource = postToResource( + { + ...basePost, + attachments: [ + { + id: "att-1", + kind: "image", + url: "/uploads/full.jpg", + mime: "image/jpeg", + filename: "full.jpg", + sizeBytes: 123, + thumbnailUrl: "/uploads/thumb.jpg", + }, + ], + linkPreview: { + url: "https://example.com/article", + canonicalUrl: "https://example.com/article", + siteName: "Example", + title: "Example article", + description: "Example description", + imageUrl: "https://example.com/preview.jpg", + }, + }, + "zh-TW", + ); + + expect(resource.coverImage).toBe("/uploads/thumb.jpg"); + }); +}); diff --git a/src/utils/postResourceAdapter.ts b/src/utils/postResourceAdapter.ts index aa850d0..de2466e 100644 --- a/src/utils/postResourceAdapter.ts +++ b/src/utils/postResourceAdapter.ts @@ -24,12 +24,13 @@ function inferType(post: Post, att: Attachment | undefined): string { return "text"; } -function coverFor(att: Attachment | undefined) { - if (!att) return ""; +function coverFor(post: Post, att: Attachment | undefined) { + const linkPreviewImage = post.linkPreview?.imageUrl || ""; + if (!att) return linkPreviewImage; if (att.kind === "image" || att.mime.startsWith("image/")) { - return att.thumbnailUrl || att.url; + return att.thumbnailUrl || att.url || linkPreviewImage; } - return att.posterUrl || att.thumbUrl || att.thumbnailUrl || ""; + return att.thumbnailUrl || att.posterUrl || att.thumbUrl || linkPreviewImage; } export function postToResource( @@ -49,9 +50,10 @@ export function postToResource( categoryId: post.categoryId, categorySlug: post.categorySlug, categoryName: category?.name || post.categorySlug, - coverImage: coverFor(first), + coverImage: coverFor(post, first), fileUrl: first?.url, - previewUrl: first?.posterUrl || first?.thumbnailUrl, + previewUrl: + first?.thumbnailUrl || first?.posterUrl || post.linkPreview?.imageUrl, externalUrl: undefined, bodyText: postDisplayText(post, lang), badgeLabel: post.isRecommended ? "Recommended" : undefined,