aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/features/names.js53
-rw-r--r--src/features/nostrlogin.js4
-rw-r--r--src/i18n.js1
3 files changed, 33 insertions, 25 deletions
diff --git a/src/features/names.js b/src/features/names.js
index 48fb72a..ec422be 100644
--- a/src/features/names.js
+++ b/src/features/names.js
@@ -41,10 +41,10 @@ export function namesFeature(ctx) {
async function post(path, method, payload, signer) {
const url = `${REGISTRAR}${path}`;
const body = JSON.stringify(payload);
- const auth = await withTimeout(nip98Header(signer || walletSigner(), url, method, body), 20000, 'signing');
+ const auth = await withTimeout(nip98Header(signer || walletSigner(), url, method, body), 12000, 'signing');
const r = await withTimeout(fetch(url, {
method, headers: { 'content-type': 'application/json', authorization: auth }, body,
- }), 20000, 'registrar');
+ }), 12000, 'registrar');
const j = await r.json().catch(() => ({}));
if (!r.ok || j.error) throw new Error(j.error || `registrar refused (${r.status})`);
return j;
@@ -57,7 +57,7 @@ export function namesFeature(ctx) {
&& !!wallet.nostrSign && !!hook('arkReady');
async function currentUri() {
- const addr = await withTimeout(hook('arkStaticAddress'), 20000, 'ark');
+ const addr = await withTimeout(hook('arkStaticAddress'), 12000, 'ark');
return addr ? `bitcoin:?ark=${encodeURIComponent(addr)}` : null;
}
@@ -91,12 +91,13 @@ export function namesFeature(ctx) {
// out), so nothing here ever blocks the wallet.
let retryTimer = null;
let deadline = null;
+ let lastError = null;
async function refresh(attempt = 0) {
clearTimeout(retryTimer);
// Whatever goes wrong, stop claiming to be "setting up" after a minute
// and show the user the manual form instead.
if (!deadline) {
- deadline = setTimeout(() => { checked = true; clearTimeout(retryTimer); render(); }, 60000);
+ deadline = setTimeout(() => { checked = true; clearTimeout(retryTimer); render(); }, 12000);
}
try {
if (!available()) {
@@ -117,25 +118,12 @@ export function namesFeature(ctx) {
// usually be re-attached silently; anything else falls back to the
// wallet's own identity rather than retrying a claim that can only
// ever fail.
- const linked = hook('nostrLoginIdentity');
- let signer = linked && linked.signer;
- if (linked && !signer) { try { signer = await withTimeout(hook('nostrLoginResume'), 10000, 'signer'); } catch {} }
- const useReal = !!(linked && signer);
- const npub = useReal ? linked.npub : (wallet.nostrNpub && wallet.nostrNpub());
- if (npub) {
- const opts = useReal ? { signer, manager: wallet.nostrPubkey() } : {};
- try {
- await claim(npub.slice(0, 20), opts);
- } catch (e) {
- // A refusal is permanent — never spin on it. Fall back to the
- // wallet's own name so the user always ends up with an address.
- const permanent = /belongs to another|is taken|reserved|invalid/i.test(e.message);
- const own = wallet.nostrNpub && wallet.nostrNpub();
- if (permanent && useReal && own) await claim(own.slice(0, 20), {});
- else if (!permanent) throw e;
- }
- st = load();
- }
+ // Claim with the WALLET's own key: it needs no signer, prompts
+ // nobody, and always works. Tying the address to the user's real
+ // npub happens at login instead (adoptIdentity below), where a
+ // signer is already live — an address must never wait on a popup.
+ const own = wallet.nostrNpub && wallet.nostrNpub();
+ if (own) { await claim(own.slice(0, 20), {}); st = load(); }
}
checked = true;
clearTimeout(deadline); deadline = null;
@@ -145,12 +133,25 @@ export function namesFeature(ctx) {
if (uri && uri !== st.uri) await claim(st.name);
} catch (e) {
console.warn('names: refresh failed —', e.message);
- if (attempt < 6) { retryTimer = setTimeout(() => refresh(attempt + 1), 5000); return; }
+ lastError = e.message;
+ if (attempt < 3) { retryTimer = setTimeout(() => refresh(attempt + 1), 4000); return; }
checked = true;
render();
}
}
+ // Called right after a nostr login, while that signer is definitely live:
+ // move the payment address onto the user's real npub and nominate the
+ // wallet key to keep the record updated afterwards.
+ async function adoptIdentity(signer, npub) {
+ if (!signer || !npub || !available()) return null;
+ const name = npub.slice(0, 20);
+ if (load().name === name) return name;
+ await claim(name, { signer, manager: wallet.nostrPubkey() });
+ render();
+ return name;
+ }
+
// ---- sending to a name -------------------------------------------------
function beginResolve(text) {
@@ -269,6 +270,7 @@ export function namesFeature(ctx) {
if (!st.name) {
return h('div', { class: 'card col', style: 'gap:10px' },
seg,
+ lastError ? h('div', { class: 'notice err small' }, t('namesAutoFailed', { why: lastError })) : null,
h('p', { class: 'small muted', style: 'margin:0' }, t('namesDesc')),
claimForm(false));
}
@@ -282,7 +284,8 @@ export function namesFeature(ctx) {
return {
id: 'names',
- init() { checked = false; refresh(); },
+ init() { checked = false; lastError = null; refresh(); },
+ namesAdoptIdentity(signer, npub) { return adoptIdentity(signer, npub); },
stop() { clearTimeout(retryTimer); clearTimeout(deadline); deadline = null; },
receiveModes() {
if (!available()) return [];
diff --git a/src/features/nostrlogin.js b/src/features/nostrlogin.js
index cea13b4..7d4bdc1 100644
--- a/src/features/nostrlogin.js
+++ b/src/features/nostrlogin.js
@@ -52,6 +52,8 @@ export function nostrLoginFeature(ctx) {
live = signer;
await ctx.openMnemonic(res.mnemonic, res.passphrase || '', { nostrPubkey: signer.pubkey });
save({ ...load(), pubkey: signer.pubkey, linked: Date.now() });
+ // claim the real npub as the payment address while this signer is live
+ ctx.hook('namesAdoptIdentity', signer, npubOf(signer.pubkey))?.catch?.(() => {});
toast(res.mode === 'derived' ? t('nlOpenedDerived') : t('nlOpenedRestored'));
busy(false);
} catch (e) { fail(e); }
@@ -68,6 +70,7 @@ export function nostrLoginFeature(ctx) {
live = st.signer;
await ctx.openMnemonic(mnemonic, '', { nostrPubkey: st.signer.pubkey });
save({ ...load(), pubkey: st.signer.pubkey, linked: Date.now() });
+ ctx.hook('namesAdoptIdentity', st.signer, npubOf(st.signer.pubkey))?.catch?.(() => {});
toast(t('nlCreated'));
busy(false);
} catch (e) { fail(e); }
@@ -89,6 +92,7 @@ export function nostrLoginFeature(ctx) {
await publishWalletBackup(signer, { mnemonic: wallet.mnemonic, passphrase: wallet.passphrase || '' });
live = signer;
save({ ...load(), pubkey: signer.pubkey, linked: Date.now() });
+ ctx.hook('namesAdoptIdentity', signer, npubOf(signer.pubkey))?.catch?.(() => {});
toast(t('nlLinked'));
busy(false);
} catch (e) { fail(e); }
diff --git a/src/i18n.js b/src/i18n.js
index c3684bd..c1722d2 100644
--- a/src/i18n.js
+++ b/src/i18n.js
@@ -449,6 +449,7 @@ const STR = {
nlLinked: 'Wallet linked to your Nostr account',
nlNeedSeed: 'This wallet has no recovery phrase loaded.',
nlAlreadyLinked: 'That Nostr account already has a different wallet.',
+ namesAutoFailed: 'Couldn’t set your address up automatically ({why}). Pick one below.',
namesSettingUp: 'Setting up your payment address…',
namesTitle: 'Payment address',
receiveNameTab: 'Payment address',