fix(savebar): save all changed sections in a group with one click (#3552)#3558
Conversation
) The unified SaveBar only saved the active section, so editing multiple sections before clicking Save persisted just one — the others appeared to silently revert. Introduce an optional section "group": sections sharing a group are saved and dismissed together by a single "Save All" action. The group is supplied via a <SaveBarGroup> provider that wraps a render region, so sections inherit it from context without per-section edits. Settings, Automation, the Global Settings page, and the MeshCore Automations view are now grouped (the Settings tab itself contains two registered sections — SettingsTab and the nested PositionEstimationSection — which is the Settings manifestation of the bug). Device Configuration is intentionally left ungrouped so each section keeps saving individually, since each is a separate device admin write. Batch saves run sequentially and guard each section so one failure doesn't abort the rest. Behavior for ungrouped sections is unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SVGxkuD4Fwa2JGVim8ZeVj
|
Claude finished @Yeraze's task —— View job PR Review: fix(savebar): save all changed sections in a group with one click (#3552)
Overall this is a well-scoped and clean fix. The context-propagation approach (wrap a region in Issues found1. The sequential loop calls each section's 2. Group resolution uses const group = options.group !== undefined ? options.group ?? undefined : inheritedGroup ?? undefined;The double-null-coalescing is hard to read. When 3. const showTabs = !effectiveSection.group && sectionsWithChanges.length > 1;If sections from mixed groups and ungrouped sections coexist, 4. Badge renders in wrong position for batch mode (SaveBar.tsx:117–121) {showTabs && (
<div className="save-bar-badge">
{sectionsWithChanges.length}
</div>
)}The badge only renders when 5. {view === 'automations' && (
<SaveBarProvider>
<SaveBarGroup id="meshcore-automation">
<MeshCoreAutomationsView ... />
</SaveBarGroup>
<SaveBar />
</SaveBarProvider>
)}This works, but it means MeshCore automations use a completely separate Minor nits
Test coverage assessmentThe 4 tests cover the core regression scenarios well:
Missing test coverage:
None of these are blockers for merging given the bug is fixed and the primary paths are covered. SummaryThe fix is correct, the architecture is clean, and the test suite addresses the regression. The main actionable issues are:
Neither is a blocker. The rest are observations for future hardening. |
…lution - SaveBar: track isBatchSaving for the whole sequential batch so Save/Dismiss stay disabled between sections (closes the brief re-enable/double-click window the reviewer flagged), with a regression test holding one save open. - useSaveBar: replace the double null-coalescing group resolution with an explicit two-branch form + comment. - SaveBar.test: document why the ungrouped assertion is order-independent (auto-select makes the last-registered section active, not the first). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SVGxkuD4Fwa2JGVim8ZeVj
|
Thanks for the review — addressed the actionable items in cafe37f:
Declining (with reasons) — all consistent with existing architecture, no change:
|
|
Claude finished @Yeraze's task —— View job PR Review: fix(savebar): save all changed sections in a group with one click (#3552)
This is a well-structured fix. The previous review's actionable items were addressed in What's working wellArchitecture: The Batch-save resilience (
Group resolution ( Issues found1. }, [id, sectionName, group, registerSection, unregisterSection, stableOnSave, stableOnDismiss]); // eslint-disable-line react-hooks/exhaustive-depsThe lint suppression is intentional (and the separation into a second 2. const groupSections = effectiveSection.group
? sectionsWithChanges.filter(s => s.group === effectiveSection.group)
: [effectiveSection];
3. {view === 'automations' && (
<SaveBarProvider>
<SaveBarGroup id="meshcore-automation">
...
</SaveBarGroup>
<SaveBar />
</SaveBarProvider>
)}The Minor nits
Test coverageThe 5 tests (4 original + the new in-flight disabled test) cover the regression well:
The SummaryThe fix correctly solves the root cause, the previous review's issues were addressed properly, and the implementation is clean. The issues I've noted above are either pre-existing (nested provider), non-regressions (mixed provider edge case), or minor readability improvements. No blockers. --- | Branch: |
|
Thanks for the second pass. The three remaining items are non-blocking nits (no behavior change), so I'm leaving the code as-is to avoid churn — reasoning below:
CI is green across the matrix. Ready for merge from my side. |
Summary
Fixes #3552 — editing settings across multiple sections before clicking Save only persisted some changes; the others silently reverted.
Root cause: the unified
SaveBaronly calledeffectiveSection.onSave()— the single active section. Any other section with unsaved changes was never saved.Fix
Introduce an optional section group. Sections sharing a group are saved (and dismissed) together by a single "Save All" action. The group is supplied via a
<SaveBarGroup id="…">provider wrapping a render region, so sections inherit it from context — no need to edit each of the ~25 section files.SaveBarContext.tsx—group?onSaveBarSection; newSaveBarGroupprovider +useSaveBarGrouphook.useSaveBar.ts— sections inherit the nearestSaveBarGroup(explicitgroupoption still wins; passnullto opt out).SaveBar.tsx— grouped active section ⇒ Save/Dismiss apply to every changed section in the group; saves run sequentially, each guarded so one failure doesn't abort the batch. Shows "Save All" with the changed-section count in the message text.App.tsx), the Global Settings page, and MeshCore Automations (MeshCorePage.tsx) inSaveBarGroup.en.json—savebar.save_all/savebar.save_all_changes.Device Configuration is intentionally left ungrouped — each section is a separate device admin write, so it keeps the per-section tab behavior, exactly as before.
Behavior change
Testing
SaveBar.test.tsx(4 tests): grouped batch-save, batch-dismiss, resilience when one save throws, and ungrouped sections still save only the active section.🤖 Generated with Claude Code