summaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/src/meshbay_hub/static/app.js
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/app.js')
-rw-r--r--packages/meshbay-hub/src/meshbay_hub/static/app.js77
1 files changed, 77 insertions, 0 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/app.js b/packages/meshbay-hub/src/meshbay_hub/static/app.js
index 4aab150..c31653a 100644
--- a/packages/meshbay-hub/src/meshbay_hub/static/app.js
+++ b/packages/meshbay-hub/src/meshbay_hub/static/app.js
@@ -2373,6 +2373,9 @@ function NodePage({ token, username, userId, groups }) {
const [rosterGroup, setRosterGroup] = useState('');
const [denylist, setDenylist] = useState(null);
const [showDenylist, setShowDenylist] = useState(false);
+ const [nodeSettings, setNodeSettings] = useState(null);
+ const [editSettings, setEditSettings] = useState(null);
+ const [savingSettings, setSavingSettings] = useState(false);
const [attachGroup_, setAttachGroup_] = useState('');
const [attachDir, setAttachDir] = useState('');
const [attachUpload, setAttachUpload] = useState('');
@@ -2434,6 +2437,7 @@ function NodePage({ token, username, userId, groups }) {
transportRef.current = transport;
setNodeGroups(result.groups || []);
setOperatorPaired(!!result.operator_paired);
+ setNodeSettings(result.settings || null);
setStatus('connected');
return;
} catch (err) {
@@ -2460,6 +2464,10 @@ function NodePage({ token, username, userId, groups }) {
};
}, [connectAndFetch]);
+ useEffect(() => {
+ if (nodeSettings && !editSettings) setEditSettings({ ...nodeSettings });
+ }, [nodeSettings]);
+
const refresh = useCallback(async () => {
const transport = transportRef.current;
if (!transport || !transport.connected) return;
@@ -2467,6 +2475,7 @@ function NodePage({ token, username, userId, groups }) {
const result = await transport.fetchNodeStatus();
setNodeGroups(result.groups || []);
setOperatorPaired(!!result.operator_paired);
+ setNodeSettings(result.settings || null);
} catch {}
}, []);
@@ -2673,6 +2682,22 @@ function NodePage({ token, username, userId, groups }) {
}
}, [refresh]);
+ const saveSettings = useCallback(async () => {
+ const transport = transportRef.current;
+ if (!transport || !transport.connected || !editSettings) return;
+ setSavingSettings(true);
+ setActionMsg('');
+ try {
+ await transport.updateNodeSettings(editSettings);
+ setNodeSettings({ ...editSettings });
+ setActionMsg(t('node.settings_saved'));
+ } catch (err) {
+ setActionMsg(err.message);
+ } finally {
+ setSavingSettings(false);
+ }
+ }, [editSettings]);
+
if (status === 'idle' || status === 'connecting') {
return html`<div class="page-content">
<h2>${t('node.title')}</h2>
@@ -2888,6 +2913,58 @@ function NodePage({ token, username, userId, groups }) {
`}
</div>
+ ${editSettings && html`
+ <div class="node-group">
+ <span class="settings-heading">${t('node.settings')}</span>
+ <p class="node-hint">${t('node.settings_edit_hint')}</p>
+ <div class="node-settings-grid">
+ <label class="node-setting">
+ <span class="node-setting-label">${t('node.setting_invite_ttl')}</span>
+ <div class="node-setting-input">
+ <input type="number" min="1" value=${editSettings.invite_ttl_hours}
+ onInput=${e => setEditSettings(s => ({...s, invite_ttl_hours: parseInt(e.target.value) || 1}))} />
+ <span class="node-setting-unit">${t('node.setting_unit_hours')}</span>
+ </div>
+ </label>
+ <label class="node-setting">
+ <span class="node-setting-label">${t('node.setting_pair_ttl')}</span>
+ <div class="node-setting-input">
+ <input type="number" min="1" value=${editSettings.pair_ttl_hours}
+ onInput=${e => setEditSettings(s => ({...s, pair_ttl_hours: parseInt(e.target.value) || 1}))} />
+ <span class="node-setting-unit">${t('node.setting_unit_hours')}</span>
+ </div>
+ </label>
+ <label class="node-setting">
+ <span class="node-setting-label">${t('node.setting_device_ttl')}</span>
+ <div class="node-setting-input">
+ <input type="number" min="1" value=${editSettings.device_request_ttl_minutes}
+ onInput=${e => setEditSettings(s => ({...s, device_request_ttl_minutes: parseInt(e.target.value) || 1}))} />
+ <span class="node-setting-unit">${t('node.setting_unit_minutes')}</span>
+ </div>
+ </label>
+ <label class="node-setting">
+ <span class="node-setting-label">${t('node.setting_max_streams')}</span>
+ <div class="node-setting-input">
+ <input type="number" min="1" value=${editSettings.max_concurrent_streams}
+ onInput=${e => setEditSettings(s => ({...s, max_concurrent_streams: parseInt(e.target.value) || 1}))} />
+ </div>
+ </label>
+ <label class="node-setting">
+ <span class="node-setting-label">${t('node.setting_transcode')}</span>
+ <div class="node-setting-input">
+ <input type="checkbox" checked=${editSettings.transcode_incompatible_video}
+ onChange=${e => setEditSettings(s => ({...s, transcode_incompatible_video: e.target.checked}))} />
+ </div>
+ </label>
+ </div>
+ <div class="node-settings-actions">
+ <button class="btn btn-primary btn-small" disabled=${savingSettings || busy}
+ onClick=${saveSettings}>
+ ${savingSettings ? t('node.settings_saving') : t('node.settings_save')}</button>
+ </div>
+ </div>
+ `}
+
${/* In Electron, the Create Group wizard handles attaching. Keep for
browser users who manage nodes via MNP. */
!platform.node.available && (() => {