aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/static
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/group-page.js25
1 files changed, 24 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 6b9c82a..7d13260 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/group-page.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/group-page.js
@@ -96,6 +96,30 @@ 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);
+ // Declared here rather than beside the render, because the effect below
+ // depends on it and a `const` further down would be in its temporal dead
+ // zone — the hook-ordering trap this codebase has already paid for.
+ const apps = visibleApps(enabledApps);
+
+ // The landing tab is chosen before the node has said which applications this
+ // group has, and a preference is a preference — not a promise that the app
+ // exists here. Two ways to land on a tab that renders nothing at all, with no
+ // tab shown active and no way to tell what went wrong: the group has Chat
+ // disabled while 'chat' is the default, or the reader's preferred app is one
+ // this group does not run. The first app the group *does* offer is the
+ // answer to both.
+ //
+ // Also covers an operator disabling the app someone is currently looking at:
+ // `enabledApps` changes live over `apps_enabled`, and being moved to a
+ // working tab beats being left staring at an empty panel.
+ //
+ // Settings is exempt: it is not an application, it is never in `apps`, and
+ // the create-group wizard lands on it deliberately.
+ useEffect(() => {
+ if (tab === 'settings') return;
+ if (!apps.length || apps.some(a => a.key === tab)) return;
+ setTab(apps[0].key);
+ }, [enabledApps, tab]);
// 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.
@@ -532,7 +556,6 @@ function GroupPage({ groupId, group, token, username, userId, userPrefs,
setPreviewEntry(entry);
}, [entries, onPlayQueue, onStopMusic]);
- const apps = visibleApps(enabledApps);
const commonProps = {
groupId, transportRef, gekRef, status, username,
entries, nodeDirs, nodeRoots, setEntries, setNodeDirs, setNodeRoots, applyIndex,