Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improvement: introduce DeckPickerViewModel #17690

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

david-allison
Copy link
Member

Purpose / Description

We want more ViewModels in 2025. Let it start

Approach

  • Extract launchCollectionInLifecycleScope
  • Document remove
  • Create class
  • Bring method across

How Has This Been Tested?

Deleted a deck in the UI of an API 34 emulator via context menu

Checklist

  • You have a descriptive commit message with a short title (first line, max 50 chars).
  • You have commented your code, particularly in hard-to-understand areas
  • You have performed a self-review of your own code
  • UI changes: include screenshots of all affected screens (in particular showing any new or changed strings)
  • UI Changes: You have tested your change using the Google Accessibility Scanner

It will also be used in the Deck Picker
We want to use a ViewModel for DeckPicker
This is the start

* extract 'focusedDeck'
  * will be used in the next commit
* inline confirmDeckDeletion
  * removing unnecessary 'dismiss' calls
* extract `deleteDeck`
* introduce `DeckDeletionResult`
* @see deleteDeck
* @see DeckDeletionResult
*/
val deckDeletedNotification = MutableSharedFlow<DeckDeletionResult>()
Copy link
Member Author

Choose a reason for hiding this comment

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

This could be part of an 'undoableSnackbar' flow, didn't seem useful

Comment on lines +55 to +79
@CheckResult // This is a slow operation and should be inside `withProgress`
fun deleteDeck(did: DeckId) =
viewModelScope.launch {
val deckName = withCol { decks.get(did)!!.name }
val changes = undoableOp { decks.remove(listOf(did)) }
// After deletion: decks.current() reverts to Default, necessitating `focusedDeck`
// to match and avoid unnecessary scrolls in `renderPage()`.
focusedDeck = Consts.DEFAULT_DECK_ID

deckDeletedNotification.emit(
DeckDeletionResult(deckName = deckName, cardsDeleted = changes.count),
)
}

/**
* Deletes the currently selected deck
*
* This is a slow operation and should be inside `withProgress`
*/
@CheckResult
fun deleteSelectedDeck() =
viewModelScope.launch {
val targetDeckId = withCol { decks.selected() }
deleteDeck(targetDeckId).join()
}
Copy link
Member Author

Choose a reason for hiding this comment

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

We really have 3:

  • Delete [collection] Selected Deck
  • Delete Focused Deck
  • Delete DeckID

At 3, we can potentially parameterize the method: added complexity in the VM, for 4/5 less lines in the Activity

But it's probably best to stop using 'Delete [collection] Selected Deck' to avoid race conditions

decks.name(focusedDeck),
decks.cardCount(focusedDeck, includeSubdecks = true),
decks.isFiltered(focusedDeck),
decks.name(viewModel.focusedDeck),
Copy link
Member Author

@david-allison david-allison Dec 29, 2024

Choose a reason for hiding this comment

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

@SanjaySargam showDeleteDeckConfirmationDialog is only used when pressing 'DEL', in all other cases, we delete unconditionally and show an 'undo' snackbar

Was this intentional?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes it is intentional.

            KeyEvent.KEYCODE_DEL -> {
                // This action on a deck should only occur when the user see the deck name very clearly,
                // that is, when it appears in the trailing study option fragment
                if (fragmented) {
                    if (event.isShiftPressed) {
                        // Shortcut: Shift + DEL - Delete deck without confirmation dialog
                        Timber.i("Shift+DEL: Deck deck without confirmation")
                        deleteDeck(focusedDeck)
                    } else {
                        // Shortcut: DEL
                        Timber.i("Delete Deck from keypress")
                        showDeleteDeckConfirmationDialog()
                    }
                    return true
                }
            }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants