mirror of
https://github.com/block/buzz.git
synced 2026-10-31 14:31:02 +00:00
🤖 ## Summary Every Buzz account is identified by a long public key. Before this change, when someone had no display name, surfaces fell back to inconsistent labels — mostly raw hex fragments like `abcd1234…wxyz`, sometimes a generic role label with no key — so the same person looked different from surface to surface, and nothing looked like an npub address. This PR applies the npub identity foundation from #7488 to the everyday surfaces: a person without a display name now falls back to the same compact npub everywhere — `npub1xxxx…yyyy`, the human-readable spelling of their public key (first 8 + last 4 characters of the full npub) — across messages and mentions, reactions, huddles, member and participant lists, the sidebar and channel activity, search, projects, tray, notifications, and workflow surfaces. - **Mentions and messages**: key-only mention chips render the compact npub. Pasting a copied mention back still re-binds it byte-exactly to the identity it declares, for both the new npub chips and legacy hex-truncated chips copied by older clients — wrong, missing, or tampered key qualification is rejected instead of silently degrading to plain text. - **Reactions and huddles**: huddle reaction events and the huddle roster/participants render the compact npub for unnamed participants; workflow reaction triggers describe authors with the same form. - **Members and sidebar**: channel and community member lists, add-member results and invites, the members sidebar, the channel-activity popover, search, projects (assignees/reviewers/PR panels), the tray menu, and desktop notifications all fall back to the compact npub; titles and aria labels keep the machine-readable full labels. - **Profile labels**: panel/popover display names and owner handles fall back to the compact npub (never raw hex) when there is no name; linked-event (nevent) message metadata shows the npub-shaped author fallback while the event lookup and event IDs are unchanged. - **Workflows**: author-picker secondary labels, step destination keys, and trigger-author references render compact npubs; event and blob IDs keep their existing hex compacts (they are not identities). - **Avatars stay distinct**: fallback avatars for key-only identities derive initials from the key's tail, so prefixed role labels like "Participant npub1…" no longer collapse every unnamed participant onto the same initials; people with names keep their name initials. Preserved exactly: display names and distinct avatars, internal hex keys (storage/API forms unchanged), clipboard identity roundtrips, event/blob ID compaction, private keys (no nsec path is touched), and nevent link handling. Scope: this PR changes what identity labels **display**, not identity controls — profile/settings copy controls, the respond-to allowlist, workflow key fields, and agent dialogs are the sibling slice #7489, and the shared primitives (`canonicalNpub`, `truncateNpub`, the `<PubKey>` gate, strict input parsing) come from the foundation #7488. ### Related issue - Fixes: N/A. Searched existing issues/PRs for duplicates — none found; the related work is the npub identity stack this slice belongs to. - Base/dependency: stacks on #7488 (foundation) — this PR does not stand alone on main. - #7489 is a sibling slice on the same #7488 base (profile/agent/workflow controls), not a dependency: this PR does not require #7489, and #7489 does not require this PR — both only require #7488. ### Testing At exact head `4763cbeae1dd521309755e6d61f657324cb98667` (base: `fix/desktop-npub-identity-d1a` @ `5f3a4a8111998c8aa41ad77cf66992bd1c85343c`; 71 files, +656/−189 — production +277/−136, test support +379/−53): - At this head: targeted `mentions.spec.ts` (1/1), the e2e build, typecheck, and biome — green. - 9 changed/related unit files: 100/100 green; typecheck, e2e build, biome, and px text/truncation checks clean; huddle-roster focused run green; channel-activity e2e 11/11; mutation checks confirm the fallback wiring (removing it collapses shared initials and drops fallback rows). - Known pre-existing local e2e failures, unchanged by this PR and reproduced identically at the upstream merge-base: huddle-transcription voice-menu attribution (25 pass / 1 fail) and the `workflow-local-controls` 438px caret drift. Not claimed green locally. - Update at head `236af9e6137386737e84d3a474d6bc808a704c50` (test-only follow-ups `1143af345` + `236af9e6`): the `workflow-local-controls` races were fixed in the test drivers, and the 438px diff was shown to be a stale Darwin snapshot baseline (name-row enable switch already absent and `message_posted` already MessageSquare at recording commit `9390e11c9`) and refreshed — the focused screenshot test, including keyboard/caret assertions, now passes locally (twice). The full spec was not rerun after the snapshot refresh; the huddle-transcription item above is unchanged. Label/copy text changes are asserted by the e2e specs (`mentions`, `mention-recipients`, `pubkey-display-screenshots`, `huddle-transcription`, `channel-activity-popover`, `workflow-local-controls`) rather than new screenshots; the screenshot spec pins the compact npub text forms. ### Task provenance Buzz channel: `1f0e4a3d-7e01-4efe-bb16-843b357f85c9` Task: buzz://message?channel=1f0e4a3d-7e01-4efe-bb16-843b357f85c9&id=86b34eb4bd84a1472419e9af22636c011c0fe273e3c196f967d7a36996e149b6 --------- Signed-off-by: Logan Johnson <loganj@squareup.com> Co-authored-by: Larry <627498bd4bd1f281a16431e3c6cce3b5c25b6692798c78672298aefbf2f8f8b5@buzz.block.builderlab.xyz>
66 lines
2.2 KiB
JavaScript
66 lines
2.2 KiB
JavaScript
import assert from "node:assert/strict";
|
||
import { describe, it } from "node:test";
|
||
|
||
import { getInitials } from "./initials.ts";
|
||
|
||
describe("getInitials", () => {
|
||
it("filters punctuation before deriving initials", () => {
|
||
assert.equal(getInitials("B (relay)"), "BR");
|
||
});
|
||
|
||
it("handles a leading symbol on a single word", () => {
|
||
assert.equal(getInitials("(staging)"), "S");
|
||
});
|
||
|
||
it("still returns plain initials for normal names", () => {
|
||
assert.equal(getInitials("Bravo Beta"), "BB");
|
||
});
|
||
|
||
it("returns empty for a symbol-only name", () => {
|
||
assert.equal(getInitials("()"), "");
|
||
});
|
||
|
||
it("derives key-label initials from the compact key’s visible tail, not the npub prefix", () => {
|
||
// Compact npub labels every start with npub1: the tail is the only
|
||
// fragment that distinguishes one key-identified identity from another.
|
||
assert.equal(getInitials("npub1z59…zwkg"), "ZW");
|
||
assert.equal(getInitials("npub1m6k…zuz0"), "ZU");
|
||
});
|
||
|
||
it("derives full-npub initials from the same tail fragment", () => {
|
||
assert.equal(
|
||
getInitials(
|
||
"npub1z59jp0d24242424242424242424242424242424242424242zhwqnlzwkg",
|
||
),
|
||
"ZW",
|
||
);
|
||
});
|
||
|
||
it("leaves authored names that merely resemble npubs on the name path", () => {
|
||
// Not a key-shaped label: wrong lengths, separators, or alphabet must
|
||
// keep the ordinary name derivation so an authored name is never
|
||
// re-derived as a key just for resembling one.
|
||
for (const [label, expected] of [
|
||
["Npub1 Person", "NP"],
|
||
["npub1cool handle", "NH"],
|
||
// Compact-label shape with a missing data character.
|
||
["npub1ab…wxy", "NW"],
|
||
// Compact-label shape over letters outside the bech32 alphabet.
|
||
["npub1bio…biob", "NB"],
|
||
]) {
|
||
assert.equal(getInitials(label), expected);
|
||
}
|
||
});
|
||
|
||
it("requires a checksum-valid npub before deriving key-tail initials", () => {
|
||
// Same length and alphabet as a real npub, but the checksum does not
|
||
// decode: an authored lookalike must stay on the name path.
|
||
assert.equal(
|
||
getInitials(
|
||
"npub1z59jp0d24242424242424242424242424242424242424242zhwqnlzwkq",
|
||
),
|
||
"N",
|
||
);
|
||
});
|
||
});
|