Skip to content

feat(collection): batch set/clear dance level in multi-select (#409)#422

Merged
ibanner56 merged 3 commits into
mainfrom
isaacbanner-bulk-add-tags-409
Jul 21, 2026
Merged

feat(collection): batch set/clear dance level in multi-select (#409)#422
ibanner56 merged 3 commits into
mainfrom
isaacbanner-bulk-add-tags-409

Conversation

@ibanner56

Copy link
Copy Markdown
Owner

What & why

Closes part of #409 ("Ability to add tags en-masse").

Scope note (important): The issue's title asks for bulk tagging, but batch Add tags / Remove tags already ship in multi-select (landed in #93, v0.1.0-beta.3) — as the issue owner's own triage comment confirms. The genuinely new ask is "add metadata/other info like level". This PR delivers that for level, reusing the existing batch-tag flow. rating / tunes / customFields are deferred to a follow-up issue to keep this atomic.

Changes

  • Core DanceRepository.setLevelForMany(ids, {level, clearLevel, now}) — a single-transaction batched write that reuses the existing upsert path (keeping derived figure/FTS indexes consistent), skips unknown ids and dances already at the target level (idempotent), and returns the changed count. Running the whole batch in one transaction means an error leaves the collection untouched rather than half-updated. Stays Flutter-free.
  • App new batch_level_dialog.dart single-choice picker (mirrors batch_tag_dialog.dart) with an explicit Unspecified (clear) option, using the repo's RadioGroup pattern.
  • App dance_list_screen.dart: _batchSetLevel + _undoBatchLevel and a batch-set-level action in the selection app bar. Captures prior per-dance levels for a snackbar Undo, announces the result to assistive tech, and handles empty-selection / no-op cases.

Semantics

Setting a level is a replace (not additive like tags), so the picker is single-choice and the action is explicit + Undo-restorable. Idempotent (dances already at the target are skipped -> "No changes" when nothing changes). Non-selected dances are never touched. Empty selection disables the button and is a guarded no-op.

Tests & gates

  • Core: dance_batch_level_test.dart — sets level on N ids in one txn; unknown ids ignored; empty list no-op; idempotent skip; clearLevel unsets and wins over a passed value; updatedAt stamped only on changed dances.
  • App: batch_level_test.dart — applies to all selected; un-selected untouched; idempotent; clear path; Undo restores prior per-dance levels; no-op message; button disabled on empty selection.

Gates (exact):

  • fvm dart format . — clean
  • fvm flutter analyze — No issues found (0/0)
  • core fvm dart test — 1804 passed, 4 skipped
  • app fvm flutter test — 1651 passed

Co-authored-by: Copilot App 223556219+Copilot@users.noreply.github.com

Adds a 'Set level' action to the Collection multi-select bar, letting a
caller apply (or clear) a difficulty level across many selected dances at
once, parallel to the existing batch tag add/remove flow.

Core gains DanceRepository.setLevelForMany, a single-transaction batched
write that reuses the existing upsert path, skips unknown ids and dances
already at the target level (idempotent), and returns the changed count so
an error leaves the collection untouched rather than half-updated.

The app-side flow captures prior per-dance levels for a snackbar Undo,
announces the result to assistive tech, and treats an empty selection or a
no-op change accordingly.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new Collection multi-select action to batch set or clear a dance’s difficulty level, implemented with a single-transaction core repository method and a new app dialog + undo flow.

Changes:

  • Core: add DanceRepository.setLevelForMany(...) to update many dances’ level atomically and idempotently.
  • App: add a batch level picker dialog with an explicit “Unspecified (clear)” option.
  • App: wire a new selection-mode action to apply the batch change with snackbar + Undo, plus widget tests.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
packages/compendium_core/lib/src/storage/repositories/dance_repository.dart Adds batched repository API for setting/clearing level in one transaction.
packages/compendium_core/test/storage/dance_batch_level_test.dart Adds core tests for batch set/clear/idempotency/unknown IDs.
app/lib/src/widgets/batch_level_dialog.dart New single-choice batch level dialog with explicit clear option.
app/lib/src/screens/dance_list_screen.dart Adds selection-mode “Set level” action with apply + undo + AT announcement.
app/test/screens/batch_level_test.dart Adds widget tests covering apply/clear/idempotency/undo/disabled state.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

ibanner56 and others added 2 commits July 20, 2026 17:48
#409)

Require a non-null level unless clearLevel is true, so calling the batch
setter with default args can no longer silently wipe every selected
dance's level. Documents the mutually-exclusive set-vs-clear contract and
adds a test asserting the guard fires.

Addresses Copilot review feedback on #422.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
… call (#409)

Asserts are stripped in release, so the debug-only guard alone left a
latent footgun: a caller omitting level: (with clearLevel default false)
would silently wipe every selected dance's level in production. Add a
release-safe ArgumentError thrown before the assert so the contract holds
in all build modes; clearLevel still wins when both are passed. Updates the
core test to expect ArgumentError and verify the existing level survives.

Addresses Copilot review feedback on #422.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@ibanner56
ibanner56 merged commit 75ee8b7 into main Jul 21, 2026
7 checks passed
@ibanner56
ibanner56 deleted the isaacbanner-bulk-add-tags-409 branch July 21, 2026 01:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants