From 3817bc61bb8f31daa99eb75e904c391969062a0f Mon Sep 17 00:00:00 2001 From: David Allison <62114487+david-allison@users.noreply.github.com> Date: Thu, 16 Jan 2025 21:27:44 +0000 Subject: [PATCH] feat(deck-picker): make 'empty filtered' undoable --- .../src/main/java/com/ichi2/anki/DeckPicker.kt | 2 +- .../ichi2/anki/deckpicker/DeckPickerViewModel.kt | 3 +-- .../java/com/ichi2/libanki/sched/Scheduler.kt | 4 +--- .../anki/deckpicker/DeckPickerViewModelTest.kt | 15 +++++++++++++++ 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt b/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt index 050999cee194..91a7a8b4e4d8 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt @@ -2662,7 +2662,7 @@ open class DeckPicker : changes: OpChanges, handler: Any?, ) { - if (changes.studyQueues && handler !== this) { + if (changes.studyQueues && handler !== this && handler !== viewModel) { invalidateOptionsMenu() if (!activityPaused) { // No need to update while the activity is paused, because `onResume` calls `refreshState` that calls `updateDeckList`. diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/deckpicker/DeckPickerViewModel.kt b/AnkiDroid/src/main/java/com/ichi2/anki/deckpicker/DeckPickerViewModel.kt index 3339fc17ff89..2b478a224eaa 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/deckpicker/DeckPickerViewModel.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/deckpicker/DeckPickerViewModel.kt @@ -102,12 +102,11 @@ class DeckPickerViewModel : ViewModel() { } // TODO: move withProgress to the ViewModel, so we don't return 'Job' - // TODO: undoableOp { } on emptyDyn fun emptyFilteredDeck(deckId: DeckId): Job = viewModelScope.launch { Timber.i("empty filtered deck %s", deckId) withCol { decks.select(deckId) } - withCol { sched.emptyDyn(decks.selected()) } + undoableOp { sched.emptyDyn(decks.selected()) } flowOfDeckCountsChanged.emit(Unit) } } diff --git a/AnkiDroid/src/main/java/com/ichi2/libanki/sched/Scheduler.kt b/AnkiDroid/src/main/java/com/ichi2/libanki/sched/Scheduler.kt index ff1d0612373a..c0cb9303f98f 100644 --- a/AnkiDroid/src/main/java/com/ichi2/libanki/sched/Scheduler.kt +++ b/AnkiDroid/src/main/java/com/ichi2/libanki/sched/Scheduler.kt @@ -449,9 +449,7 @@ open class Scheduler( /** Remove all cards from a dynamic deck * @param did The deck to empty. 0 means current deck. */ - open fun emptyDyn(did: DeckId) { - col.backend.emptyFilteredDeck(did) - } + open fun emptyDyn(did: DeckId) = col.backend.emptyFilteredDeck(did) fun deckDueTree(): DeckNode = deckTree(true) diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/deckpicker/DeckPickerViewModelTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/deckpicker/DeckPickerViewModelTest.kt index ef5a2a20ff6f..2bdf421a7f94 100644 --- a/AnkiDroid/src/test/java/com/ichi2/anki/deckpicker/DeckPickerViewModelTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/anki/deckpicker/DeckPickerViewModelTest.kt @@ -116,6 +116,21 @@ class DeckPickerViewModelTest : RobolectricTest() { } } + @Test + fun `empty filtered - undoable`() { + runTest { + val filteredDeckId = moveAllCardsToFilteredDeck() + + // ChangeManager assert + ensureOpsExecuted(1) { + viewModel.emptyFilteredDeck(filteredDeckId).join() + } + + // backend assert + assertThat("col undo status", col.undoStatus().undo, equalTo("Empty")) + } + } + @CheckResult private suspend fun createEmptyCards(): List { addNoteUsingNoteTypeName("Cloze", "{{c1::Hello}} {{c2::World}}", "").apply {