diff options
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/group-page.js')
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/static/group-page.js | 57 |
1 files changed, 56 insertions, 1 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/group-page.js b/packages/meshbay-hub/src/meshbay_hub/static/group-page.js index 1528213..29f3602 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/group-page.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/group-page.js @@ -76,6 +76,10 @@ function GroupPage({ groupId, group, token, username, userId, userPrefs, // every registered app when a node predates the setting (or hasn't answered // yet), so nothing disappears for an existing group. const [enabledApps, setEnabledApps] = useState(null); + // Reconcile interval / debounce currently in effect on the node — shown + // to the operator in Settings, not enforced from here (indexer.py owns + // that). Null until the handshake ack arrives. + const [scanSettings, setScanSettings] = useState(null); // Paired ≠ operator account. `is_node_admin` says the hub account owning this // node is the one connecting; this says the node pinned *this browser's* key // as an operator key. Only the second one lets you sign an invite, and only @@ -123,6 +127,25 @@ function GroupPage({ groupId, group, token, username, userId, userPrefs, cacheGroupIndex(groupId, group ? group.name : groupId, fresh); }, [groupId, group]); + // additions/deletions only (daemon.py _broadcast_index_change, once there + // is a previous snapshot to diff against) — applied on top of whatever + // applyIndex last put in `entries`, instead of replacing the whole table + // for one changed file. + const applyIndexDelta = useCallback((deltaMsg) => { + setEntries((prev) => { + const deletions = new Set(deltaMsg.deletions || []); + const kept = prev.filter((e) => !deletions.has(e.id)); + // The index is keyed by content hash: an addition whose id is already + // present is the same duplicate-content case indexer.py's own + // reconcile sweep leaves alone, not a second row for one file. + const keptIds = new Set(kept.map((e) => e.id)); + const additions = (deltaMsg.additions || []).filter((e) => !keptIds.has(e.id)); + const fresh = kept.concat(additions); + cacheGroupIndex(groupId, group ? group.name : groupId, fresh); + return fresh; + }); + }, [groupId, group]); + useEffect(() => { let cancelled = false; @@ -172,11 +195,24 @@ function GroupPage({ groupId, group, token, username, userId, userPrefs, setIsNodeAdmin(!!ack.is_node_admin); setMemberUpload(ack.member_upload !== false); setEnabledApps(ack.enabled_apps || null); + setScanSettings(ack.scan_settings || null); // Changed while we are connected, by an operator who may be someone // else entirely. Without this the button stays until a reconnection, // and a button that is still there is a button people press. transport.onUploadPolicy = (allowed) => setMemberUpload(allowed); transport.onAppsEnabled = (apps) => setEnabledApps(apps); + // The node's own scan (a root added while we were already connected, + // or reconcile catching one back up) — never the entries, just + // enough to animate the sidebar dot. Guaranteed a final push at the + // False transition (daemon.py _progress_pusher), so this always + // settles back to 'online' rather than getting stuck. + transport.onIndexProgress = (status) => { + if (cancelled || !onPresence) return; + const pct = status.total_bytes + ? Math.min(100, Math.round(100 * status.scanned_bytes / status.total_bytes)) + : 0; + onPresence(groupId, status.scanning ? 'indexing' : 'online', pct); + }; setOperatorPaired(transport.memberRole === 'operator'); // A first join to this node generated an identity for it; leave it with @@ -203,6 +239,10 @@ function GroupPage({ groupId, group, token, username, userId, userPrefs, if (cancelled) return; applyIndex(msg); }; + transport.onIndexDelta = (msg) => { + if (cancelled) return; + applyIndexDelta(msg); + }; // We are in: an invitation to this group has served its purpose. if (onJoined) onJoined(groupId); @@ -214,7 +254,20 @@ function GroupPage({ groupId, group, token, username, userId, userPrefs, touchActivity(); // First-hand evidence, and the strongest available: this browser spoke // to the node. It outranks whatever the hub said in the group list. - if (onPresence) onPresence(groupId, 'online'); + // A scan already under way at the moment of connecting (ack.indexing, + // webrtc_server.py _complete_handshake) shows as indexing right away + // rather than waiting for the next periodic push. + if (onPresence) { + const idx = ack.indexing; + if (idx && idx.scanning) { + const pct = idx.total_bytes + ? Math.min(100, Math.round(100 * idx.scanned_bytes / idx.total_bytes)) + : 0; + onPresence(groupId, 'indexing', pct); + } else { + onPresence(groupId, 'online'); + } + } } catch (err) { if (cancelled) return; @@ -448,6 +501,8 @@ function GroupPage({ groupId, group, token, username, userId, userPrefs, onMemberUpload=${(allowed) => setMemberUpload(allowed)} enabledApps=${enabledApps} onEnabledApps=${(keys) => setEnabledApps(keys)} + scanSettings=${scanSettings} + onScanSettings=${(s) => setScanSettings(s)} onLeft=${onLeft} onPaired=${() => setOperatorPaired(true)} /> `} |