From d512443d0051ef639aa59c9b738bfc4cc38ef742 Mon Sep 17 00:00:00 2001 From: tangsongdayo Date: Sun, 19 Jul 2026 13:35:20 +0800 Subject: [PATCH] feat: show source-qualified author names --- CHANGELOG.md | 4 ++++ app/components/post-card.tsx | 3 ++- lib/display.ts | 16 ++++++++++++++++ tests/display.test.ts | 15 +++++++++++++++ 4 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 lib/display.ts create mode 100644 tests/display.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index eea3f24..d104246 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ - Constrained remote Memos avatars to a fixed, cropped size so oversized source images cannot break dashboard or source-page layouts. +### Changed + +- Changed homepage author labels for Memos and Hub-to-Memos posts from duplicated source names to `@name@source-hostname` identities. + ## [0.8.0] - 2026-07-19 ### Added diff --git a/app/components/post-card.tsx b/app/components/post-card.tsx index daba735..4e22512 100644 --- a/app/components/post-card.tsx +++ b/app/components/post-card.tsx @@ -2,10 +2,11 @@ import Link from "next/link"; import { Attachments } from "./attachments"; import { Markdown } from "./markdown"; import { canonicalTags } from "@/lib/tags"; +import { postAuthorLabel } from "@/lib/display"; export type PublicPost = { id: number; source_id: number | null; origin: string; content: string; tags_json: string; attachments_json: string; created_at: string; remote_created_at?: string | null; username: string; name: string | null; remote_display_name?: string | null; source_base_url: string | null; comment_count: number; reaction_count: number }; export function PostCard({ post }: { post: PublicPost }) { let tags: string[] = []; try { tags = canonicalTags(JSON.parse(post.tags_json)); } catch { /* Ignore malformed legacy tags. */ } - const publishedAt = post.remote_created_at || post.created_at; const author = post.origin === "memos" ? (post.remote_display_name || post.name || post.username) : (post.name || post.username); return
@{author}{post.name ? ` · ${post.name}` : ""}{new Date(publishedAt).toLocaleString("zh-TW")}
{tags.map((tag) => #{tag})}{post.source_id && 來源}閱讀全文 · 💬 {post.comment_count} 🙂 {post.reaction_count}
; + const publishedAt = post.remote_created_at || post.created_at; return
{postAuthorLabel(post)}{new Date(publishedAt).toLocaleString("zh-TW")}
{tags.map((tag) => #{tag})}{post.source_id && 來源}閱讀全文 · 💬 {post.comment_count} 🙂 {post.reaction_count}
; } diff --git a/lib/display.ts b/lib/display.ts new file mode 100644 index 0000000..c298f5a --- /dev/null +++ b/lib/display.ts @@ -0,0 +1,16 @@ +export type AuthorDisplay = { + origin: string; + username: string; + name: string | null; + remote_display_name?: string | null; + source_base_url: string | null; +}; + +export function postAuthorLabel(post: AuthorDisplay) { + const linkedMemos = post.origin === "memos" || post.origin === "hub"; + const author = linkedMemos ? (post.remote_display_name || post.name || post.username) : (post.name || post.username); + if (linkedMemos && post.source_base_url) { + try { return `@${author}@${new URL(post.source_base_url).hostname}`; } catch { /* Keep a readable fallback for legacy malformed URLs. */ } + } + return `@${author}${post.name ? ` · ${post.name}` : ""}`; +} diff --git a/tests/display.test.ts b/tests/display.test.ts new file mode 100644 index 0000000..49d03f1 --- /dev/null +++ b/tests/display.test.ts @@ -0,0 +1,15 @@ +import assert from "node:assert/strict"; +import { test } from "node:test"; +import { postAuthorLabel } from "../lib/display"; + +test("formats Memos authors as name at source hostname", () => { + assert.equal(postAuthorLabel({ origin: "memos", username: "hub-user", name: "codex", remote_display_name: "codex", source_base_url: "https://memos.fishking.studio/path" }), "@codex@memos.fishking.studio"); +}); + +test("formats Hub posts linked to Memos with the same source identity", () => { + assert.equal(postAuthorLabel({ origin: "hub", username: "codex", name: "codex", remote_display_name: "codex", source_base_url: "https://memos.fishking.studio" }), "@codex@memos.fishking.studio"); +}); + +test("keeps the existing label for non-Memos posts", () => { + assert.equal(postAuthorLabel({ origin: "hub", username: "codex", name: "Hub", source_base_url: null }), "@Hub · Hub"); +});