From 7422a777297347dc83e6c5b2e89c1a6fa99a36b0 Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Sun, 27 Sep 2026 22:21:07 +0200 Subject: fix(hub): say "No operator paired" only when the node says so The Node page showed the banner whenever operator_paired was not true, so a node that had not yet read its roster -- one still signing in to the hub -- was reported unpaired while its pairing was intact. The node now answers null until it knows; the page shows the banner for false only. Co-Authored-By: Claude Opus 5.5 --- .../src/meshbay_hub/static/node-page.js | 16 ++++++-- .../meshbay-hub/tests/test_node_page_pairing.py | 48 ++++++++++++++++++++++ 2 files changed, 60 insertions(+), 4 deletions(-) create mode 100644 packages/meshbay-hub/tests/test_node_page_pairing.py (limited to 'packages/meshbay-hub') diff --git a/packages/meshbay-hub/src/meshbay_hub/static/node-page.js b/packages/meshbay-hub/src/meshbay_hub/static/node-page.js index a59c100..7fd7ab1 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/node-page.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/node-page.js @@ -13,6 +13,14 @@ import { HUB } from './hub-client.js'; // (platform.node.call), not over MNP/WebRTC. The MNP protocol types remain // for potential future browser-side use. +// true / false from a node that has read its roster, null from one that has +// not yet (it publishes the roster only once it has signed in to the hub) -- +// which is not the same as having no operator. +export function pairedFrom(result) { + const v = result && result.operator_paired; + return v === true || v === false ? v : null; +} + function NodeServicePanel({ onChanged, token, username }) { const [info, setInfo] = useState(null); const [busy, setBusy] = useState(''); @@ -202,7 +210,7 @@ export function NodePage({ groups, token, username }) { const [editIce, setEditIce] = useState(null); const [savingIce, setSavingIce] = useState(false); const [iceInput, setIceInput] = useState(''); - const [operatorPaired, setOperatorPaired] = useState(false); + const [operatorPaired, setOperatorPaired] = useState(null); const [pairBusy, setPairBusy] = useState(false); const [pairStatus, setPairStatus] = useState(''); const [nodeInfo, setNodeInfo] = useState(null); @@ -231,7 +239,7 @@ export function NodePage({ groups, token, username }) { } const result = await nodeCall('GET', '/api/groups'); setNodeGroups(result.groups || []); - setOperatorPaired(!!result.operator_paired); + setOperatorPaired(pairedFrom(result)); setNodeSettings(result.settings || null); try { setNodeInfo(await nodeCall('GET', '/api/status')); } catch {} setStatus('connected'); @@ -253,7 +261,7 @@ export function NodePage({ groups, token, username }) { try { const result = await nodeCall('GET', '/api/groups'); setNodeGroups(result.groups || []); - setOperatorPaired(!!result.operator_paired); + setOperatorPaired(pairedFrom(result)); setNodeSettings(result.settings || null); try { setNodeInfo(await nodeCall('GET', '/api/status')); } catch {} } catch {} @@ -711,7 +719,7 @@ export function NodePage({ groups, token, username }) { <${NodeServicePanel} onChanged=${fetchStatus} token=${token} username=${username} /> ${actionMsg && html`
${actionMsg}
`} - ${!operatorPaired && html` + ${operatorPaired === false && html`

${t('node.pair_needed')}