Skip to content

Commit

Permalink
feat: [Anki 25.01] handle new deckOptions backend methods
Browse files Browse the repository at this point in the history
Both are introduced by Anki 25.01

* **deckOptionsReady**
  * Called when deck options WebView is ready to receive requests.
  * This is piped through, but not implemented
* **deckOptionsRequireClose**
  * Force closing the deck options

Introduced in https://redirect.github.com/ankitects/anki/commit/d7fc98d4d87a0e07a228ab36c4ce2b1d7187690c
https://redirect.github.com/ankitects/anki/pull/3571

These methods are part of upstream moving away from
bridge commands, replacing them with HTTP calls
(which we handle with `PostRequestHandler`)

The workflow is now:

* anki.deckOptionsPendingChanges() is called on back press
  * If no changes, deckOptionsRequireClose() is called
  * Otherwise, confirm() is called
    * on OK, deckOptionsRequireClose() is called
    * no-op on cancel

Implementation:
* Handle methods in PostRequestHandler
  * via calling through to DeckOptions
  * document new workflow
  * remove unused methods
  * remove bridgeCommands

Fixes 14438 - 'back' button shows discard dialog
Issue 15788 - Uses a better 'Discard' message
  • Loading branch information
david-allison committed Jan 28, 2025
1 parent f05f025 commit 3b543a1
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 26 deletions.
60 changes: 34 additions & 26 deletions AnkiDroid/src/main/java/com/ichi2/anki/pages/DeckOptions.kt
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import com.ichi2.anki.CollectionManager.withCol
import com.ichi2.anki.CrashReportService
import com.ichi2.anki.ProgressContext
import com.ichi2.anki.R
import com.ichi2.anki.dialogs.DiscardChangesDialog
import com.ichi2.anki.SingleFragmentActivity
import com.ichi2.anki.launchCatchingTask
import com.ichi2.anki.utils.openUrl
import com.ichi2.anki.withProgress
Expand Down Expand Up @@ -67,28 +67,18 @@ class DeckOptions : PageFragment() {
override fun handleOnBackPressed() {
Timber.v("DeckOptions: requesting the webview to handle the user close request.")
webView.evaluateJavascript("anki.deckOptionsPendingChanges()") {
// No callback. Checking whether there are change is asynchronous. Javascript will use the BridgeCommand to request
// Kotlin to either close (if there is no change) or request the user to confirm they want to discard the changes.
// Callback is handled in the WebView:
// * A 'discard changes' dialog may be shown, using confirm()
// * if no changes, or changes discarded, `deckOptionsRequireClose` is called
// which PostRequestHandler handles and calls on this fragment
}
}
}

override val bridgeCommands =
mapOf<String, () -> Unit>(
"confirmDiscardChanges" to {
launchCatchingTask {
requestConfirmDiscard()
}
},
"_close" to {
actuallyClose()
},
)

/**
* Close the view, discarding change if needed.
*/
private fun actuallyClose() {
fun actuallyClose() {
onBackFromDeckOptions.isEnabled = false
Timber.v("webView: navigating back")
launchCatchingTask {
Expand All @@ -97,16 +87,6 @@ class DeckOptions : PageFragment() {
}
}

/**
* Request the user to confirm they want to close the options, discarding change. If they accept, do it.
*/
private fun requestConfirmDiscard() {
Timber.v("DeckOptions: showing 'discard changes'")
DiscardChangesDialog.showDialog(requireActivity()) {
actuallyClose()
}
}

/**
* Callback used when a modal is opened in the webview. It requests the webview to close it.
*/
Expand Down Expand Up @@ -223,6 +203,11 @@ class DeckOptions : PageFragment() {
webView.evaluateJavascript(closeJs, {})
}

fun onWebViewReady() {
Timber.d("WebView ready to receive input")
// TODO: handle this
}

companion object {
fun getIntent(
context: Context,
Expand Down Expand Up @@ -307,3 +292,26 @@ private fun ProgressContext.toUpdatingCardsString(): String? {
totalCardsCount = params.total,
)
}

private fun FragmentActivity.requireDeckOptionsFragment(): DeckOptions {
require(this is SingleFragmentActivity) { "activity must be SingleFragmentActivity" }
return requireNotNull(this.fragment as? DeckOptions?) { "fragment must be DeckOptions" }
}

/**
* Called when Deck Options WebView is ready to receive requests.
*/
fun FragmentActivity.deckOptionsReady(input: ByteArray): ByteArray {
requireDeckOptionsFragment().onWebViewReady()
return input
}

/**
* Force closing the deck options
*
* This is called after a 'discard changes?' dialog is accepted
*/
fun FragmentActivity.deckOptionsRequireClose(input: ByteArray): ByteArray {
requireDeckOptionsFragment().actuallyClose()
return input
}
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ val uiMethods =
}
},
"computeFsrsParams" to { bytes -> lifecycleScope.async { computeFsrsParams(bytes) } },
"deckOptionsReady" to { bytes -> lifecycleScope.async { deckOptionsReady(bytes) } },
"deckOptionsRequireClose" to { bytes -> lifecycleScope.async { deckOptionsRequireClose(bytes) } },
)

suspend fun FragmentActivity?.handleUiPostRequest(
Expand Down

0 comments on commit 3b543a1

Please sign in to comment.