{{ message }}
fix(filtered-deck): restore state on recreation (#21463) - #21465
Merged
criticalAY merged 1 commit intoAug 16, 2026
Conversation
Contributor
david-allison
approved these changes
Aug 11, 2026
david-allison
approved these changes
Aug 12, 2026
Contributor
Author
|
Oh! sorry. I tapped it by mistake.. |
Three related defects in FilteredDeckOptionsViewModel: The init block returned early when a previous state was found, skipping both decksNames and initialState. After the view model was recreated (process death or a system-initiated restore) decksNames stayed empty, so duplicate name validation never fired, and initialState stayed null, so wasStateModified() always returned false and the discard-changes dialog never showed. decksNames is now loaded on both paths and initialState is persisted under its own key. Separately, safeGetDecksNames() includes filtered decks, so renaming an existing filtered deck and reverting to its original name was flagged as a duplicate. The deck's own name is now excluded when editing. Verified by mutation testing that each change is needed independently. Fixes ankidroid#21463
criticalAY
force-pushed
the
fix/filtered-deck-options-restore-21463
branch
from
August 16, 2026 02:25
7073928 to
a59b388
Compare
Contributor
|
criticalAY
enabled auto-merge
August 16, 2026 02:25
Contributor
Author
github-merge-queue
Bot
removed this pull request from the merge queue due to failed status checks
Aug 16, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

this fixes the 3 things from the issue - after the screen gets recreated the duplicate name check
and the discard changes dialog both stop working, plus renaming a filtered deck back to its own
name was getting flagged as duplicate.
Purpose / Description
the init block in FilteredDeckOptionsViewModel was returning early whenever there was a previous
state saved:
problem is two things below that return never ran. decksNames stayed as emptyList() so the
decksNames.contains(name) check inside onDeckNameChange could never be true, and initialState
stayed null so wasStateModified() hit
val initial = initialState ?: return falseand alwaysreturned false. so after a restore you could type a name that already exist and get no error at
all (Build stays enabled, you just get a raw backend exception when you press it), and pressing
back would throw away all your edits without asking anything.
the third one is unrelated to restore - safeGetDecksNames() uses includeFiltered = true so the
list also has the deck you are editing right now in it. so if you rename an existing filtered deck
and then put the original name back it says "already exists" and disables Rebuild.
Approach
moved
decksNames = withCol { safeGetDecksNames() }above the early return since both paths needit. for initialState i couldnt just read it back from ARG_DATA because that key holds the edited
state, not the original one, so its saved under its own key (ARG_INITIAL_DATA) and read back on
the restore path.
for the third one i used the existing
titlefield - its already documented as the name from whenthe deck was first loaded and it doesnt change for the lifetime of the screen, so its exactly what
was needed. only applied it when id != null so it doesnt affect creating new decks.
How Has This Been Tested?
added 3 tests to FilteredDeckOptionsViewModelTest, all of them failed before the fix:
name validation still works after the screen is restored -> expected but was
changed status still works after the screen is restored -> Expected value to be true
reverting to the deck's own name is not a duplicate -> expected but was
for simulating the restore theres a small withRestoredViewModel helper - it builds a view model,
throws it away and builds a second one over the same SavedStateHandle, which is basically what
happens when the VM dies but the handle survives.
also did mutation testing to make sure none of the 3 changes are redundant:
whole com.ichi2.anki.filtered package is green after (17 tests), ktlint passes.
Checklist