aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-08-27 12:03:59 -0700
committerAdam Soltys <[email protected]>2026-08-27 12:03:59 -0700
commit0526da5cb8c5d1790effe6c3871c1f237962509c (patch)
tree8109169272e2b0185f8475dd762cbb134269765b /src
parent838046f049cad53fd45ef435a6dafcb3efbda638 (diff)
downloadcoinosv3-0526da5cb8c5d1790effe6c3871c1f237962509c.tar.gz
coinosv3-0526da5cb8c5d1790effe6c3871c1f237962509c.zip
One tap, one dialog: a passkey sign-in with nothing on the device rolls straight into creating one — the browser's own passkey sheet is the confirmation, and dismissing it is a quiet cancel, not an error
Co-Authored-By: Claude Fable 5 <[email protected]> Claude-Session: https://claude.ai/code/session_019eA8JRq9Wr7z2fRQk99HLi
Diffstat (limited to 'src')
-rw-r--r--src/features/nostrlogin.js33
-rw-r--r--src/i18n.js2
2 files changed, 11 insertions, 24 deletions
diff --git a/src/features/nostrlogin.js b/src/features/nostrlogin.js
index 981491e..9b9d1ef 100644
--- a/src/features/nostrlogin.js
+++ b/src/features/nostrlogin.js
@@ -239,8 +239,9 @@ export function nostrLoginFeature(ctx) {
}
// Passkey sign-in: try an existing credential first; when the device has
- // none (or the user dismisses the picker), offer to create one instead of
- // showing a bare error.
+ // none (or the picker is dismissed), roll straight into creating one — the
+ // browser's own passkey dialog IS the confirmation, so an interstitial of
+ // ours would just be a second question.
async function passkeyLogin(run) {
ui.nostrLoginError = '';
const pass = await import('../passkey.js');
@@ -249,20 +250,14 @@ export function nostrLoginFeature(ctx) {
await run(() => keySigner(sk));
} catch (e) {
if (/derive a wallet key/.test(e.message)) { ui.nostrLoginError = e.message; render(); return; }
- ui.passkeyOffer = true; // no credential picked — offer creation
- render();
- }
- }
- async function passkeyCreateLogin(run) {
- ui.nostrLoginError = '';
- ui.passkeyOffer = null;
- try {
- const pass = await import('../passkey.js');
- const sk = await pass.passkeyCreate();
- await run(() => keySigner(sk));
- } catch (e) {
- ui.nostrLoginError = e.message;
- render();
+ try {
+ const sk = await pass.passkeyCreate();
+ await run(() => keySigner(sk));
+ } catch (e2) {
+ // dismissing the create dialog is a plain cancel, not an error
+ if (!/NotAllowed|abort/i.test(String(e2.name || '') + e2.message)) ui.nostrLoginError = e2.message;
+ render();
+ }
}
}
@@ -289,12 +284,6 @@ export function nostrLoginFeature(ctx) {
? h('button', { class: 'btn-block', style: pad, disabled: ui.nostrLoginBusy,
onClick: () => passkeyLogin(run) }, markedLabel(PASSKEY_MARK, t('nlPasskey')))
: null,
- ui.passkeyOffer
- ? h('div', { class: 'row gap6', style: 'align-items:center' },
- h('span', { class: 'small muted grow' }, t('nlPasskeyNone')),
- h('button', { class: 'btn-sm', disabled: ui.nostrLoginBusy, onClick: () => passkeyCreateLogin(run) }, t('nlPasskeyCreate')),
- h('button', { class: 'btn-sm btn-ghost', onClick: () => { ui.passkeyOffer = null; render(); } }, t('cancel')))
- : null,
];
}
diff --git a/src/i18n.js b/src/i18n.js
index eec6eba..8ec64ce 100644
--- a/src/i18n.js
+++ b/src/i18n.js
@@ -633,8 +633,6 @@ const STR = {
nlExtension: 'Use browser extension',
nlGoogle: 'Sign in with Google',
nlPasskey: 'Sign in with a passkey',
- nlPasskeyNone: 'No passkey was used. Create one for this wallet?',
- nlPasskeyCreate: 'Create passkey',
nlKeyOrBunker: 'nsec, private key, or bunker://…',
nlGo: 'Log in',
nlHint: 'Works with Amber, Amethyst and other remote signers (paste their bunker:// address), or a browser extension. A pasted key is used once and never stored.',