diff options
Diffstat (limited to 'packages/meshbay-hub/src/meshbay_hub/static/create-group-page.js')
| -rw-r--r-- | packages/meshbay-hub/src/meshbay_hub/static/create-group-page.js | 110 |
1 files changed, 23 insertions, 87 deletions
diff --git a/packages/meshbay-hub/src/meshbay_hub/static/create-group-page.js b/packages/meshbay-hub/src/meshbay_hub/static/create-group-page.js index d86521f..c3b542f 100644 --- a/packages/meshbay-hub/src/meshbay_hub/static/create-group-page.js +++ b/packages/meshbay-hub/src/meshbay_hub/static/create-group-page.js @@ -5,7 +5,7 @@ import { t } from './i18n.js'; import { HUB, hubFetch, session, navigate } from './hub-client.js'; import * as platform from './platform.js'; import { Icon } from './icon.js'; -import { APPS } from './apps.js'; +import { SharedDirectoriesTable } from './group-settings.js'; export function CreateGroupPage(props) { if (platform.node.available) return html`<${CreateGroupWizard} ...${props} />`; @@ -109,13 +109,6 @@ function CreateGroupWizard({ token, username, onCreated, allowPublicGroups = tru const [description, setDescription] = useState(''); const [joinPolicy, setJoinPolicy] = useState('invite'); const [roots, setRoots] = useState([]); - const [uploadIdx, setUploadIdx] = useState(0); - const [enabledApps, setEnabledApps] = useState(() => APPS.map(a => a.key)); - const toggleWizardApp = useCallback((key) => { - setEnabledApps(prev => prev.includes(key) - ? prev.filter(k => k !== key) - : [...prev, key]); - }, []); const [setupSteps, setSetupSteps] = useState([]); const [setupError, setSetupError] = useState(''); @@ -166,20 +159,9 @@ function CreateGroupWizard({ token, username, onCreated, allowPublicGroups = tru useEffect(() => { detectNode(); }, [detectNode]); - const addRoot = useCallback(async () => { - const chosen = await platform.rootPicker.choose(); - if (!chosen) return; - if (roots.some(r => r.path === chosen.path)) return; - setRoots(prev => [...prev, chosen]); - }, [roots]); - - const removeRoot = useCallback((idx) => { - setRoots(prev => { - const next = prev.filter((_, i) => i !== idx); - if (uploadIdx >= next.length && next.length > 0) setUploadIdx(0); - return next; - }); - }, [uploadIdx]); + const handleLocalRootsChange = useCallback((newRoots) => { + setRoots(newRoots); + }, []); const runSetup = useCallback(async () => { setStep(2); @@ -189,7 +171,6 @@ function CreateGroupWizard({ token, username, onCreated, allowPublicGroups = tru { label: t('wizard.step_attach'), status: 'pending' }, ]; steps.push({ label: t('wizard.step_index'), status: 'pending' }); - steps.push({ label: t('wizard.step_apps'), status: 'pending' }); if (roots.length > 1) steps.push({ label: t('wizard.step_add_roots'), status: 'pending' }); steps.push({ label: t('wizard.step_gek'), status: 'pending' }); @@ -231,11 +212,16 @@ function CreateGroupWizard({ token, username, onCreated, allowPublicGroups = tru // 2. Attach to node with first root update('running'); - const mainRoot = roots[uploadIdx] || roots[0]; - const attachBody = { name: name.trim(), shared_dir: mainRoot.path }; - if (roots.length === 1 || uploadIdx === 0) { - attachBody.upload_dir = mainRoot.path; - } + // The first root is attached with the group, so its RW switch has to + // travel with it — writing it and then correcting it afterwards would + // leave a window where a group the operator marked read-only accepts + // uploads. + const mainRoot = roots[0]; + const attachBody = { + name: name.trim(), + shared_dir: mainRoot.path, + writable: mainRoot.writable !== false, + }; await platform.node.call('POST', '/api/groups/attach', attachBody); await platform.node.call('POST', '/api/reload'); update('done'); @@ -247,22 +233,13 @@ function CreateGroupWizard({ token, username, onCreated, allowPublicGroups = tru update('done'); advance(); - // 4. Set enabled apps - update('running'); - await withRetry(() => platform.node.call( - 'PUT', `/api/groups/${gid}/apps`, { apps: enabledApps })); - update('done'); - advance(); - - // 5. Add extra roots (if >1) + // 4. Add extra roots (if >1) if (roots.length > 1) { update('running'); - for (let i = 0; i < roots.length; i++) { - if (i === (uploadIdx < roots.length ? uploadIdx : 0)) continue; + for (let i = 1; i < roots.length; i++) { const r = roots[i]; await withRetry(() => platform.node.call('POST', `/api/groups/${gid}/roots`, { - path: r.path, name: r.name, - upload: i === uploadIdx, + path: r.path, name: r.name, writable: !!r.writable, removable: !!r.removable, })); } await platform.waitForRootsIndexed(gid, setIndexProgress); @@ -270,13 +247,13 @@ function CreateGroupWizard({ token, username, onCreated, allowPublicGroups = tru advance(); } - // 6. GEK init + // 5. GEK init update('running'); await withRetry(() => platform.node.call('POST', `/api/groups/${gid}/gek`)); update('done'); advance(); - // 7. Generate pairing code + // 6. Generate pairing code update('running'); const pairResult = await platform.node.call('POST', '/api/operator/pair'); if (pairResult && pairResult.code) { @@ -293,7 +270,7 @@ function CreateGroupWizard({ token, username, onCreated, allowPublicGroups = tru update('error'); setSetupError(platform.bridgeMessage(err)); } - }, [name, description, joinPolicy, roots, uploadIdx, enabledApps, token, onCreated]); + }, [name, description, joinPolicy, roots, token, onCreated]); // Step 0: Node detection if (step === 0) { @@ -349,7 +326,7 @@ function CreateGroupWizard({ token, username, onCreated, allowPublicGroups = tru </div>`; } if (step === 1) { - const canProceed = name.trim() && roots.length > 0 && enabledApps.length > 0; + const canProceed = name.trim() && roots.length > 0; return html`<div class="page-content"> <h2>${t('wizard.title')}</h2> ${error && html`<div class="error-msg" style="margin-bottom:16px">${error}</div>`} @@ -398,52 +375,11 @@ function CreateGroupWizard({ token, username, onCreated, allowPublicGroups = tru `} <div class="settings-section"> - <h3 class="settings-heading">${t('members.apps_title')}</h3> - <p style="font-size:0.85em;color:var(--text-dim);margin-bottom:8px"> - ${t('members.apps_hint')}</p> - <ul class="apps-toggle-list"> - ${APPS.map(a => html` - <li key=${a.key} class="settings-row"> - <label class="settings-label"> - <input type="checkbox" checked=${enabledApps.includes(a.key)} - onChange=${() => toggleWizardApp(a.key)} /> - ${' '}${t(a.labelKey)} - </label> - </li> - `)} - </ul> - ${enabledApps.length === 0 && html` - <p class="error-msg">${t('members.apps_need_one')}</p>`} - </div> - - <div class="settings-section"> <h3 class="settings-heading">${t('wizard.directories')}</h3> <p style="font-size:0.85em;color:var(--text-dim);margin-bottom:8px"> ${t('wizard.directories_hint')}</p> - ${roots.map((r, i) => html` - <div class="wizard-root" key=${r.path}> - <div class="wizard-root-info"> - <${Icon} name="folder" /> - <span class="wizard-root-name">${r.name}</span> - <span class="wizard-root-path">${r.path}</span> - ${i === uploadIdx && html` - <span class="node-root-badge">${t('wizard.upload_target')}</span>`} - </div> - <div class="wizard-root-actions"> - ${roots.length > 1 && i !== uploadIdx && html` - <button class="btn btn-small btn-secondary" - onClick=${() => setUploadIdx(i)}> - ${t('wizard.set_upload')}</button>`} - <button class="btn btn-small btn-danger" - onClick=${() => removeRoot(i)}> - ${t('wizard.remove')}</button> - </div> - </div> - `)} - <button class="btn btn-secondary" style="margin-top:8px" - onClick=${addRoot}> - <${Icon} name="folder-plus" /> ${t('wizard.add_directory')} - </button> + <${SharedDirectoriesTable} mode="local" + localRoots=${roots} onLocalRootsChange=${handleLocalRootsChange} /> </div> <div style="display:flex;gap:8px;margin-top:16px"> |