Standardization of error mamagement.
This commit is contained in:
parent
a97a1b1ce0
commit
e8504c3b52
9 changed files with 60 additions and 77 deletions
|
|
@ -1,56 +1,51 @@
|
|||
package com.pixelized.rplexicon.ui.composable.error
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import android.content.Context
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import com.pixelized.rplexicon.LocalSnack
|
||||
import com.pixelized.rplexicon.R
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Stable
|
||||
sealed class FetchErrorUio {
|
||||
@Stable
|
||||
data class Permission(val intent: Intent) : FetchErrorUio()
|
||||
data object Structure : FetchErrorUio()
|
||||
|
||||
@Stable
|
||||
object Structure : FetchErrorUio()
|
||||
|
||||
@Stable
|
||||
object Default : FetchErrorUio()
|
||||
data object Default : FetchErrorUio()
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun HandleFetchError(
|
||||
errors: SharedFlow<FetchErrorUio>,
|
||||
onPermissionGranted: suspend () -> Unit,
|
||||
onStructureError: suspend (context: Context, snack: SnackbarHostState, error: FetchErrorUio.Structure) -> Unit = { context, snack, _ ->
|
||||
snack.showSnackbar(message = context.getString(R.string.error_structure))
|
||||
},
|
||||
onDefaultError: suspend (context: Context, snack: SnackbarHostState, error: FetchErrorUio.Default) -> Unit = { context, snack, _ ->
|
||||
snack.showSnackbar(message = context.getString(R.string.error_generic))
|
||||
},
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val snack = LocalSnack.current
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
val launcher = rememberLauncherForActivityResult(
|
||||
contract = ActivityResultContracts.StartActivityForResult(),
|
||||
) { result ->
|
||||
if (result.resultCode == Activity.RESULT_OK) {
|
||||
scope.launch {
|
||||
onPermissionGranted()
|
||||
}
|
||||
}
|
||||
}
|
||||
val currentOnStructureError = rememberUpdatedState(newValue = onStructureError)
|
||||
val currentOnDefaultError = rememberUpdatedState(newValue = onDefaultError)
|
||||
|
||||
LaunchedEffect(key1 = "LexiconErrorManagement") {
|
||||
LaunchedEffect(key1 = "HandleFetchError") {
|
||||
errors.collect { error ->
|
||||
when (error) {
|
||||
is FetchErrorUio.Permission -> launcher.launch(error.intent)
|
||||
is FetchErrorUio.Structure -> snack.showSnackbar(message = context.getString(R.string.error_structure))
|
||||
is FetchErrorUio.Default -> snack.showSnackbar(message = context.getString(R.string.error_generic))
|
||||
is FetchErrorUio.Structure -> currentOnStructureError.value.invoke(
|
||||
context, snack, error,
|
||||
)
|
||||
|
||||
is FetchErrorUio.Default -> currentOnDefaultError.value.invoke(
|
||||
context, snack, error,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -90,9 +90,6 @@ fun LexiconScreen(
|
|||
|
||||
HandleFetchError(
|
||||
errors = viewModel.error,
|
||||
onPermissionGranted = {
|
||||
viewModel.updateLexicon(force = true)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,14 +5,10 @@ import androidx.compose.runtime.State
|
|||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.google.api.client.googleapis.extensions.android.gms.auth.UserRecoverableAuthIOException
|
||||
import com.pixelized.rplexicon.R
|
||||
import com.pixelized.rplexicon.model.Lexicon
|
||||
import com.pixelized.rplexicon.repository.data.ActionRepository
|
||||
import com.pixelized.rplexicon.repository.data.AlterationRepository
|
||||
import com.pixelized.rplexicon.repository.data.CharacterSheetRepository
|
||||
import com.pixelized.rplexicon.repository.data.LexiconRepository
|
||||
import com.pixelized.rplexicon.repository.data.SpellRepository
|
||||
import com.pixelized.rplexicon.ui.composable.error.FetchErrorUio
|
||||
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
|
|
@ -102,11 +98,6 @@ class LexiconViewModel @Inject constructor(
|
|||
lexiconRepository.fetchLexicon()
|
||||
}
|
||||
}
|
||||
// user need to accept OAuth2 permission.
|
||||
catch (exception: UserRecoverableAuthIOException) {
|
||||
Log.e(TAG, exception.message, exception)
|
||||
_error.emit(FetchErrorUio.Permission(intent = exception.intent))
|
||||
}
|
||||
// the data sheet structure is not as expected
|
||||
catch (exception: IncompatibleSheetStructure) {
|
||||
Log.e(TAG, exception.message, exception)
|
||||
|
|
@ -125,11 +116,6 @@ class LexiconViewModel @Inject constructor(
|
|||
characterSheetRepository.fetchCharacterSheet()
|
||||
}
|
||||
}
|
||||
// user need to accept OAuth2 permission.
|
||||
catch (exception: UserRecoverableAuthIOException) {
|
||||
Log.e(TAG, exception.message, exception)
|
||||
_error.emit(FetchErrorUio.Permission(intent = exception.intent))
|
||||
}
|
||||
// the data sheet structure is not as expected
|
||||
catch (exception: IncompatibleSheetStructure) {
|
||||
Log.e(TAG, exception.message, exception)
|
||||
|
|
|
|||
|
|
@ -65,9 +65,6 @@ fun LocationScreen(
|
|||
|
||||
HandleFetchError(
|
||||
errors = viewModel.error,
|
||||
onPermissionGranted = {
|
||||
viewModel.update(force = true)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,11 +54,8 @@ class LocationViewModel @Inject constructor(
|
|||
repository.fetchLocation()
|
||||
}
|
||||
}
|
||||
// user need to accept OAuth2 permission.
|
||||
catch (exception: UserRecoverableAuthIOException) {
|
||||
Log.e(TAG, exception.message, exception)
|
||||
_error.emit(FetchErrorUio.Permission(intent = exception.intent))
|
||||
} catch (exception: IncompatibleSheetStructure) {
|
||||
// the data sheet structure is not as expected
|
||||
catch (exception: IncompatibleSheetStructure) {
|
||||
Log.e(TAG, exception.message, exception)
|
||||
_error.emit(FetchErrorUio.Structure)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,9 +65,6 @@ fun QuestListScreen(
|
|||
|
||||
HandleFetchError(
|
||||
errors = viewModel.error,
|
||||
onPermissionGranted = {
|
||||
viewModel.update(force = true)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,11 +55,8 @@ class QuestListViewModel @Inject constructor(
|
|||
repository.fetchQuests()
|
||||
}
|
||||
}
|
||||
// user need to accept OAuth2 permission.
|
||||
catch (exception: UserRecoverableAuthIOException) {
|
||||
Log.e(TAG, exception.message, exception)
|
||||
_error.emit(FetchErrorUio.Permission(intent = exception.intent))
|
||||
} catch (exception: IncompatibleSheetStructure) {
|
||||
// the data sheet structure is not as expected
|
||||
catch (exception: IncompatibleSheetStructure) {
|
||||
Log.e(TAG, exception.message, exception)
|
||||
_error.emit(FetchErrorUio.Structure)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ import androidx.compose.ui.unit.dp
|
|||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import com.pixelized.rplexicon.R
|
||||
import com.pixelized.rplexicon.ui.composable.BackgroundImage
|
||||
import com.pixelized.rplexicon.ui.composable.error.HandleFetchError
|
||||
import com.pixelized.rplexicon.ui.navigation.LocalScreenNavHost
|
||||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
|
||||
import com.pixelized.rplexicon.utilitary.extentions.scrollOffset
|
||||
|
|
@ -72,6 +73,10 @@ fun SpellDetailScreen(
|
|||
spell = viewModel.spell,
|
||||
onBack = { screen.popBackStack() },
|
||||
)
|
||||
|
||||
HandleFetchError(
|
||||
errors = viewModel.error,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,11 +4,16 @@ import androidx.compose.runtime.State
|
|||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.pixelized.rplexicon.repository.data.SpellRepository
|
||||
import com.pixelized.rplexicon.ui.composable.error.FetchErrorUio
|
||||
import com.pixelized.rplexicon.ui.navigation.screens.spellDetailArgument
|
||||
import com.pixelized.rplexicon.utilitary.extentions.local.icon
|
||||
import com.pixelized.rplexicon.utilitary.extentions.local.label
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
|
|
@ -17,28 +22,35 @@ class SpellDetailViewModel @Inject constructor(
|
|||
repository: SpellRepository
|
||||
) : ViewModel() {
|
||||
val spell: State<SpellDetailUio?>
|
||||
val error: SharedFlow<FetchErrorUio>
|
||||
|
||||
init {
|
||||
val argument = savedStateHandle.spellDetailArgument
|
||||
val assignedSpell = repository.findAssignedSpell(
|
||||
character = argument.character,
|
||||
spell = argument.spell,
|
||||
)?.let {
|
||||
SpellDetailUio(
|
||||
icon = it.spell.school.icon,
|
||||
name = it.spell.name,
|
||||
translated = it.spell.originalName,
|
||||
level = "${it.spell.level}",
|
||||
school = it.spell.school.label,
|
||||
castingTime = it.spell.castingTime,
|
||||
range = it.spell.range,
|
||||
requirement = it.spell.requirement,
|
||||
duration = it.spell.duration,
|
||||
description = it.spell.description,
|
||||
)
|
||||
}
|
||||
|
||||
spell = mutableStateOf(
|
||||
repository.findAssignedSpell(
|
||||
character = argument.character,
|
||||
spell = argument.spell,
|
||||
)?.let {
|
||||
SpellDetailUio(
|
||||
icon = it.spell.school.icon,
|
||||
name = it.spell.name,
|
||||
translated = it.spell.originalName,
|
||||
level = "${it.spell.level}",
|
||||
school = it.spell.school.label,
|
||||
castingTime = it.spell.castingTime,
|
||||
range = it.spell.range,
|
||||
requirement = it.spell.requirement,
|
||||
duration = it.spell.duration,
|
||||
description = it.spell.description,
|
||||
)
|
||||
spell = mutableStateOf(assignedSpell)
|
||||
error = MutableSharedFlow<FetchErrorUio>().also { flow ->
|
||||
if (assignedSpell == null) {
|
||||
viewModelScope.launch {
|
||||
flow.tryEmit(FetchErrorUio.Default)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue