aboutsummaryrefslogtreecommitdiffstats
path: root/src/features
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-08-18 00:16:10 -0700
committerAdam Soltys <[email protected]>2026-08-18 00:16:10 -0700
commita109c2742de15703d10aa6c30b4af7bd7ebae19b (patch)
tree51cbba5d6da94c6219164470a8656bac5a11d963 /src/features
parentdfb37ac34f5b5aa567fd818784042b3d317e03f5 (diff)
downloadcoinosv3-a109c2742de15703d10aa6c30b4af7bd7ebae19b.tar.gz
coinosv3-a109c2742de15703d10aa6c30b4af7bd7ebae19b.zip
A wallet keeps one name per domain, not one name total: staging and mainnet claims stop clobbering each other, the registrar answers who-am-I per domain, and a custom name claimed on one device replaces the npub placeholder on every other
Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01JtaatFfAbx91Nh3xqKVqKz
Diffstat (limited to 'src/features')
-rw-r--r--src/features/names.js40
1 files changed, 37 insertions, 3 deletions
diff --git a/src/features/names.js b/src/features/names.js
index d3971a4..cf2aa3c 100644
--- a/src/features/names.js
+++ b/src/features/names.js
@@ -55,8 +55,25 @@ export function namesFeature(ctx) {
return j;
}
- const load = () => wallet.loadFeatureState('names', {});
- const save = (st) => wallet.saveFeatureState('names', st);
+ // One name PER DOMAIN: mainnet and staging claims live side by side in the
+ // wallet's state instead of overwriting each other on a network switch.
+ // Legacy flat state ({name, domain, …}) reads/migrates into its domain slot.
+ const loadAll = () => wallet.loadFeatureState('names', {});
+ const load = () => {
+ const all = loadAll();
+ if (all.byDomain) return all.byDomain[DOMAIN()] || {};
+ if (all.name) return (all.domain || 'coinos.io') === DOMAIN() ? all : {};
+ return {};
+ };
+ const save = (st) => {
+ const all = loadAll();
+ const byDomain = all.byDomain || {};
+ if (!all.byDomain && all.name) {
+ byDomain[all.domain || 'coinos.io'] = { name: all.name, domain: all.domain || 'coinos.io', uri: all.uri, offerPk: all.offerPk, updated: all.updated };
+ }
+ byDomain[DOMAIN()] = st;
+ wallet.saveFeatureState('names', { byDomain });
+ };
// Mainnet and mutinynet: staging's play network gets the full payment-
// address experience (claims already registered fine — the record simply
@@ -113,7 +130,7 @@ export function namesFeature(ctx) {
const pk = wallet.nostrPubkey && wallet.nostrPubkey();
if (!pk) return;
const r = await withTimeout(
- fetch(`${REGISTRAR}/pubkey/${pk}`).then((x) => x.json()), 12000, 'registrar');
+ fetch(`${REGISTRAR}/pubkey/${pk}?domain=${encodeURIComponent(DOMAIN())}`).then((x) => x.json()), 12000, 'registrar');
if (r && r.name) save({ ...load(), name: r.name, domain: r.domain || DOMAIN(), uri: r.uri });
} catch (e) { console.warn('names: lookup failed —', e.message); }
}
@@ -143,6 +160,23 @@ export function namesFeature(ctx) {
}
let st = load();
if (!st.name) { await lookupMine(); st = load(); }
+ // A custom name claimed on ANOTHER DEVICE by the login identity beats a
+ // local npub placeholder: the username people know the user by should
+ // show everywhere they sign in, not only where they typed it.
+ if (!st.name || /^npub1/.test(st.name)) {
+ const login = hook('nostrLoginIdentity');
+ if (login && login.pubkey) {
+ try {
+ const r = await withTimeout(
+ fetch(`${REGISTRAR}/pubkey/${login.pubkey}?domain=${encodeURIComponent(DOMAIN())}`).then((x) => x.json()),
+ 8000, 'registrar');
+ if (r && r.name && !/^npub1/.test(r.name)) {
+ save({ ...st, name: r.name, domain: r.domain || DOMAIN(), uri: r.uri });
+ st = load();
+ }
+ } catch {}
+ }
+ }
if (!st.name) {
// The address people see should be the identity they know the user
// by: their real npub when a nostr account is linked, else the