diff options
| author | Christophe Besson <cbesson@gmail.com> | 2026-08-25 00:29:03 +0200 |
|---|---|---|
| committer | Christophe Besson <cbesson@gmail.com> | 2026-08-25 00:29:03 +0200 |
| commit | 06c101154d544290d27f18d6fc08fb5f58a4e5d5 (patch) | |
| tree | bbcb6b863bdc7b722e5605bf3ff975cdfa022ee0 /packages/meshbay-hub/src | |
| parent | a41be5c5d6212f4eadc54bc17354311b0655a5a4 (diff) | |
| download | meshbay-06c101154d544290d27f18d6fc08fb5f58a4e5d5.tar.gz | |
fix(hub): poll for a root count change instead of trusting one fetch
Point 1 (directory list not updating without a full page reload) turned
out to still reproduce after the earlier onRefreshIndex fix — that one
addressed the Videos/Music root pickers (nodeDirs), but this section's own
list reads a different field entirely (ops.list_groups returns the
*runtime* root set, groups_ctx[gid]["roots"]) that only gets replaced once
_reload_config_inner's retarget actually finishes. /api/reload itself is
fire-and-forget on the node by design (ops.start_reload's own docstring —
a brand-new group's initial scan can take minutes, the caller must not
block on it), so a single loadNodeInfo() call right after can land in the
gap before that replacement happens and show the pre-change count.
Both the add and remove handlers now poll /api/groups (up to ~4s, every
400ms) until the root count actually matches what the action should have
produced, rather than fetching once and hoping the timing worked out.
Diffstat (limited to 'packages/meshbay-hub/src')
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/static/group-settings.js | 36 |
1 files changed, 34 insertions, 2 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/group-settings.js b/packages/meshbay-hub/src/meshbay_hub/static/group-settings.js index 34f94f9..a4db4cb 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/group-settings.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/group-settings.js @@ -157,6 +157,36 @@ function GroupSettingsPanel({ groupId, group, token, transportRef, gekRef, }, [groupId]); useEffect(() => { loadNodeInfo(); }, [loadNodeInfo]); + + /** + * Poll /api/groups until the root count actually matches what an + * add/remove just did, instead of trusting a single loadNodeInfo() call + * right after /api/reload. Found live: /api/reload is fire-and-forget on + * the node (ops.start_reload schedules the real work and returns + * immediately, deliberately — a brand-new group's initial scan can take + * minutes, see its own docstring) — and the list this section renders + * (ops.list_groups) reads the *runtime* root set + * (groups_ctx[gid]["roots"]), which only gets replaced once + * _reload_config_inner's retarget actually finishes, not the config-file + * list add_root/remove_root already updated synchronously. A single + * fetch right after can land in that gap and show the old count. + */ + const waitForRootCount = useCallback(async (expectedCount) => { + for (let i = 0; i < 10; i++) { + try { + const data = await platform.node.call('GET', '/api/groups'); + const ng = (data.groups || []).find(g => g.id === groupId); + const roots = (ng && ng.roots) || []; + if (roots.length === expectedCount) { + setNodeRoots(roots); + if (ng) setNodeGroupName(ng.name || ''); + return true; + } + } catch { /* keep trying — the node may be mid-reload */ } + await new Promise(r => setTimeout(r, 400)); + } + return false; + }, [groupId]); const [inviteCode, setInviteCode] = useState(null); const [pairCode, setPairCode] = useState(''); const [pairStatus, setPairStatus] = useState(''); @@ -976,13 +1006,14 @@ function GroupSettingsPanel({ groupId, group, token, transportRef, gekRef, disabled=${nodeBusy} onClick=${async () => { if (!confirm(t('node.root_remove_confirm', { name: r.name }))) return; + const countBefore = nodeRoots.length; setNodeBusy(true); setNodeMsg(''); try { await platform.node.call('DELETE', '/api/groups/' + groupId + '/roots/' + encodeURIComponent(r.name)); await platform.node.call('POST', '/api/reload'); setNodeMsg(t('node.root_removed')); - await loadNodeInfo(); + await waitForRootCount(countBefore - 1); // Folders (unlike files) only ever arrive via a full // index_sync, never index_delta (daemon.py's ongoing // push has no `dirs` field) — without this, the @@ -1000,6 +1031,7 @@ function GroupSettingsPanel({ groupId, group, token, transportRef, gekRef, onClick=${async () => { const chosen = await platform.rootPicker.choose(); if (!chosen) return; + const countBefore = nodeRoots.length; setNodeBusy(true); setNodeMsg(''); setNodeIndexProgress(null); try { await platform.node.call('POST', @@ -1013,7 +1045,7 @@ function GroupSettingsPanel({ groupId, group, token, transportRef, gekRef, // an unchanged screen while it happens. await platform.watchIndexProgress(groupId, setNodeIndexProgress); setNodeMsg(t('node.root_added')); - await loadNodeInfo(); + await waitForRootCount(countBefore + 1); // See the matching comment on root removal above — a new // folder needs a full index_sync to show up anywhere that // reads `nodeDirs` (the Videos/Music root pickers), not |