diff --git a/static/css/connect.css b/static/css/connect.css index 0f7d19e9..bb1b5395 100644 --- a/static/css/connect.css +++ b/static/css/connect.css @@ -26,6 +26,12 @@ transition: background-color 0.3s; } +/* prevent overlap: hide connect string until revealed; click surface on box */ +.qrcode-box #connectString { display: none; } +.qrcode-box.revealed #connectString { display: block; } +.qrcode-box #qrcode { pointer-events: none; } +.qrcode-box { cursor: pointer; } + .qrcode-box-clicked::before { content: ""; position: absolute; diff --git a/static/index.html b/static/index.html index 5daf5121..bdbe8e08 100644 --- a/static/index.html +++ b/static/index.html @@ -186,7 +186,7 @@
Scan the QR or Copy-Paste the string to establish the connection.
-
+
Code contains a one-time pairing secret
Click to reveal
diff --git a/static/js/wizard.js b/static/js/wizard.js index c52d82b7..1c8de1d0 100644 --- a/static/js/wizard.js +++ b/static/js/wizard.js @@ -3,7 +3,9 @@ $(() => { const pages = { node: $('#page-node'), liquidity: $('#page-liquidity'), - backup: $('#page-backup') + backup: $('#page-backup'), + connect: $('#page-connect'), + status: $('#page-status') }; // Inputs @@ -39,6 +41,33 @@ $(() => { pageToShow.show(); }; + const populateStatus = async () => { + try { + const res = await fetch('/wizard/service_state'); + if (res.status !== 200) return; + const s = await res.json(); + const name = s.source_name || s.provider_name || ''; + const relay = s.relay_url || (s.relays && s.relays[0]) || ''; + const admin = s.admin_npub || ''; + const avatar = s.avatar_url || (s.app_id ? `https://robohash.org/${encodeURIComponent(s.app_id)}.png?size=128x128&set=set3` : ''); + + const lndState = s.lnd_state; // 0 OFFLINE, 1 SYNCING, 2 ONLINE (per enum) + const watchdogOk = !!s.watchdog_ok; + const relayConnected = !!s.relay_connected; + + $('#show-nodey-text').text(name || '—'); + $('#show-nostr-text').text(relay || '—'); + $('#adminNpub').text(admin || '—'); + if (avatar) { $('#avatarImg').attr('src', avatar); } + + const mkDot = (ok) => ok ? '' : ''; + const lndTxt = lndState === 2 ? 'Online' : (lndState === 1 ? 'Syncing' : 'Offline'); + $('#lndStatus').html(`${mkDot(lndState === 2)} ${lndTxt}`); + $('#watchdog-status').html(`${mkDot(watchdogOk)} ${watchdogOk ? 'OK' : 'Alert'}`); + $('#relayStatus').html(`${mkDot(relayConnected)} ${relayConnected ? 'Connected' : 'Disconnected'}`); + } catch { /* noop */ } + }; + // Navigation toLiquidityBtn.click(() => { const nodeName = nodeNameInput.val(); @@ -97,23 +126,41 @@ $(() => { throw new Error(j.reason || "Failed to start service"); } // Move to in-page connect step - showPage(pages.connect || $('#page-connect')); - // fetch and render connect info (re-using logic from connect.html) + showPage(pages.connect); + // fetch and prepare connect info (async () => { const res = await fetch('/wizard/admin_connect_info'); if (res.status !== 200) return; const j = await res.json(); if (j.connect_info && j.connect_info.enrolled_npub) { - showPage(pages.status || $('#page-status')) - return + showPage(pages.status); + await populateStatus(); + return; } - const connectString = j.nprofile + ':' + j.connect_info.admin_token - const qrElement = document.getElementById('qrcode') - if (qrElement && !qrElement.firstChild) { - new QRCode(qrElement, { text: connectString, colorDark: '#000000', colorLight: '#ffffff', width: 157, height: 157 }); + const connectString = j.nprofile + ':' + j.connect_info.admin_token; + const qrElement = document.getElementById('qrcode'); + const codebox = $('#codebox'); + const clickText = $('#click-text'); + const cs = $('#connectString'); + + // Reset visual state + codebox.removeClass('revealed'); + cs.text(''); + if (qrElement) { + while (qrElement.firstChild) qrElement.removeChild(qrElement.firstChild); } - const cs = document.getElementById('connectString'); - if (cs) cs.innerText = connectString + + // Reveal on click: generate QR and show string + codebox.off('click').on('click', () => { + if (!codebox.hasClass('revealed')) { + if (qrElement && !qrElement.firstChild) { + new QRCode(qrElement, { text: connectString, colorDark: '#000000', colorLight: '#ffffff', width: 157, height: 157 }); + } + cs.text(connectString); + codebox.addClass('revealed'); + clickText.text('Pairing code'); + } + }); })(); } catch (err) { errorTextBackup.text(err.message); @@ -121,8 +168,9 @@ $(() => { }); // Navigate from connect to status - toStatusBtn && toStatusBtn.click(() => { - showPage(pages.status || $('#page-status')) + toStatusBtn && toStatusBtn.click(async () => { + showPage(pages.status); + await populateStatus(); }) const syncRelayState = () => { @@ -140,45 +188,36 @@ $(() => { } }); - // Initial state load - fetch("/wizard/state").then(res => res.json()).then(data => { - if (data.admin_linked) { - location.href = 'status.html'; - } else if (data.config_sent) { - location.href = 'connect.html'; + // Initial state load (no redirects; SPA only) + fetch("/wizard/service_state").then(res => res.json()).then(state => { + nodeNameInput.val(state.source_name); + if (state.relay_url === 'wss://relay.lightning.pub') { + customCheckbox.prop('checked', true); } else { - // Pre-populate from service state - fetch("/wizard/service_state").then(res => res.json()).then(state => { - nodeNameInput.val(state.source_name); - if (state.relay_url === 'wss://relay.lightning.pub') { - customCheckbox.prop('checked', true); - } else { - relayUrlInput.val(state.relay_url); - } - const robo = state.app_id ? `https://robohash.org/${encodeURIComponent(state.app_id)}.png?size=128x128&set=set3` : '' - if (state.avatar_url) { - avatarUrlInput.val(state.avatar_url); - avatarPreview.attr('src', state.avatar_url) - } else if (robo) { - avatarPreview.attr('src', robo) - } - if (robo) { - avatarUrlInput.attr('placeholder', robo) - } - syncRelayState(); + relayUrlInput.val(state.relay_url); + } + const robo = state.app_id ? `https://robohash.org/${encodeURIComponent(state.app_id)}.png?size=128x128&set=set3` : '' + if (state.avatar_url) { + avatarUrlInput.val(state.avatar_url); + avatarPreview.attr('src', state.avatar_url) + } else if (robo) { + avatarPreview.attr('src', robo) + } + if (robo) { + avatarUrlInput.attr('placeholder', robo) + } + syncRelayState(); - if (state.automate_liquidity) { - automateLiquidityRadio.prop('checked', true); - } else { - manualLiquidityRadio.prop('checked', true); - } + if (state.automate_liquidity) { + automateLiquidityRadio.prop('checked', true); + } else { + manualLiquidityRadio.prop('checked', true); + } - if (state.push_backups_to_nostr) { - backupNostrRadio.prop('checked', true); - } else { - manualBackupRadio.prop('checked', true); - } - }); + if (state.push_backups_to_nostr) { + backupNostrRadio.prop('checked', true); + } else { + manualBackupRadio.prop('checked', true); } }); });