aboutsummaryrefslogtreecommitdiffstats
path: root/src/recipient-search.js
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-09-18 23:12:18 -0700
committerAdam Soltys <[email protected]>2026-09-18 23:12:18 -0700
commit703ee6e1281e72ec00f65768d7de7a015c242abb (patch)
tree8a643d0f1ad6d6e49db210aec45341a7b119e5ab /src/recipient-search.js
parent9e0f9528cd6726836386658c4f38ba8a42bea3ac (diff)
downloadcoinosv3-703ee6e1281e72ec00f65768d7de7a015c242abb.tar.gz
coinosv3-703ee6e1281e72ec00f65768d7de7a015c242abb.zip
Accounts from the header, snapshot-first Spending restore, name ownership grants.
Header buttons always leave the current feature screen; an account with no remembered nostr identity opens Accounts instead of nothing. Logout confirms in place. A scan finishing after logout or an account switch no longer drops the new session into offline mode (stateGeneration guards in sync + ark). Spending restores from the snapshot first. A migration grant names the identity that owns an address; its manager may register it without becoming that identity (names/ownership.js). Browser tests for account access, profile retry, spending restore, thread reply focus, custom-emoji reactions and registry ownership. Co-Authored-By: Claude Fable 5.1 <[email protected]>
Diffstat (limited to 'src/recipient-search.js')
-rw-r--r--src/recipient-search.js24
1 files changed, 15 insertions, 9 deletions
diff --git a/src/recipient-search.js b/src/recipient-search.js
index be3c70e..e0339ca 100644
--- a/src/recipient-search.js
+++ b/src/recipient-search.js
@@ -16,23 +16,28 @@ import { queryOn, parseNostrPubkey, npubOf, PROFILE_RELAYS } from './nostr.js';
// Self-hosted (dist/punks) — coinos.io's own copies 502 for a third of the
// set. Same deterministic formula the coinos app uses.
const punkN = (pk) => Math.floor((parseInt(pk.slice(-2), 16) / 256) * 64) + 1;
-export const punkUrl = (pk) => `punks/${punkN(pk)}.webp`;
+export const punkImageUrl = (n, small = false) =>
+ `${typeof location !== 'undefined' && location.protocol === 'file:' ? '' : '/'}punks${small ? '-sm' : ''}/${n}.webp`;
+export const punkUrl = (pk) => punkImageUrl(punkN(pk));
// The art is 240px, which is what a punk published as someone's nostr
// picture should be — and eight times what a 30px circle needs. We ship a
// 128px copy of each (a fifth of the bytes, 20KB → 4KB) and paint the circles
// from that, so a brand-new wallet with nothing cached fills its faces in
// straight away. Only the 64px profile avatar takes the full-size one.
-export const punkSmallUrl = (pk) => `punks-sm/${punkN(pk)}.webp`;
-export const punkSmallN = (n) => `punks-sm/${n}.webp`;
+export const punkSmallUrl = (pk) => punkImageUrl(punkN(pk), true);
+export const punkSmallN = (n) => punkImageUrl(n, true);
-export const fallbackAvatar = (h, pk, name, cls) =>
- h('div', { class: cls + ' fallback' },
- h('img', {
+const failedPunks = new Map();
+export const fallbackAvatar = (h, pk, name, cls) => {
+ const src = cls.includes('profile-avatar') ? punkUrl(pk) : punkSmallUrl(pk);
+ return h('div', { class: cls + ' fallback' },
+ Date.now() - (failedPunks.get(src) || 0) < 60_000 ? null : h('img', {
class: 'punk', alt: '',
- src: cls.includes('profile-avatar') ? punkUrl(pk) : punkSmallUrl(pk),
- onError: (e) => { e.target.style.display = 'none'; },
+ src,
+ onError: (e) => { failedPunks.set(src, Date.now()); e.target.remove(); },
}),
(name || npubOf(pk) || '??').slice(0, 2));
+};
const REGISTRAR = 'https://names.coinos.io';
const SEARCH_RELAYS = ['wss://search.nos.today', 'wss://relay.nostr.band'];
@@ -103,7 +108,8 @@ export function warmSearch() {
// Caches survive reloads: a name searched yesterday paints instantly today.
// Profiles cap at 200 by recency; whole-query results keep for 12 hours.
const PROF_KEY = 'btc-wallet-search-profiles';
-const QUERY_KEY = 'btc-wallet-search-queries';
+// Discard old results that identified migrated accounts by their wallet key.
+const QUERY_KEY = 'btc-wallet-search-queries-v2';
const QUERY_TTL = 12 * 3600_000;
const profileCache = new Map(); // pk -> { name, picture }
const queryCache = new Map(); // q -> rows