From 70f8866007f8658a3e6b9cd9ba80229b70fd9252 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Fri, 9 Oct 2026 14:42:15 -0700 Subject: Follow packs: a Draftable (mutable.top) link pasted into the feed editor's pack search finds its pack. Draftable makes ordinary kind 39089 packs — the pack index already finds them by title on damus / nos.lol / primal — but its links name the author by only the first eight hex (/draftable/d/?p=), which the link parser refused; the id is now asked for and the author matched by prefix. nostr.oxtr.dev (one of Draftable's relays, and the one holding the most packs) joins the pack index. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Vps2H27rhbG9ZqseGfMrc3 --- src/features/messages.js | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'src/features') diff --git a/src/features/messages.js b/src/features/messages.js index 6245c60..6572e27 100644 --- a/src/features/messages.js +++ b/src/features/messages.js @@ -5106,6 +5106,18 @@ export function messagesFeature(ctx) { if (ref.kind === LIST_KIND) return { pk: ref.pk, d: ref.d, relays: ref.relays || [], kind: LIST_KIND }; return null; } + // Draftable (mutable.top) links name a pack by its id and only the first + // eight hex of its author: /draftable/d/?p=. The id is asked + // for and the author matched by that prefix. + const PACK_INDEX_RELAYS = () => [...new Set([...PACK_RELAYS, 'wss://relay.damus.io', 'wss://nos.lol', 'wss://nostr.oxtr.dev'])]; + async function resolveShortPackLink(input) { + const m = /\/d\/([^/?#\s]+)[^?\s]*\?(?:[^#\s]*&)?p=([0-9a-f]{4,63})(?![0-9a-f])/i.exec(String(input || '')); + if (!m) return null; + const d = decodeURIComponent(m[1]), prefix = m[2].toLowerCase(); + const evs = await queryOn(PACK_INDEX_RELAYS(), { kinds: [PACK_KIND], '#d': [d], limit: 50 }, 5000).catch(() => []); + const ev = evs.filter((e) => e.kind === PACK_KIND && e.pubkey.startsWith(prefix)).sort((a, b) => b.created_at - a.created_at)[0]; + return ev ? { pk: ev.pubkey, d, relays: [] } : null; + } // Every pack the wide relays hold, once per session: the way to find one // by a few letters of its title — NIP-50 search only knows the packs the // search relay happens to carry, and most live on damus / nos.lol. @@ -5113,7 +5125,7 @@ export function messagesFeature(ctx) { function loadPackIndex() { if (packIndex) return packIndex; packIndex = (async () => { - const relays = [...new Set([...PACK_RELAYS, 'wss://relay.damus.io', 'wss://nos.lol'])]; + const relays = PACK_INDEX_RELAYS(); const all = await Promise.all(relays.map((r) => queryOn([r], { kinds: [PACK_KIND], limit: 500 }, 6000).catch(() => []))); const newest = new Map(); for (const ev of all.flat()) { @@ -11694,7 +11706,8 @@ export function messagesFeature(ctx) { const seq = ++packSearchSeq; q = String(q || '').trim(); if (!e || q.length < 2) { if (e) e.packRows = null; render(); return; } - const link = parsePackLink(q); + const link = parsePackLink(q) || await resolveShortPackLink(q); + if (seq !== packSearchSeq) return; let rows = []; if (link) { const got = await fetchPack(link).catch(() => null); -- cgit v1.2.3