Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ import com.ichi2.anki.reviewreminders.ScheduleRemindersDestination
import com.ichi2.anki.settings.Prefs
import com.ichi2.anki.syncAuth
import com.ichi2.anki.utils.Destination
import com.ichi2.anki.utils.ext.allDecksCountsUncapped
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableSharedFlow
Expand Down Expand Up @@ -166,10 +167,14 @@ class DeckPickerViewModel :
tree.onlyHasDefaultDeck() && noCards
}.stateIn(viewModelScope, SharingStarted.Eagerly, initialValue = null)

// The deck-tree protobuf clamps each node's new/review/learn counts at 9999
// (backend behavior, shared with Anki Desktop), so summing the root's three
// fields underreports for large collections — see issue #17605. Route through
// `allDecksCountsUncapped` (uncapped, limit-aware via getQueuedCards) instead.
val flowOfCardsDue =
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please add a unit test

combine(flowOfDeckDueTree, flowOfDeckListInInitialState) { tree, inInitialState ->
if (tree == null || inInitialState != false) return@combine null
tree.newCount + tree.revCount + tree.lrnCount
withCol { sched.allDecksCountsUncapped().count() }
}

/** "Studied N cards in 0 seconds today */
Expand Down
27 changes: 27 additions & 0 deletions AnkiDroid/src/main/java/com/ichi2/anki/utils/ext/Scheduler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,30 @@ fun Scheduler.allDecksCounts(): Counts {
}
return total
}

/**
* Like [allDecksCounts] but uncapped: per-node counts on the deck-tree protobuf
* are clamped at 9999 by the backend, so summing them produces a wrong total
* for large collections (issue #17605). This iterates top-level decks and pulls
* each subtree's count from [Scheduler.counts] instead, which sources from
* `getQueuedCards` and is both limit-aware and unclamped — the same source
* `StudyOptionsFragment` uses to display per-deck counts.
*
* Side effect: temporarily mutates and restores `decks.selected()`.
*/
fun Scheduler.allDecksCountsUncapped(): Counts {
val total = Counts()
val previouslySelected = col.decks.selected()
try {
for (node in deckDueTree().children) {
col.decks.select(node.did)
val c = counts()
total.addNew(c.new)
total.addLrn(c.lrn)
total.addRev(c.rev)
}
} finally {
col.decks.select(previouslySelected)
}
return total
}
Loading