From 686e2d391d3a963535d5deec7043999b9b92b19b Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Fri, 2 Oct 2026 07:48:32 -0700 Subject: Profiles show Following and Followers, each a list you can open MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Under the bio: "116 Following ยท 572 Followers". Tapping either opens the people below it (50 at a time); tapping a person opens their profile. Follows are the person's kind 3, now read from Primal's cache alongside the relays (a list kept only on their outbox relays used to read as nobody). Followers, which no relay counts, come from Primal: the count from user_profile, the list from user_followers, a first screenful fast and the rest after, without reordering what is on screen. Every event taken from Primal is signature-checked in the verify worker first, and the profiles among them fill names and faces the relays lacked. Also translates the post-zap "no address" message. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Dmcrt9KyNDKVWFKnb8BREo --- src/recipient-search.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'src/recipient-search.js') diff --git a/src/recipient-search.js b/src/recipient-search.js index b0a0edc..fdeaeaf 100644 --- a/src/recipient-search.js +++ b/src/recipient-search.js @@ -51,21 +51,25 @@ const MAX_RESULTS = 8; // to cost more than the query itself. Idle for a minute, it closes itself. let pws = null, pwsN = 0, pwsIdle = null; const pwsSubs = new Map(); // sub id -> { rows, finish } -function primalSearch(q, limit = MAX_RESULTS) { +// Any of Primal's cache calls over the shared socket; resolves with every +// event it answered (its own stats kinds included) at EOSE or the timeout. +export function primalCache(cmd, args, timeoutMs = 3500) { return new Promise((resolve) => { const sid = 'rs' + ++pwsN; const rows = []; const finish = () => { clearTimeout(to); pwsSubs.delete(sid); resolve(rows); }; - const to = setTimeout(finish, 3500); + const to = setTimeout(finish, timeoutMs); pwsSubs.set(sid, { rows, finish }); clearTimeout(pwsIdle); pwsIdle = setTimeout(() => { try { pws?.close(); } catch {} pws = null; }, 60_000); - const send = () => { try { pws.send(JSON.stringify(['REQ', sid, { cache: ['user_search', { query: q, limit }] }])); } catch { finish(); } }; + const send = () => { try { pws.send(JSON.stringify(['REQ', sid, { cache: [cmd, args] }])); } catch { finish(); } }; if (pws && pws.readyState === 1) return send(); if (!ensurePws()) return finish(); pws.addEventListener('open', send, { once: true }); }); } +const primalSearch = (q, limit = MAX_RESULTS) => + primalCache('user_search', { query: q, limit }).then((evs) => evs.filter((e) => e.kind === 0)); function ensurePws() { if (pws && pws.readyState <= 1) return true; @@ -75,7 +79,7 @@ function ensurePws() { const m = JSON.parse(ev.data); const sub = pwsSubs.get(m[1]); if (!sub) return; - if (m[0] === 'EVENT' && m[2] && m[2].kind === 0) sub.rows.push(m[2]); + if (m[0] === 'EVENT' && m[2]) sub.rows.push(m[2]); if (m[0] === 'EOSE') sub.finish(); } catch {} }; -- cgit v1.2.3