Skip to content

Commit

Permalink
CardBrowser: refactor 'cardsOrNotes' to Flow (ankidroid#14892)
Browse files Browse the repository at this point in the history
* refactor: extract 'setCardsOrNotes' to VM
* refactor: 'cardsOrNotes' init -> ViewModel
* refactor: 'cardsOrNotes' -> Flow
* introduce flow
* remove switchCardOrNote
* refactor: inline 'cardsOrNotes'
  • Loading branch information
david-allison authored Dec 7, 2023
1 parent f585293 commit 17a6bd4
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 31 deletions.
36 changes: 11 additions & 25 deletions AnkiDroid/src/main/java/com/ichi2/anki/CardBrowser.kt
Original file line number Diff line number Diff line change
Expand Up @@ -160,16 +160,6 @@ open class CardBrowser :

private lateinit var mExportingDelegate: ActivityExportingDelegate

/**
* Boolean that keeps track of whether the browser is working in
* Cards mode or Notes mode.
* True by default.
* */
@get:VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
var cardsOrNotes
get() = viewModel.cardsOrNotes
set(value) { viewModel.cardsOrNotes = value }

// card that was clicked (not marked)
private var mCurrentCardId
get() = viewModel.currentCardId
Expand Down Expand Up @@ -492,6 +482,11 @@ open class CardBrowser :
.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
.onEach { cardsAdapter.notifyDataSetChanged() }
.launchIn(lifecycleScope)

viewModel.cardsOrNotesFlow
.flowWithLifecycle(lifecycle, Lifecycle.State.STARTED)
.onEach { runOnUiThread { searchCards() } }
.launchIn(lifecycleScope)
}

fun searchWithFilterQuery(filterQuery: String) {
Expand Down Expand Up @@ -622,7 +617,6 @@ open class CardBrowser :
alwaysShowDefault = false,
showFilteredDecks = true
)
cardsOrNotes = CardsOrNotes.fromCollection(col)
deckSpinnerSelection!!.initializeActionBarDeckSpinner(this.supportActionBar!!)
selectDeckAndSave(deckId)

