Merge pull request #643 from PR0M3TH3AN/codex/use-abbreviated-npub-format-everywhere

Use consistent short npub display
This commit is contained in:
thePR0M3TH3AN authored and GitHub committed 2025-10-18 09:52:32 -04:00
commit 0eb691b3fb
5 files changed
+110 -19

No files matched your search

+6 -7
View File
@@ -62,6 +62,7 @@ import {
formatAbsoluteTimestamp as formatAbsoluteTimestampUtil,
formatTimeAgo as formatTimeAgoUtil,
truncateMiddle,
formatShortNpub,
} from "./utils/formatters.js";
import {
escapeHTML as escapeHtml,
@@ -436,6 +437,7 @@ class Application {
formatTimeAgo: (timestamp) => this.formatTimeAgo(timestamp),
escapeHTML: (value) => this.escapeHTML(value),
truncateMiddle,
formatShortNpub: (value) => formatShortNpub(value),
fallbackThumbnailSrc: FALLBACK_THUMBNAIL_SRC,
container: document.getElementById("modalContainer") || null,
});
@@ -456,6 +458,7 @@ class Application {
safeEncodeNpub: (pubkey) => this.safeEncodeNpub(pubkey),
safeDecodeNpub: (npub) => this.safeDecodeNpub(npub),
truncateMiddle: (value, maxLength) => truncateMiddle(value, maxLength),
formatShortNpub: (value) => formatShortNpub(value),
getProfileCacheEntry: (pubkey) => this.getProfileCacheEntry(pubkey),
batchFetchProfiles: (authorSet) => this.batchFetchProfiles(authorSet),
switchProfile: (pubkey) => this.authService.switchProfile(pubkey),
@@ -4504,18 +4507,14 @@ class Application {
if (typeof window !== "undefined" && window.NostrTools?.nip19?.npubEncode) {
const encoded = window.NostrTools.nip19.npubEncode(trimmed);
if (encoded && typeof encoded === "string") {
return `${encoded.slice(0, 8)}…${encoded.slice(-4)}`;
return formatShortNpub(encoded);
}
}
} catch (error) {
userLogger.warn("[Application] Failed to encode reporter npub", error);
}
if (trimmed.length > 12) {
return `${trimmed.slice(0, 8)}…${trimmed.slice(-4)}`;
}
return trimmed;
return formatShortNpub(trimmed);
}
decorateVideoModeration(video) {
@@ -6656,7 +6655,7 @@ class Application {
postedAt: this.currentVideo?.rootCreatedAt ?? null,
editedAt: normalizedEditedAt,
});
const displayNpub = `${creatorNpub.slice(0, 8)}...${creatorNpub.slice(-4)}`;
const displayNpub = formatShortNpub(creatorNpub);
this.videoModal.updateMetadata({
title: video.title || "Untitled",
description: video.description || "No description available.",
+4 -2
View File
@@ -11,6 +11,7 @@ import { attachUrlHealthBadges } from "./urlHealthObserver.js";
import { accessControl } from "./accessControl.js";
import { getApplication } from "./applicationContext.js";
import { escapeHTML } from "./utils/domUtils.js";
import { formatShortNpub } from "./utils/formatters.js";
import createPopover from "./ui/overlay/popoverEngine.js";
import { VideoCard } from "./ui/components/VideoCard.js";
import { createChannelProfileMenuPanel } from "./ui/components/videoMenuRenderers.js";
@@ -3109,10 +3110,11 @@ function applyChannelProfileMetadata({
const channelNpubEl = document.getElementById("channelNpub");
if (channelNpubEl) {
if (npub) {
channelNpubEl.textContent = npub;
channelNpubEl.textContent = formatShortNpub(npub);
} else if (pubkey) {
try {
channelNpubEl.textContent = window.NostrTools.nip19.npubEncode(pubkey);
const encoded = window.NostrTools.nip19.npubEncode(pubkey);
channelNpubEl.textContent = formatShortNpub(encoded);
} catch (error) {
channelNpubEl.textContent = "";
}
+39 -10
View File
@@ -89,6 +89,13 @@ const SERVICE_CONTRACT = [
"Utility used for shortening long identifiers in the modal without losing context.",
fallback: () => (value) => value,
},
{
key: "formatShortNpub",
type: "function",
description:
"Formats npub strings for display using the canonical short representation (npubXXXX…XXXX).",
fallback: () => (value) => (typeof value === "string" ? value : ""),
},
{
key: "getProfileCacheEntry",
type: "function",
@@ -617,6 +624,7 @@ export class ProfileModalController {
this.safeEncodeNpub = this.services.safeEncodeNpub;
this.safeDecodeNpub = this.services.safeDecodeNpub;
this.truncateMiddle = this.services.truncateMiddle;
this.formatShortNpub = this.services.formatShortNpub;
this.sendAdminListNotificationService =
typeof this.services.sendAdminListNotification === "function"
? this.services.sendAdminListNotification
@@ -1198,8 +1206,12 @@ export class ProfileModalController {
const activeMeta = activeEntry ? resolveMeta(activeEntry) : null;
const hasActiveProfile = Boolean(activeEntry && activeMeta);
const truncate = this.truncateMiddle || ((value) => value);
const formatNpub =
typeof this.formatShortNpub === "function"
? (value) => this.formatShortNpub(value)
: (value) => (typeof value === "string" ? value : "");
const activeNameFallback = activeMeta?.npub
? truncate(activeMeta.npub, 32)
? formatNpub(activeMeta.npub) || "Saved profile"
: "Saved profile";
const activeDisplayName = hasActiveProfile
? activeMeta?.name?.trim() || activeNameFallback
@@ -1225,7 +1237,8 @@ export class ProfileModalController {
if (this.profileNpub) {
if (hasActiveProfile && activeMeta?.npub) {
this.profileNpub.textContent = truncate(activeMeta.npub, 48);
const displayNpub = formatNpub(activeMeta.npub);
this.profileNpub.textContent = displayNpub || "npub unavailable";
} else if (hasActiveProfile) {
this.profileNpub.textContent = "npub unavailable";
} else {
@@ -1320,7 +1333,7 @@ export class ProfileModalController {
avatarImg.src = meta.picture || FALLBACK_PROFILE_AVATAR;
const cardDisplayName =
meta.name?.trim() ||
(meta.npub ? truncate(meta.npub, 32) : "Saved profile");
(meta.npub ? formatNpub(meta.npub) || "Saved profile" : "Saved profile");
avatarImg.alt = `${cardDisplayName} avatar`;
avatarSpan.appendChild(avatarImg);
@@ -1349,9 +1362,12 @@ export class ProfileModalController {
const npubSpan = document.createElement("span");
npubSpan.className = "break-all font-mono text-xs text-muted";
npubSpan.textContent = meta.npub
? truncate(meta.npub, 48)
: "npub unavailable";
if (meta.npub) {
const displayNpub = formatNpub(meta.npub);
npubSpan.textContent = displayNpub || "npub unavailable";
} else {
npubSpan.textContent = "npub unavailable";
}
metaSpan.append(topLine, nameSpan, npubSpan);
button.append(avatarSpan, metaSpan);
@@ -2740,6 +2756,11 @@ export class ProfileModalController {
const { onRemove, removeLabel = "Remove", confirmMessage, removable = true } =
options;
const formatNpub =
typeof this.formatShortNpub === "function"
? (value) => this.formatShortNpub(value)
: (value) => (typeof value === "string" ? value : "");
listEl.innerHTML = "";
const values = Array.isArray(entries) ? [...entries] : [];
values.sort((a, b) => a.localeCompare(b));
@@ -2760,7 +2781,8 @@ export class ProfileModalController {
const label = document.createElement("p");
label.className = "break-all text-sm font-medium text-primary";
label.textContent = npub;
const displayNpub = formatNpub(npub) || npub;
label.textContent = displayNpub;
item.appendChild(label);
if (removable && typeof onRemove === "function") {
@@ -2770,7 +2792,7 @@ export class ProfileModalController {
removeBtn.textContent = removeLabel;
removeBtn.addEventListener("click", () => {
if (confirmMessage) {
const message = confirmMessage.replace("{npub}", npub);
const message = confirmMessage.replace("{npub}", displayNpub);
if (!window.confirm(message)) {
return;
}
@@ -3395,9 +3417,16 @@ export class ProfileModalController {
const actorDisplay = this.normalizeNpubValue(actorNpub) || fallbackActor;
const isWhitelist = listType === "whitelist";
const formatNpub =
typeof this.formatShortNpub === "function"
? (value) => this.formatShortNpub(value)
: (value) => (typeof value === "string" ? value : "");
const displayTarget = formatNpub(normalizedTarget) || normalizedTarget;
const displayActor = formatNpub(actorDisplay) || actorDisplay;
const introLine = isWhitelist
? `Great news—your npub ${normalizedTarget} has been added to the bitvid whitelist by ${actorDisplay}.`
: `We wanted to let you know that your npub ${normalizedTarget} has been placed on the bitvid blacklist by ${actorDisplay}.`;
? `Great news—your npub ${displayTarget} has been added to the bitvid whitelist by ${displayActor}.`
: `We wanted to let you know that your npub ${displayTarget} has been placed on the bitvid blacklist by ${displayActor}.`;
const statusLine = isWhitelist
? `You now have full creator access across bitvid (${this.bitvidWebsiteUrl}).`
+22
View File
@@ -20,6 +20,28 @@ export function truncateMiddle(text, maxLength = 72) {
return `${text.slice(0, front)}${ellipsis}${text.slice(text.length - back)}`;
}
/**
* Formats an npub string using the canonical short display format used across
* the app (first 8 characters, ellipsis, last 4 characters). When the input is
* not an npub or is already short, the original value is returned.
*/
export function formatShortNpub(value) {
if (typeof value !== "string") {
return "";
}
const trimmed = value.trim();
if (!trimmed) {
return "";
}
if (!trimmed.startsWith("npub") || trimmed.length <= 12) {
return trimmed;
}
return `${trimmed.slice(0, 8)}...${trimmed.slice(-4)}`;
}
/**
* Formats a timestamp (in seconds) to a localized string. Falls back to
* ISO-8601 formatting if locale formatting fails.
+39
View File
@@ -3,6 +3,7 @@ import { strict as assert } from 'node:assert';
import { readFile } from 'node:fs/promises';
import { JSDOM } from 'jsdom';
import ProfileModalController from '../js/ui/profileModalController.js';
import { formatShortNpub } from '../js/utils/formatters.js';
import { resetRuntimeFlags } from '../js/constants.js';
import { applyDesignSystemAttributes } from '../js/designSystem.js';
@@ -289,6 +290,44 @@ for (const _ of [0]) {
});
},
);
test('Profile modal uses abbreviated npub display', async () => {
const sampleProfiles = [
{
pubkey: defaultActorHex,
npub: 'npub1abcdefghijkmnopqrstuvwxyz1234567890example',
name: '',
picture: '',
},
{
pubkey: 'b'.repeat(64),
npub: 'npub1zyxwvutsrqponmlkjihgfedcba1234567890sample',
name: '',
picture: '',
},
];
const controller = createController({
services: {
formatShortNpub,
},
});
await controller.load();
controller.state.setSavedProfiles(sampleProfiles);
controller.state.setActivePubkey(sampleProfiles[0].pubkey);
controller.renderSavedProfiles();
const expectedActive = formatShortNpub(sampleProfiles[0].npub);
assert.equal(controller.profileNpub.textContent, expectedActive);
const switcherNpubEl = controller.switcherList.querySelector('.font-mono');
assert.ok(switcherNpubEl, 'switcher should render the secondary profile');
const expectedSwitcher = formatShortNpub(sampleProfiles[1].npub);
assert.equal(switcherNpubEl.textContent, expectedSwitcher);
});
}
test('load() injects markup and caches expected elements', async () => {