feat(desktop): show the adder's avatar in the catalog "Added by" line

The catalog detail header named the entry's publisher but showed no
face. Resolve the publisher's profile picture (or the viewer's own for
"Added by You") and render it inline before the name.

Signed-off-by: Thomas Petersen <thomasp@squareup.com>
This commit is contained in:
Thomas Petersen
2026-08-04 22:52:49 +02:00
parent d3f3958a5d
commit 3ebda6fd47
2 changed files with 34 additions and 14 deletions

View File

@@ -1,18 +1,32 @@
import { ProfileAvatar } from "@/features/profile/ui/ProfileAvatar";
import { cn } from "@/shared/lib/cn";
type PersonaAddedByProps = {
/** The adder's profile picture; omitted while the profile resolves. */
avatarUrl?: string | null;
className?: string;
label?: string;
};
export function PersonaAddedBy({
avatarUrl,
className,
label = "You",
}: PersonaAddedByProps) {
return (
<p className={cn("truncate text-xs leading-tight", className)}>
<span className="text-muted-foreground/55">Added by</span>{" "}
<span className="text-muted-foreground">{label}</span>
<p
className={cn(
"flex min-w-0 items-center gap-1 text-xs leading-tight",
className,
)}
>
<span className="shrink-0 text-muted-foreground/55">Added by</span>
<ProfileAvatar
avatarUrl={avatarUrl ?? null}
className="h-3.5 w-3.5 shrink-0 text-3xs"
label={label}
/>
<span className="truncate text-muted-foreground">{label}</span>
</p>
);
}

View File

@@ -4,6 +4,7 @@ import { isCatalogPersonaSelected } from "@/features/agents/lib/catalog";
import { isCatalogPersona } from "@/features/agents/lib/personaCatalogRelay";
import { useUsersBatchQuery } from "@/features/profile/hooks";
import { ProfileAvatar } from "@/features/profile/ui/ProfileAvatar";
import { useIdentityQuery } from "@/shared/api/hooks";
import type { AgentPersona } from "@/shared/api/types";
import { useFeedbackToasts } from "@/shared/hooks/useToastEffect";
import { cn } from "@/shared/lib/cn";
@@ -306,22 +307,23 @@ function PersonaCatalogDetail({
}) {
const isCommunityEntry =
isCatalogPersona(persona) && !persona.catalogSource.isOwn;
const identityQuery = useIdentityQuery();
// Community entries resolve their publisher's profile; own/local entries
// resolve the viewer's, so "Added by You" carries the viewer's avatar.
const ownerPubkey = isCommunityEntry
? persona.catalogSource.ownerPubkey
: undefined;
: identityQuery.data?.pubkey;
const ownerBatchQuery = useUsersBatchQuery(ownerPubkey ? [ownerPubkey] : [], {
enabled: !!ownerPubkey,
});
const ownerSummary = ownerPubkey
? ownerBatchQuery.data?.profiles[ownerPubkey.toLowerCase()]
: undefined;
let addedByLabel: string;
if (!isCommunityEntry) {
addedByLabel = "You";
} else {
const summary = ownerPubkey
? ownerBatchQuery.data?.profiles[ownerPubkey.toLowerCase()]
: undefined;
addedByLabel = resolveCatalogOwnerLabel(summary);
}
const addedByLabel = isCommunityEntry
? resolveCatalogOwnerLabel(ownerSummary)
: "You";
const addedByAvatarUrl = ownerSummary?.avatarUrl ?? null;
return (
<div className="w-full min-w-0 max-w-full space-y-6 overflow-x-hidden">
@@ -336,7 +338,11 @@ function PersonaCatalogDetail({
{persona.displayName}
</h3>
{persona.isBuiltIn ? null : (
<PersonaAddedBy className="mt-0.5" label={addedByLabel} />
<PersonaAddedBy
avatarUrl={addedByAvatarUrl}
className="mt-0.5"
label={addedByLabel}
/>
)}
</div>
</div>