/** * Single BackOne core network for all addons (same as backone-cli listnetworks/join). */ window.BackOneNetworkSession = (function () { const NETWORK_KEY = 'backone.lastNetworkId'; const PEER_KEY = 'backone.lastPeerId'; let activeNetwork = null; let selectedPeer = null; let coreStatus = { online: false, address: '', networkCount: 0, leafPeerCount: 0, authenticated: false }; const listeners = new Set(); function emit() { const snap = { network: activeNetwork, networkId: getNetworkId(), peer: selectedPeer, core: { ...coreStatus } }; listeners.forEach(fn => { try { fn(snap); } catch (_) { /* ignore */ } }); } function getNetworkId() { return (localStorage.getItem(NETWORK_KEY) || '').toLowerCase(); } function setNetworkId(id) { const nwid = BackOnePeerDirectory.normalizeNetworkId(id); if (nwid) { localStorage.setItem(NETWORK_KEY, nwid); } else { localStorage.removeItem(NETWORK_KEY); } BackOnePeerDirectory.setLastNetworkId(nwid); return nwid; } function getSelectedPeerId() { return (localStorage.getItem(PEER_KEY) || '').toLowerCase(); } function setSelectedPeer(peer) { selectedPeer = peer || null; if (peer && peer.peerId) { localStorage.setItem(PEER_KEY, peer.peerId); } else { localStorage.removeItem(PEER_KEY); } emit(); } function pickDefaultNetworkId(networks) { if (!networks.length) return ''; const stored = getNetworkId(); if (stored && networks.some(n => (n.id || '').toLowerCase() === stored)) { return stored; } const preferred = networks.find(n => n.status === 'OK') || networks.find(n => n.status === 'REQUESTING_CONFIGURATION') || networks[0]; return preferred ? (preferred.id || '').toLowerCase() : ''; } /** * On app start: read BackOne core state (same as backone-cli info / listnetworks / peers). */ async function syncFromCore() { const token = await BackOneAuth.getToken(); const [status, networks, cliPeers] = await Promise.all([ BackOnePeerDirectory.loadCoreStatus(), BackOnePeerDirectory.loadNetworks(), BackOnePeerDirectory.loadCliPeers() ]); coreStatus = { online: !!(status && status.online), address: (status && status.address) || '', version: (status && status.version) || '', networkCount: networks.length, leafPeerCount: BackOnePeerDirectory.countLeafPeers(cliPeers), authenticated: !!token }; const pick = pickDefaultNetworkId(networks); if (pick) { setNetworkId(pick); activeNetwork = networks.find(n => (n.id || '').toLowerCase() === pick) || await BackOnePeerDirectory.loadNetwork(pick); } else { setNetworkId(''); activeNetwork = null; } emit(); return { coreStatus, activeNetwork, networks }; } async function refresh() { const id = getNetworkId(); if (!id) { activeNetwork = null; emit(); return null; } activeNetwork = await BackOnePeerDirectory.loadNetwork(id); if (!activeNetwork) { activeNetwork = { id, name: id, status: 'UNKNOWN' }; } emit(); return activeNetwork; } async function loadNetworks() { return BackOnePeerDirectory.loadNetworks(); } async function selectNetwork(nwid) { const id = setNetworkId(nwid); if (!id) { await refresh(); return null; } return refresh(); } async function joinNetwork(nwid) { const id = BackOnePeerDirectory.normalizeNetworkId(nwid); if (!id) throw new Error('Network ID must be 16 hexadecimal characters'); await BackOnePeerDirectory.joinNetwork(id); return syncFromCore(); } async function leaveNetwork(nwid) { const id = BackOnePeerDirectory.normalizeNetworkId(nwid || getNetworkId()); if (!id) throw new Error('No network selected'); await BackOnePeerDirectory.leaveNetwork(id); if (getNetworkId() === id) { setNetworkId(''); selectedPeer = null; localStorage.removeItem(PEER_KEY); } return syncFromCore(); } function subscribe(fn) { listeners.add(fn); return () => listeners.delete(fn); } function requireNetworkId() { const id = getNetworkId(); if (!id) throw new Error('Join a BackOne network first (same as backone-cli join)'); return id; } function getCoreStatus() { return { ...coreStatus }; } return { getNetworkId, getActiveNetwork: () => activeNetwork, getCoreStatus, getSelectedPeer: () => selectedPeer, getSelectedPeerId, setSelectedPeer, selectNetwork, joinNetwork, leaveNetwork, loadNetworks, syncFromCore, refresh, subscribe, requireNetworkId }; })();