From e76e27868b30a2b00b1ba42dd8e7ee6071e0c0d7 Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Sun, 6 Sep 2026 16:05:39 +0200 Subject: feat: groups refactor Phase 1 — root RO/RW model + shared directories UI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the upload boolean with per-root writable/removable/ejected flags. Backend: new ops (update_root, eject_root, plug_root), MNP 1.1 protocol messages, live RootSet updates so API always reflects current state, CLI root subcommand (add/remove/set/list/eject/plug). Frontend: SharedDirectoriesTable with optimistic toggle switches, eject/plug in Files and Settings, upload gated on root.writable, ejected-root filtering in all media apps, updated Create Group wizard, 10-locale i18n. Co-Authored-By: Claude Opus 4.6 --- .../src/meshbay_hub/static/create-group-page.js | 100 +++------------------ 1 file changed, 14 insertions(+), 86 deletions(-) (limited to 'packages/meshbay-hub/src/meshbay_hub/static/create-group-page.js') 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..a5676ba 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,8 @@ function CreateGroupWizard({ token, username, onCreated, allowPublicGroups = tru // 2. Attach to node with first root update('running'); - const mainRoot = roots[uploadIdx] || roots[0]; + const mainRoot = roots[0]; const attachBody = { name: name.trim(), shared_dir: mainRoot.path }; - if (roots.length === 1 || uploadIdx === 0) { - attachBody.upload_dir = mainRoot.path; - } await platform.node.call('POST', '/api/groups/attach', attachBody); await platform.node.call('POST', '/api/reload'); update('done'); @@ -247,22 +225,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 +239,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 +262,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 +318,7 @@ function CreateGroupWizard({ token, username, onCreated, allowPublicGroups = tru `; } if (step === 1) { - const canProceed = name.trim() && roots.length > 0 && enabledApps.length > 0; + const canProceed = name.trim() && roots.length > 0; return html`

${t('wizard.title')}

${error && html`
${error}
`} @@ -397,53 +366,12 @@ function CreateGroupWizard({ token, username, onCreated, allowPublicGroups = tru
`} -
-

${t('members.apps_title')}

-

- ${t('members.apps_hint')}

-
    - ${APPS.map(a => html` -
  • - -
  • - `)} -
- ${enabledApps.length === 0 && html` -

${t('members.apps_need_one')}

`} -
-

${t('wizard.directories')}

${t('wizard.directories_hint')}

- ${roots.map((r, i) => html` -
-
- <${Icon} name="folder" /> - ${r.name} - ${r.path} - ${i === uploadIdx && html` - ${t('wizard.upload_target')}`} -
-
- ${roots.length > 1 && i !== uploadIdx && html` - `} - -
-
- `)} - + <${SharedDirectoriesTable} mode="local" + localRoots=${roots} onLocalRootsChange=${handleLocalRootsChange} />
-- cgit v1.2.3