Expand Down Expand Up @@ -928,14 +922,14 @@ open class CardBrowser :
}
}
mActionBarMenu!!.findItem(R.id.action_export_selected).apply {
this.title = if (cardsOrNotes == CARDS) {
this.title = if (viewModel.cardsOrNotes == CARDS) {
resources.getQuantityString(R.plurals.card_browser_export_cards, checkedCardCount())
} else {
resources.getQuantityString(R.plurals.card_browser_export_notes, checkedCardCount())
}
}
mActionBarMenu!!.findItem(R.id.action_delete_card).apply {
this.title = if (cardsOrNotes == CARDS) {
this.title = if (viewModel.cardsOrNotes == CARDS) {
resources.getQuantityString(R.plurals.card_browser_delete_cards, checkedCardCount())
} else {
resources.getQuantityString(R.plurals.card_browser_delete_notes, checkedCardCount())
Expand Down Expand Up @@ -1215,20 +1209,12 @@ open class CardBrowser :
return super.onOptionsItemSelected(item)
}

fun switchCardOrNote(newCardsMode: CardsOrNotes) {
launchCatchingTask {
withCol { newCardsMode.saveToCollection(this) }
cardsOrNotes = newCardsMode
searchCards()
}
}

fun exportSelected() {
if (!isInMultiSelectMode) {
return
}

if (cardsOrNotes == CARDS) {
if (viewModel.cardsOrNotes == CARDS) {
mExportingDelegate.showExportDialog(
ExportDialogParams(
message = resources.getQuantityString(R.plurals.confirm_apkg_export_selected_cards, selectedCardIds.size, selectedCardIds.size),
Expand Down Expand Up @@ -1435,7 +1421,7 @@ open class CardBrowser :
}

private fun showOptionsDialog() {
val dialog = BrowserOptionsDialog(cardsOrNotes, viewModel.isTruncated)
val dialog = BrowserOptionsDialog(viewModel.cardsOrNotes, viewModel.isTruncated)
dialog.show(supportFragmentManager, "browserOptionsDialog")
}

Expand Down Expand Up @@ -1495,7 +1481,7 @@ open class CardBrowser :
val order = mOrder.toSortOrder()
launchCatchingTask {
Timber.d("performing search")
val cards = withProgress { searchForCards(query, order, cardsOrNotes) }
val cards = withProgress { searchForCards(query, order, viewModel.cardsOrNotes) }
Timber.d("Search returned %d cards", cards.size)
// Render the first few items
for (i in 0 until Math.min(numCardsToRender(), cards.size)) {
Expand Down Expand Up @@ -1574,7 +1560,7 @@ open class CardBrowser :
get() {
val count = cardCount

@androidx.annotation.StringRes val subtitleId = if (cardsOrNotes == CARDS) {
@androidx.annotation.StringRes val subtitleId = if (viewModel.cardsOrNotes == CARDS) {
R.plurals.card_browser_subtitle
} else {
R.plurals.card_browser_subtitle_notes_mode
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,16 @@ import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import com.ichi2.anki.AnkiDroidApp
import com.ichi2.anki.CardBrowser
import com.ichi2.anki.CollectionManager.withCol
import com.ichi2.anki.model.CardsOrNotes
import com.ichi2.anki.model.CardsOrNotes.*
import com.ichi2.anki.model.SortType
import com.ichi2.anki.pages.CardInfoDestination
import com.ichi2.anki.preferences.SharedPreferencesProvider
import com.ichi2.libanki.CardId
import com.ichi2.libanki.undoableOp
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import timber.log.Timber
import java.util.Collections
Expand All @@ -44,7 +47,12 @@ class CardBrowserViewModel(
var restrictOnDeck: String = ""
var currentFlag = 0

var cardsOrNotes = CardsOrNotes.CARDS
/**
* Whether the browser is working in Cards mode or Notes mode.
* default: [CARDS]
* */
val cardsOrNotesFlow = MutableStateFlow(CARDS)
val cardsOrNotes get() = cardsOrNotesFlow.value

// card that was clicked (not marked)
var currentCardId: CardId = 0
Expand Down Expand Up @@ -73,6 +81,13 @@ class CardBrowserViewModel(
return CardInfoDestination(firstSelectedCard)
}

init {
viewModelScope.launch {
val cardsOrNotes = withCol { CardsOrNotes.fromCollection(this) }
cardsOrNotesFlow.update { cardsOrNotes }
}
}

fun hasSelectedCards(): Boolean = checkedCards.isNotEmpty()

/**
Expand Down Expand Up @@ -107,6 +122,14 @@ class CardBrowserViewModel(
suspend fun deleteSelectedNotes(): Int =
undoableOp { removeNotes(cids = selectedCardIds) }.count

fun setCardsOrNotes(newValue: CardsOrNotes) = viewModelScope.launch {
withCol {
// Change this to only change the preference on a state change
newValue.saveToCollection(this)
}
cardsOrNotesFlow.update { newValue }
}

fun setTruncated(value: Boolean) {
viewModelScope.launch {
isTruncatedFlow.emit(value)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import androidx.annotation.IdRes
import androidx.appcompat.app.AppCompatDialogFragment
import androidx.fragment.app.activityViewModels
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.ichi2.anki.CardBrowser
import com.ichi2.anki.R
import com.ichi2.anki.browser.CardBrowserViewModel
import com.ichi2.anki.model.CardsOrNotes
Expand All @@ -41,7 +40,7 @@ class BrowserOptionsDialog(private val cardsOrNotes: CardsOrNotes, private val i
@IdRes val selectedButtonId = dialogView.findViewById<RadioGroup>(R.id.select_browser_mode).checkedRadioButtonId
val newCardsOrNotes = if (selectedButtonId == R.id.select_cards_mode) CardsOrNotes.CARDS else CardsOrNotes.NOTES
if (cardsOrNotes != newCardsOrNotes) {
(activity as CardBrowser).switchCardOrNote(newCardsOrNotes)
viewModel.setCardsOrNotes(newCardsOrNotes)
}
val newTruncate = dialogView.findViewById<CheckBox>(R.id.truncate_checkbox).isChecked

Expand Down
6 changes: 3 additions & 3 deletions AnkiDroid/src/test/java/com/ichi2/anki/CardBrowserTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -750,7 +750,7 @@ class CardBrowserTest : RobolectricTest() {
browserWithNoNewCards.apply {
searchAllDecks()
assertThat("Result should contain 4 cards", cardCount, equalTo(4))
switchCardOrNote(newCardsMode = NOTES)
viewModel.setCardsOrNotes(NOTES)
assertThat("Result should contain 2 cards (one per note)", cardCount, equalTo(2))
}
}
Expand Down Expand Up @@ -935,14 +935,14 @@ class CardBrowserTest : RobolectricTest() {
fun checkCardsNotesMode() = runTest {
val cardBrowser = getBrowserWithNotes(3, true)

cardBrowser.cardsOrNotes = CARDS
cardBrowser.viewModel.setCardsOrNotes(CARDS)
cardBrowser.searchCards()

advanceRobolectricUiLooper()
// check if we get both cards of each note
assertThat(cardBrowser.mCards.size(), equalTo(6))

cardBrowser.cardsOrNotes = NOTES
cardBrowser.viewModel.setCardsOrNotes(NOTES)
cardBrowser.searchCards()

// check if we get one card per note
Expand Down

0 comments on commit 17a6bd4

Please sign in to comment.