Normalize forum URLs as nostr events and rendered them as titles

This commit is contained in:
dtonon 2026-06-02 20:25:20 +01:00
parent fc51e33ceb
commit 2dc823d055
9 changed files with 262 additions and 57 deletions

View file

@ -1,4 +1,5 @@
import * as nip19 from "@nostr/tools/nip19";
import { RELAY_URL } from "./config";
// Curated TLD list: gTLDs, popular new gTLDs, common ccTLDs
const TLDS = [
@ -107,7 +108,14 @@ export type ChatToken =
| { type: "text"; value: string }
| { type: "link"; href: string; label: string }
| { type: "mention"; pubkey: string; entity: string; fallback: string }
| { type: "entity"; entity: string; href: string; label: string };
| {
type: "entity";
entity: string;
href: string;
label: string;
// Underlying event id for note/nevent — used to resolve a forum thread.
id?: string;
};
// Shorten a nostr entity to xxxxxxxx...xxxx
export function shortNostrEntity(entity: string): string {
@ -145,7 +153,15 @@ function decodeEntity(entity: string): ChatToken | null {
entity,
fallback: shortNostrEntity(entity),
};
if (decoded.type === "note" || decoded.type === "nevent" || decoded.type === "naddr")
if (decoded.type === "note" || decoded.type === "nevent")
return {
type: "entity",
entity,
href: `https://njump.me/${entity}`,
label: shortNostrEntity(entity),
id: decoded.type === "note" ? decoded.data : decoded.data.id,
};
if (decoded.type === "naddr")
return {
type: "entity",
entity,
@ -193,3 +209,29 @@ export function tokenizeChat(content: string): ChatToken[] {
out.push({ type: "text", value: content.slice(last) });
return out;
}
function escapeRegExp(s: string): string {
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
// Rewrite same-origin forum thread URLs into nostr entities before publishing,
// so the reference is portable to other Nostr clients. A `#post-<id>` fragment
// targets the reply (kind 1111); otherwise the thread itself (kind 11). The
// author is omitted; the parent is recovered from the reply's own tags on read.
export function convertForumUrls(content: string): string {
if (typeof window === "undefined") return content;
const re = new RegExp(
`${escapeRegExp(window.location.origin)}/thread/([0-9a-f]{64})\\/?(?:#post-([0-9a-f]{64}))?`,
"g",
);
return content.replace(re, (whole, threadId, replyId) => {
try {
const nevent = replyId
? nip19.neventEncode({ id: replyId, kind: 1111, relays: [RELAY_URL] })
: nip19.neventEncode({ id: threadId, kind: 11, relays: [RELAY_URL] });
return `nostr:${nevent}`;
} catch {
return whole;
}
});
}