Skip to content

Commit

Permalink
refactor: extract PreviewerViewModel.launchCatching
Browse files Browse the repository at this point in the history
  • Loading branch information
BrayanDSO committed Dec 16, 2023
1 parent ce1b10b commit 5d18119
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 19 deletions.
42 changes: 42 additions & 0 deletions AnkiDroid/src/main/java/com/ichi2/anki/CoroutineHelpers.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ import androidx.annotation.StringRes
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.ViewModel
import androidx.lifecycle.coroutineScope
import androidx.lifecycle.viewModelScope
import anki.collection.Progress
import com.ichi2.anki.CollectionManager.TR
import com.ichi2.anki.CollectionManager.withCol
Expand All @@ -41,6 +43,46 @@ import timber.log.Timber
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine

/**
* Runs a suspend function that catches any uncaught errors and reports them to the user.
* Errors from the backend contain localized text that is often suitable to show to the user as-is.
* Other errors should ideally be handled in the block.
*/
fun CoroutineScope.launchCatching(
block: suspend () -> Unit,
dispatcher: CoroutineDispatcher = Dispatchers.Default,
errorMessageHandler: suspend (String) -> Unit
): Job {
return launch(dispatcher) {
try {
block.invoke()
} catch (cancellationException: CancellationException) {
// CancellationException should be re-thrown to propagate it to the parent coroutine
throw cancellationException
} catch (backendException: BackendException) {
Timber.w(backendException)
val message = backendException.localizedMessage ?: backendException.toString()
errorMessageHandler.invoke(message)
} catch (exception: Exception) {
Timber.w(exception)
errorMessageHandler.invoke(exception.toString())
}
}
}

@Suppress("UNCHECKED_CAST")
fun <T> ViewModel.launchCatching(
block: suspend T.() -> Unit,
dispatcher: CoroutineDispatcher = Dispatchers.Default,
errorMessageHandler: suspend (String) -> Unit
): Job {
return viewModelScope.launchCatching(
{ block.invoke(this as T) },
dispatcher,
errorMessageHandler
)
}

/**
* Runs a suspend function that catches any uncaught errors and reports them to the user.
* Errors from the backend contain localized text that is often suitable to show to the user as-is.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,28 +18,26 @@ package com.ichi2.anki.previewer
import android.content.Context
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import com.google.android.material.color.MaterialColors.getColor
import com.ichi2.anki.AnkiDroidApp
import com.ichi2.anki.CollectionManager.withCol
import com.ichi2.anki.Flag
import com.ichi2.anki.LanguageUtils
import com.ichi2.anki.launchCatching
import com.ichi2.anki.servicelayer.MARKED_TAG
import com.ichi2.anki.servicelayer.NoteService
import com.ichi2.libanki.Card
import com.ichi2.libanki.addPlayButtons
import com.ichi2.themes.Themes
import com.ichi2.utils.toRGBHex
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import net.ankiweb.rsdroid.BackendException
import org.intellij.lang.annotations.Language
import timber.log.Timber

Expand Down Expand Up @@ -170,21 +168,9 @@ class PreviewerViewModel(mediaDir: String, private val selectedCardIds: LongArra
showAnswerOrDisplayCard(currentIndex.value + 1)
}

fun launchCatching(block: suspend PreviewerViewModel.() -> Unit) {
viewModelScope.launch(Dispatchers.IO) {
try {
block.invoke(this@PreviewerViewModel)
} catch (cancellationException: CancellationException) {
// CancellationException should be re-thrown to propagate it to the parent coroutine
throw cancellationException
} catch (backendException: BackendException) {
Timber.w(backendException)
val message = backendException.localizedMessage ?: backendException.toString()
onError.emit(message)
} catch (exception: Exception) {
Timber.w(exception)
onError.emit(exception.toString())
}
fun launchCatching(block: suspend PreviewerViewModel.() -> Unit): Job {
return launchCatching(block, Dispatchers.IO) { message ->
onError.emit(message)
}
}

Expand Down

0 comments on commit 5d18119

Please sign in to comment.