aboutsummaryrefslogtreecommitdiffstats
path: root/src/app.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/app.js')
-rw-r--r--src/app.js26
1 files changed, 23 insertions, 3 deletions
diff --git a/src/app.js b/src/app.js
index 84cec51..803a82b 100644
--- a/src/app.js
+++ b/src/app.js
@@ -1848,6 +1848,9 @@ function readLockedGiftHash() {
// then it's claimed into a fresh wallet exactly like a normal gift.
function lockedGiftClaimView() {
const lk = ui.claimLocked;
+ // NIP-07: if a nostr browser extension is present, the recipient can decrypt
+ // in-place — no code needed.
+ const hasExt = !!(typeof globalThis !== 'undefined' && globalThis.nostr && lk.eph && lk.ctKey);
return h('div', { class: 'col', style: 'gap:16px;padding:16px;max-width:460px;margin:0 auto;width:100%' },
h('div', { class: 'card col', style: 'gap:14px;align-items:center' },
h('h3', { style: 'margin:0' }, t('giftForYou')),
@@ -1855,16 +1858,33 @@ function lockedGiftClaimView() {
h('div', { class: 'row gap6', style: 'align-items:center' },
h('span', { class: 'small muted' }, t('giftLockedTo')),
profileChip(lk.to, { size: 36 })),
- h('p', { class: 'small muted', style: 'text-align:center;margin:0' }, t('giftCodeHint')),
+ ui.claimError && h('div', { class: 'notice err' }, ui.claimError),
+ hasExt ? h('button', { class: 'btn-primary btn-block', disabled: ui.busy, onClick: claimViaExtension }, ui.busy ? h('span', { class: 'spinner' }) : t('claimWithExtension')) : null,
+ hasExt ? h('div', { class: 'small faint', style: 'text-align:center' }, t('orEnterCode')) : h('p', { class: 'small muted', style: 'text-align:center;margin:0' }, t('giftCodeHint')),
h('input', { type: 'text', class: 'mono-input', style: 'width:100%', placeholder: t('claimCodePlaceholder'),
autocapitalize: 'none', autocomplete: 'off', spellcheck: 'false', value: ui.claimCodeInput,
onInput: (e) => { ui.claimCodeInput = e.target.value; } }),
- ui.claimError && h('div', { class: 'notice err' }, ui.claimError),
- h('button', { class: 'btn-primary btn-block', onClick: submitLockedCode }, t('claimGift')),
+ h('button', { class: (hasExt ? '' : 'btn-primary ') + 'btn-block', onClick: submitLockedCode }, t('claimGift')),
h('button', { class: 'btn-ghost btn-block', onClick: () => { ui.claimLocked = null; ui.claimCodeInput = ''; ui.claimError = ''; render(); } }, t('dismiss'))
)
);
}
+async function claimViaExtension() {
+ const ext = typeof globalThis !== 'undefined' && globalThis.nostr;
+ if (!ext) return;
+ ui.busy = true; ui.claimError = ''; render();
+ try {
+ const pk = await ext.getPublicKey();
+ if (pk !== ui.claimLocked.to) { ui.claimError = t('extWrongAccount'); ui.busy = false; render(); return; }
+ if (!ext.nip44 || !ext.nip44.decrypt) { ui.claimError = t('extNoNip44'); ui.busy = false; render(); return; }
+ const payload = await ext.nip44.decrypt(ui.claimLocked.eph, ui.claimLocked.ctKey);
+ if (!payload || !previewGift(payload)) { ui.claimError = t('extDecryptFailed'); ui.busy = false; render(); return; }
+ ui.busy = false; ui.claimLocked = null; ui.claimCodeInput = ''; ui.claimError = '';
+ enterWallet(newMnemonic(), '', { gift: payload }); // fresh-wallet claim
+ } catch {
+ ui.busy = false; ui.claimError = t('extDecryptFailed'); render();
+ }
+}
function submitLockedCode() {
const code = (ui.claimCodeInput || '').trim();
if (!code) return;