mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
fix(identity): clarify verified profile labels
This commit is contained in:
@@ -1,10 +1,16 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
|
||||
import { formatOwnerLabel, profileLookupsEqual } from "./identity.ts";
|
||||
import {
|
||||
formatOwnerLabel,
|
||||
formatVerifiedUserLabel,
|
||||
profileLookupsEqual,
|
||||
resolveUserLabel,
|
||||
} from "./identity.ts";
|
||||
|
||||
const OWNER_PUBKEY =
|
||||
"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";
|
||||
const USER_PUBKEY = "11".repeat(32);
|
||||
|
||||
const summary = (over = {}) => ({
|
||||
displayName: "Ada",
|
||||
@@ -58,6 +64,7 @@ test("profileLookupsEqual: same count, different keys is not equal", () => {
|
||||
test("profileLookupsEqual: a changed field is not equal", () => {
|
||||
for (const field of [
|
||||
"displayName",
|
||||
"verifiedName",
|
||||
"avatarUrl",
|
||||
"nip05Handle",
|
||||
"ownerPubkey",
|
||||
@@ -120,3 +127,29 @@ test("stabiliser: a real profile change swaps the reference (re-render fires)",
|
||||
const held = stabilise({ p1: summary({ displayName: "Grace" }) });
|
||||
assert.equal(held, changed, "must re-stabilise around the new value");
|
||||
});
|
||||
|
||||
test("formats a chosen name followed by the authoritative display name", () => {
|
||||
assert.equal(
|
||||
formatVerifiedUserLabel("Franco", "fsola"),
|
||||
"Franco (fsola)",
|
||||
);
|
||||
});
|
||||
|
||||
test("does not duplicate equal chosen and authoritative names", () => {
|
||||
assert.equal(formatVerifiedUserLabel("fsola", "fsola"), "fsola");
|
||||
});
|
||||
|
||||
test("resolved user labels keep the chosen name first", () => {
|
||||
assert.equal(
|
||||
resolveUserLabel({
|
||||
pubkey: USER_PUBKEY,
|
||||
profiles: {
|
||||
[USER_PUBKEY]: summary({
|
||||
displayName: "Franco",
|
||||
verifiedName: "fsola",
|
||||
}),
|
||||
},
|
||||
}),
|
||||
"Franco (fsola)",
|
||||
);
|
||||
});
|
||||
|
||||
@@ -5,6 +5,20 @@ export type UserProfileLookup = Record<string, UserProfileSummary>;
|
||||
|
||||
export { truncatePubkey };
|
||||
|
||||
export function formatVerifiedUserLabel(
|
||||
chosenName: string | null | undefined,
|
||||
verifiedName: string | null | undefined,
|
||||
): string | null {
|
||||
const chosen = chosenName?.trim();
|
||||
const verified = verifiedName?.trim();
|
||||
|
||||
if (chosen && verified && chosen !== verified) {
|
||||
return `${chosen} (${verified})`;
|
||||
}
|
||||
|
||||
return chosen || verified || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deep-equal two profile lookups by value. Used to stabilise the merged
|
||||
* `messageProfiles` reference at the ChannelScreen boundary: the underlying
|
||||
@@ -120,21 +134,14 @@ export function resolveUserLabel(input: {
|
||||
const profile = getResolvedProfile(pubkey, profiles);
|
||||
const verifiedName = profile?.verifiedName?.trim();
|
||||
const displayName = profile?.displayName?.trim();
|
||||
if (verifiedName) {
|
||||
return verifiedName;
|
||||
}
|
||||
if (displayName) {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
const nip05Handle = profile?.nip05Handle?.trim();
|
||||
if (nip05Handle) {
|
||||
return nip05Handle;
|
||||
}
|
||||
|
||||
const safeFallback = fallbackName?.trim();
|
||||
if (safeFallback) {
|
||||
return safeFallback;
|
||||
const label = formatVerifiedUserLabel(
|
||||
displayName || nip05Handle || safeFallback,
|
||||
verifiedName,
|
||||
);
|
||||
if (label) {
|
||||
return label;
|
||||
}
|
||||
|
||||
return truncatePubkey(pubkey);
|
||||
|
||||
@@ -25,6 +25,7 @@ import { useIsManagedAgent } from "@/features/agent-memory/hooks";
|
||||
import { useIdentityQuery } from "@/shared/api/hooks";
|
||||
import { useAgentWorking } from "@/features/agents/agentWorkingSignal";
|
||||
import {
|
||||
formatVerifiedUserLabel,
|
||||
formatOwnerLabel,
|
||||
ownsAuthorAgent,
|
||||
} from "@/features/profile/lib/identity";
|
||||
@@ -236,13 +237,8 @@ export function UserProfilePopover({
|
||||
managedAgentsQuery.isPending ||
|
||||
usersBatchQuery.isPending);
|
||||
const displayName =
|
||||
profile?.verifiedName ?? profile?.displayName ?? truncatePubkey(pubkey);
|
||||
const profileAlias =
|
||||
profile?.verifiedName &&
|
||||
profile.displayName &&
|
||||
profile.verifiedName !== profile.displayName
|
||||
? profile.displayName
|
||||
: null;
|
||||
formatVerifiedUserLabel(profile?.displayName, profile?.verifiedName) ??
|
||||
truncatePubkey(pubkey);
|
||||
// Owner signal mirrors UserProfilePanel: a declared NIP-OA owner whose agent
|
||||
// runs elsewhere holds no local seckey, so key custody (`isOwner`) alone
|
||||
// wrongly hides the affordance from them — and gating on bot-ness alone shows
|
||||
@@ -539,11 +535,6 @@ export function UserProfilePopover({
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
{profileAlias ? (
|
||||
<p className="mt-0.5 truncate text-xs leading-4 text-muted-foreground">
|
||||
{profileAlias}
|
||||
</p>
|
||||
) : null}
|
||||
{isBotProfile && ownerLabel ? (
|
||||
<p
|
||||
className="mt-0.5 truncate text-xs leading-4 text-muted-foreground"
|
||||
|
||||
@@ -47,6 +47,7 @@ import { CreateChannelDialog } from "@/features/sidebar/ui/CreateChannelDialog";
|
||||
import { SidebarProfileCard } from "@/features/sidebar/ui/SidebarProfileCard";
|
||||
import { SidebarRelayConnectionCard } from "@/features/sidebar/ui/SidebarRelayConnectionCard";
|
||||
import type { useSidebarRelayConnectionCard } from "@/features/sidebar/ui/useSidebarRelayConnectionCard";
|
||||
import { formatVerifiedUserLabel } from "@/features/profile/lib/identity";
|
||||
import {
|
||||
SidebarLoadingContent,
|
||||
useSidebarLoadingShape,
|
||||
@@ -472,13 +473,17 @@ export function AppSidebar({
|
||||
immediate: isSelectedDirectMessage,
|
||||
timeoutMs: 400,
|
||||
});
|
||||
const resolvedProfileDisplayName = formatVerifiedUserLabel(
|
||||
profile?.displayName,
|
||||
profile?.verifiedName,
|
||||
);
|
||||
const { dmChannelLabels, dmParticipantsByChannelId, dmPresenceByChannelId } =
|
||||
useDmSidebarMetadata({
|
||||
currentPubkey,
|
||||
directMessages,
|
||||
enabled: shouldLoadDmMetadata,
|
||||
fallbackDisplayName,
|
||||
profileDisplayName: profile?.verifiedName ?? profile?.displayName,
|
||||
profileDisplayName: resolvedProfileDisplayName,
|
||||
});
|
||||
const sortedDirectMessages = React.useMemo(
|
||||
() =>
|
||||
@@ -498,8 +503,7 @@ export function AppSidebar({
|
||||
streamChannels,
|
||||
});
|
||||
const resolvedDisplayName =
|
||||
profile?.verifiedName?.trim() ||
|
||||
profile?.displayName?.trim() ||
|
||||
resolvedProfileDisplayName ||
|
||||
fallbackDisplayName?.trim() ||
|
||||
"Current identity";
|
||||
const {
|
||||
|
||||
@@ -8,10 +8,14 @@ export function VerifiedBadge({ verifiedName }: { verifiedName: string }) {
|
||||
<TooltipTrigger asChild>
|
||||
<span
|
||||
aria-label={`Verified corporate identity: ${verifiedName}`}
|
||||
className="inline-flex shrink-0 items-center text-blue-500"
|
||||
className="inline-flex shrink-0 items-center"
|
||||
data-testid="verified-corporate-identity"
|
||||
>
|
||||
<BadgeCheck aria-hidden="true" className="h-4 w-4" fill="currentColor" />
|
||||
<BadgeCheck
|
||||
aria-hidden="true"
|
||||
className="h-4 w-4 fill-blue-500 text-white"
|
||||
strokeWidth={3}
|
||||
/>
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="top">
|
||||
|
||||
Reference in New Issue
Block a user