From ccced4634115777fcbedd221f1324612e00b61d5 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Mon, 3 Aug 2026 22:31:48 -0700 Subject: Offer the signer back where you'd notice it's missing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A remote signer dropped by a reload now says so above the composer, with a button that reconnects in place — rather than letting someone write a message and only then be told it can't be sent as them. Failing that it opens the nostr settings, where logging in again is the fallback. Every remote-signer login made before sessions were persisted lands in exactly this state once. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Wvj4aoeMZwA7VUGdJP9Swi --- src/features/messages.js | 26 +++++++++++++++++++++++++- src/i18n.js | 5 ++++- src/style.css | 1 + 3 files changed, 30 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/features/messages.js b/src/features/messages.js index 74abdd8..7f21d53 100644 --- a/src/features/messages.js +++ b/src/features/messages.js @@ -1226,7 +1226,31 @@ export function messagesFeature(ctx) { }); }; + // A remote signer that a reload dropped: say so where the typing happens, + // rather than letting someone write a message and only then be told. The + // identity itself is still known — it's the ability to sign as it that's + // missing — so this offers to fetch it back rather than to log in again. + function signerNotice() { + const id = hook('nostrLoginIdentity'); + if (!id || id.signer || ui.msgReconnected) return null; + return h('div', { class: 'row gap6 chat-signer-off', style: 'align-items:center' }, + h('span', { class: 'small muted grow' }, t('msgSignerOff')), + h('button', { + class: 'btn-sm', disabled: !!ui.msgReconnecting, + onClick: async () => { + ui.msgReconnecting = true; render(); + const s = await hook('nostrLoginResume'); + ui.msgReconnecting = false; + if (s) { ui.msgReconnected = true; toast(t('msgSignerBack')); } + else { ui.chatOpen = false; ui.screen = 'wallet'; ui.tab = 'settings'; ui.settingsPage = 'nostr'; } + render(); + }, + }, ui.msgReconnecting ? h('span', { class: 'spinner sm' }) : t('msgReconnect'))); + } + const composer = (placeholder, onSend, onType) => + h('div', { class: 'col', style: 'gap:6px' }, + signerNotice(), h('div', { class: 'chat-compose' }, h('input', { class: 'grow', type: 'text', id: 'msg-draft', placeholder, @@ -1235,7 +1259,7 @@ export function messagesFeature(ctx) { onKeydown: (e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); onSend(); } }, }), h('button', { class: 'btn-primary btn-sm', disabled: ui.msgSending, onClick: onSend }, - ui.msgSending ? h('span', { class: 'spinner sm' }) : t('msgSend'))); + ui.msgSending ? h('span', { class: 'spinner sm' }) : t('msgSend')))); // ---- home --------------------------------------------------------------- diff --git a/src/i18n.js b/src/i18n.js index 53cd643..7c55c65 100644 --- a/src/i18n.js +++ b/src/i18n.js @@ -128,7 +128,7 @@ const STR = { hiwBasics2: 'There’s no server and no account. The whole app is one page that runs in your browser. You can save the page and use it offline.', hiwBasics3: 'It follows the standard Bitcoin formats, so the same twelve words will restore your wallet in Sparrow, Electrum, or any other standard wallet even if this site disappears.', hiwLnTitle: 'Spending and Savings', - hiwLn1: 'Your money sits in two places. Savings is ordinary Bitcoin on the blockchain. Withdrawals costs a mining fee and deposits take time to confirm. Deposits into the spending wallet arrive instantly and payments out are free: it runs on Ark and speaks Lightning, so you can pay and be paid by any bitcoin app or shop.', + hiwLn1: 'Your money sits in two places. Savings is ordinary Bitcoin on the blockchain — safest for money you’re keeping, but each move costs a mining fee and waits for a confirmation. Spending sends and arrives instantly and free, and is still only yours: it runs on Ark, a newer layer on top of Bitcoin where every coin stays locked to your key, and it speaks Lightning, so you can pay and be paid by any bitcoin app or shop.', hiwLn2: 'Move money either way under your balance — a small fee each direction, free inside Spending. The wallet handles all the upkeep on its own (just open the app now and then), and every coin in Spending carries a pre-signed escape route: even if the coinos server vanished, you could pull your money back on-chain yourself.', hiwGiftsTitle: 'Gifts', hiwGifts1: 'You can gift bitcoin with a link: whoever opens it claims the coins into their own wallet — no app, no signup. On-chain gifts lock the coins until claimed and can be cancelled or reclaimed; Ark gifts claim instantly and free, even into a brand-new empty wallet.', @@ -159,6 +159,9 @@ const STR = { msgDelete: 'Delete message', msgNoIdentity: "Your nostr signer isn't connected — reconnect it to send as yourself", msgSendFailed: 'No relay accepted the message', + msgSignerOff: "Your nostr signer isn't connected, so you can't post as yourself yet.", + msgReconnect: 'Reconnect', + msgSignerBack: 'Signer reconnected', msgDmsTitle: 'Messages', msgCommunitiesTitle: 'Communities', msgNewDm: 'New message', diff --git a/src/style.css b/src/style.css index b90be07..759ee61 100644 --- a/src/style.css +++ b/src/style.css @@ -556,3 +556,4 @@ body::after { .balance-move .input-group { flex-wrap: wrap; } .balance-move .amt-field { flex: 1 1 150px; } .balance-move .input-group > button { flex: 1 1 150px; } +.chat-signer-off { padding: 6px 8px; border: 1px solid var(--line); border-radius: 10px; background: var(--surface2); } -- cgit v1.2.3