@@ -113,6 +113,7 @@ import com.ichi2.anki.deckpicker.DeckPickerViewModel.FlattenedDeckList
113113import com.ichi2.anki.deckpicker.DeckPickerViewModel.StartupResponse
114114import com.ichi2.anki.deckpicker.EmptyCardsResult
115115import com.ichi2.anki.deckpicker.OptionsMenuState
116+ import com.ichi2.anki.deckpicker.ShortcutData
116117import com.ichi2.anki.deckpicker.SyncIconState
117118import com.ichi2.anki.dialogs.AsyncDialogFragment
118119import com.ichi2.anki.dialogs.BackupPromptDialog
@@ -144,7 +145,6 @@ import com.ichi2.anki.export.ExportDialogFragment
144145import com.ichi2.anki.introduction.CollectionPermissionScreenLauncher
145146import com.ichi2.anki.introduction.hasCollectionStoragePermissions
146147import com.ichi2.anki.libanki.DeckId
147- import com.ichi2.anki.libanki.Decks
148148import com.ichi2.anki.libanki.exception.ConfirmModSchemaException
149149import com.ichi2.anki.libanki.sched.DeckNode
150150import com.ichi2.anki.libanki.undoAvailable
@@ -678,6 +678,10 @@ open class DeckPicker :
678678 startActivity(destination.toIntent(this ))
679679 }
680680
681+ fun onExportDeck (deckId : DeckId ) {
682+ ExportDialogFragment .newInstance(deckId).show(supportFragmentManager, " exportOptions" )
683+ }
684+
681685 fun onPromptUserToUpdateScheduler (op : Unit ) {
682686 SchedulerUpgradeDialog (
683687 activity = this ,
@@ -776,7 +780,7 @@ open class DeckPicker :
776780 }
777781
778782 fun onFocusedDeckChanged (deckId : DeckId ? ) {
779- val position = deckId?.let { findDeckPosition(it) } ? : 0
783+ val position = deckId?.let { viewModel. findDeckPosition(it) } ? : 0
780784 // HACK: a small delay is required before scrolling works
781785 deckPickerBinding.decks.postDelayed({
782786 decksLayoutManager.scrollToPositionWithOffset(position, deckPickerBinding.decks.height / 2 )
@@ -828,6 +832,9 @@ open class DeckPicker :
828832 viewModel.emptyCardsNotification.launchCollectionInLifecycleScope(::onCardsEmptied)
829833 viewModel.flowOfDeckCountsChanged.launchCollectionInLifecycleScope(::onDeckCountsChanged)
830834 viewModel.flowOfDestination.launchCollectionInLifecycleScope(::onDestinationChanged)
835+ viewModel.flowOfExportDeck.launchCollectionInLifecycleScope(::onExportDeck)
836+ viewModel.flowOfCreateShortcut.launchCollectionInLifecycleScope(::createIcon)
837+ viewModel.flowOfDisableShortcuts.launchCollectionInLifecycleScope(::disableDeckAndChildrenShortcuts)
831838 viewModel.onError.launchCollectionInLifecycleScope(::onError)
832839 viewModel.flowOfPromptUserToUpdateScheduler.launchCollectionInLifecycleScope(::onPromptUserToUpdateScheduler)
833840 viewModel.flowOfUndoUpdated.launchCollectionInLifecycleScope(::onUndoUpdated)
@@ -879,7 +886,7 @@ open class DeckPicker :
879886 * if fixed or given free hand to delete the shortcut with the help of API update this method and use the new one
880887 */
881888 // TODO: it feels buggy that this is not called on all deck deletion paths
882- disableDeckAndChildrenShortcuts(deckId)
889+ viewModel. disableDeckAndChildrenShortcuts(deckId)
883890 dismissAllDialogFragments()
884891 deleteDeck(deckId)
885892 }
@@ -894,7 +901,7 @@ open class DeckPicker :
894901 }
895902 DeckPickerContextMenuOption .CREATE_SHORTCUT -> {
896903 Timber .i(" ContextMenu: Create icon for a deck" )
897- createIcon(this , deckId)
904+ viewModel. createIcon(deckId)
898905 }
899906 DeckPickerContextMenuOption .RENAME_DECK -> {
900907 Timber .i(" ContextMenu: Rename deck selected" )
@@ -903,7 +910,7 @@ open class DeckPicker :
903910 }
904911 DeckPickerContextMenuOption .EXPORT_DECK -> {
905912 Timber .i(" ContextMenu: Export deck selected" )
906- exportDeck(deckId)
913+ viewModel. exportDeck(deckId)
907914 }
908915 DeckPickerContextMenuOption .UNBURY -> {
909916 Timber .i(" ContextMenu: Unbury deck selected" )
@@ -2155,40 +2162,23 @@ open class DeckPicker :
21552162 // Also forget the last deck used by the Browser
21562163 CardBrowser .clearLastDeckId()
21572164 viewModel.focusedDeck = did
2158- val deck = getNodeByDid(did)
2159- if (deck.hasCardsReadyToStudy()) {
2165+
2166+ // TODO: Reuse dueTree from ViewModel instead of recalculating for better performance.
2167+ val deck = withCol { sched.deckDueTree().find(did) }
2168+ if (deck?.hasCardsReadyToStudy() == true ) {
21602169 openReviewerOrStudyOptions(selectionType)
21612170 return
21622171 }
21632172
2164- if (! deck.filtered && isDeckAndSubdeckEmpty(did)) {
2173+ val isEmpty = withCol { decks.cardCount(did, includeSubdecks = true ) == 0 }
2174+ if (! deck?.filtered!! && isEmpty) {
21652175 showEmptyDeckSnackbar()
21662176 updateUi()
21672177 } else {
21682178 onDeckCompleted()
21692179 }
21702180 }
21712181
2172- /* *
2173- * Return the position of the deck in the deck list. If the deck is a child of a collapsed deck
2174- * (i.e., not visible in the deck list), then the position of the parent deck is returned instead.
2175- *
2176- * An invalid deck ID will return position 0.
2177- */
2178- private fun findDeckPosition (did : DeckId ): Int {
2179- deckListAdapter.currentList.forEachIndexed { index, treeNode ->
2180- if (treeNode.did == did) {
2181- return index
2182- }
2183- }
2184-
2185- // If the deck is not in our list, we search again using the immediate parent
2186- // If the deck is not found, return 0
2187- val collapsedDeck = dueTree?.find(did) ? : return 0
2188- val parent = collapsedDeck.parent?.get() ? : return 0
2189- return findDeckPosition(parent.did)
2190- }
2191-
21922182 /* *
21932183 * @see DeckPickerViewModel.updateDeckList
21942184 */
@@ -2199,28 +2189,16 @@ open class DeckPicker :
21992189 }
22002190 }
22012191
2202- /* *
2203- * Get the [DeckNode] identified by [did] from [DeckAdapter].
2204- */
2205- private fun DeckPicker.getNodeByDid (did : DeckId ): DeckNode = deckListAdapter.currentList[findDeckPosition(did)].deckNode
2206-
2207- fun exportDeck (did : DeckId ) {
2208- ExportDialogFragment .newInstance(did).show(supportFragmentManager, " exportOptions" )
2209- }
2210-
2211- private fun createIcon (
2212- context : Context ,
2213- did : DeckId ,
2214- ) {
2192+ private fun createIcon (shortcutData : ShortcutData ) {
22152193 // This code should not be reachable with lower versions
22162194 val shortcut =
22172195 ShortcutInfoCompat
2218- .Builder (this , did .toString())
2196+ .Builder (this , shortcutData.deckId .toString())
22192197 .setIntent(
2220- intentToReviewDeckFromShortcuts(context, did ),
2221- ).setIcon(IconCompat .createWithResource(context , R .mipmap.ic_launcher))
2222- .setShortLabel(Decks .basename(getColUnsafe.decks.name(did)) )
2223- .setLongLabel(getColUnsafe.decks.name(did) )
2198+ intentToReviewDeckFromShortcuts(this , shortcutData.deckId ),
2199+ ).setIcon(IconCompat .createWithResource(this , R .mipmap.ic_launcher))
2200+ .setShortLabel(shortcutData.shortLabel )
2201+ .setLongLabel(shortcutData.longLabel )
22242202 .build()
22252203 try {
22262204 val success = ShortcutManagerCompat .requestPinShortcut(this , shortcut, null )
@@ -2240,23 +2218,25 @@ open class DeckPicker :
22402218
22412219 /* * Disables the shortcut of the deck and the children belonging to it.*/
22422220 @NeedsTest(" ensure collapsed decks are also deleted" )
2243- private fun disableDeckAndChildrenShortcuts (did : DeckId ) {
2244- val deckTreeDids = dueTree?.find(did)?.map { it.did.toString() } ? : listOf ()
2221+ private fun disableDeckAndChildrenShortcuts (deckTreeDids : List <String >) {
22452222 val errorMessage: CharSequence = getString(R .string.deck_shortcut_doesnt_exist)
22462223 ShortcutUtils .disableShortcuts(this , deckTreeDids, errorMessage)
22472224 }
22482225
22492226 fun renameDeckDialog (did : DeckId ) {
2250- val currentName = getColUnsafe.decks.name(did)
2251- val createDeckDialog = CreateDeckDialog (this @DeckPicker, R .string.rename_deck, CreateDeckDialog .DeckDialogType .RENAME_DECK , null )
2252- createDeckDialog.deckName = currentName
2253- createDeckDialog.onNewDeckCreated = {
2254- dismissAllDialogFragments()
2255- deckListAdapter.notifyDataSetChanged()
2256- updateDeckList()
2257- tryShowStudyOptionsPanel()
2227+ launchCatchingTask {
2228+ val currentName = withCol { decks.name(did) }
2229+ val createDeckDialog =
2230+ CreateDeckDialog (this @DeckPicker, R .string.rename_deck, CreateDeckDialog .DeckDialogType .RENAME_DECK , null )
2231+ createDeckDialog.deckName = currentName
2232+ createDeckDialog.onNewDeckCreated = {
2233+ dismissAllDialogFragments()
2234+ deckListAdapter.notifyDataSetChanged()
2235+ updateDeckList()
2236+ tryShowStudyOptionsPanel()
2237+ }
2238+ createDeckDialog.showDialog()
22582239 }
2259- createDeckDialog.showDialog()
22602240 }
22612241
22622242 /* *
@@ -2288,11 +2268,7 @@ open class DeckPicker :
22882268 fun rebuildFiltered (did : DeckId ) {
22892269 launchCatchingTask {
22902270 withProgress(resources.getString(R .string.rebuild_filtered_deck)) {
2291- withCol {
2292- Timber .d(" rebuildFiltered: doInBackground - RebuildCram" )
2293- decks.select(did)
2294- sched.rebuildFilteredDeck(decks.selected())
2295- }
2271+ viewModel.rebuildFilteredDeck(did).join()
22962272 }
22972273 updateDeckList()
22982274 tryShowStudyOptionsPanel()
@@ -2461,18 +2437,6 @@ open class DeckPicker :
24612437 }
24622438 }
24632439
2464- /* *
2465- * Returns if the deck and its subdecks are all empty.
2466- *
2467- * @param did The id of a deck with no pending cards to review
2468- */
2469- private suspend fun isDeckAndSubdeckEmpty (did : DeckId ): Boolean {
2470- val node = getNodeByDid(did)
2471- return withCol {
2472- node.all { decks.isEmpty(it.did) }
2473- }
2474- }
2475-
24762440 override fun getApkgFileImportResultLauncher (): ActivityResultLauncher <Intent > = apkgFileImportResultLauncher
24772441
24782442 override fun getCsvFileImportResultLauncher (): ActivityResultLauncher <Intent > = csvImportResultLauncher
0 commit comments