diff options
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/node-page.js')
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/static/node-page.js | 111 |
1 files changed, 67 insertions, 44 deletions
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 2a0b0d7..ec73128 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/node-page.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/node-page.js @@ -47,21 +47,35 @@ function NodeServicePanel({ onChanged }) { } }, [refresh, onChanged]); - const toggleAutostart = useCallback(() => { - act('autostart', () => (info && info.autostart - ? platform.node.autostart.remove() - : platform.node.autostart.install())); - }, [act, info]); + // "off" / "signin" / "service" -- derived from the status payload, no new + // backend field needed: mode/autostart already distinguish all three. + const startupMode = (i) => { + if (!i) return 'off'; + if (i.mode === 'service') return i.mode; + return i.autostart ? 'signin' : 'off'; + }; // Switching mode itself — the installer's own choice is effectively one-shot // (it skips the question once the firewall rules exist for any reason, and // per-user mode sets those up on its own with no Scheduled Task), so this is // the only way back in if service mode was declined, or out if it is no // longer wanted. One elevation, task + firewall together, same script. - const toggleServiceMode = useCallback(() => { - act('serviceMode', () => (info && info.mode === 'service' - ? platform.node.serviceMode.remove() - : platform.node.serviceMode.install())); + // + // The two mechanisms are mutually exclusive by construction here: never + // both installed at once, which would start the daemon twice (once at + // boot via the Scheduled Task, again at sign-in via the Startup .vbs). + // Always remove whichever one is currently active before installing the + // target, so every transition -- not just the two that used to be + // separate toggles -- keeps that invariant. + const changeStartupMode = useCallback((target) => { + const current = startupMode(info); + if (target === current) return; + act('startupMode', async () => { + if (current === 'service') await platform.node.serviceMode.remove(); + else if (current === 'signin') await platform.node.autostart.remove(); + if (target === 'service') await platform.node.serviceMode.install(); + else if (target === 'signin') await platform.node.autostart.install(); + }); }, [act, info]); if (!platform.node.service.available) return null; @@ -79,44 +93,53 @@ function NodeServicePanel({ onChanged }) { const label = info.installed ? t('node.service_state_' + stateKey) : t('node.service_not_installed'); + // Own row, below the status/actions card rather than a further item + // crammed into its flex-wrap line -- that (plus two independent toggles + // for what is really one choice) is what made this a mess before. + const showStartupRow = (platform.node.autostart.available + || platform.node.serviceMode.available) && typeof info.mode === 'string'; + return html` - <div class="node-service"> - <div class="node-service-status"> - <span class="presence presence-${dot}" title="${label}" aria-label="${label}"></span> - <span>${label}</span> - </div> - ${err && html`<div class="error-msg">${err}</div>`} - <div class="node-service-actions"> - <button class="btn btn-small btn-secondary" disabled=${!!busy || running} - onClick=${() => act('start', () => platform.node.start())}> - ${busy === 'start' ? t('node.service_starting') : t('node.service_start')}</button> - ${info.installed && html` - <button class="btn btn-small btn-secondary" disabled=${!!busy || !running} - onClick=${() => act('stop', () => platform.node.service.stop())}> - ${busy === 'stop' ? t('node.service_stopping') : t('node.service_stop')}</button> - <button class="btn btn-small btn-secondary" disabled=${!!busy} - onClick=${() => act('restart', () => platform.node.service.restart())}> - ${busy === 'restart' ? t('node.service_restarting') : t('node.service_restart')}</button> + <div> + <div class="node-service"> + <div class="node-service-status"> + <span class="presence presence-${dot}" title="${label}" aria-label="${label}"></span> + <span>${label}</span> + </div> + ${err && html`<div class="error-msg">${err}</div>`} + <div class="node-service-actions"> + <button class="btn btn-small btn-secondary" disabled=${!!busy || running} + onClick=${() => act('start', () => platform.node.start())}> + ${busy === 'start' ? t('node.service_starting') : t('node.service_start')}</button> + ${info.installed && html` + <button class="btn btn-small btn-secondary" disabled=${!!busy || !running} + onClick=${() => act('stop', () => platform.node.service.stop())}> + ${busy === 'stop' ? t('node.service_stopping') : t('node.service_stop')}</button> + <button class="btn btn-small btn-secondary" disabled=${!!busy} + onClick=${() => act('restart', () => platform.node.service.restart())}> + ${busy === 'restart' ? t('node.service_restarting') : t('node.service_restart')}</button> + `} + </div> + ${info.mode === 'service' && html` + <p class="node-hint">${t('node.service_mode_hint')}</p> `} </div> - ${info.mode === 'service' && html` - <p class="node-hint">${t('node.service_mode_hint')}</p> - `} - ${platform.node.autostart.available && typeof info.autostart === 'boolean' && html` - <label class="toggle-switch ${busy ? 'toggle-switch-disabled' : ''}"> - <input type="checkbox" checked=${info.autostart} disabled=${!!busy} - onChange=${toggleAutostart} /> - <span class="toggle-switch-track"><span class="toggle-switch-thumb"></span></span> - ${' '}${busy === 'autostart' ? t('node.autostart_updating') : t('node.autostart_label')} - </label> - `} - ${platform.node.serviceMode.available && typeof info.mode === 'string' && html` - <label class="toggle-switch ${busy ? 'toggle-switch-disabled' : ''}"> - <input type="checkbox" checked=${info.mode === 'service'} disabled=${!!busy} - onChange=${toggleServiceMode} /> - <span class="toggle-switch-track"><span class="toggle-switch-thumb"></span></span> - ${' '}${busy === 'serviceMode' ? t('node.service_mode_updating') : t('node.service_mode_label')} - </label> + ${showStartupRow && html` + <div class="settings-row"> + <span class="settings-label">${t('node.startup_mode_label')}</span> + <select class="settings-select" disabled=${!!busy} + value=${startupMode(info)} + onChange=${(e) => changeStartupMode(e.target.value)}> + <option value="off">${t('node.startup_mode_off')}</option> + <option value="signin">${t('node.startup_mode_signin')}</option> + <option value="service" disabled=${!info.canElevate}> + ${t('node.startup_mode_service')}</option> + </select> + </div> + ${busy === 'startupMode' && html` + <p class="settings-hint">${t('node.startup_mode_updating')}</p>`} + ${!info.canElevate && html` + <p class="settings-hint">${t('node.startup_mode_service_unavailable_hint')}</p>`} `} </div>`; } |