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
|
package com.pixelized.rplexicon.ui.composable.error
|
||||||
|
|
||||||
import android.app.Activity
|
import android.content.Context
|
||||||
import android.content.Intent
|
import androidx.compose.material3.SnackbarHostState
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberUpdatedState
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import com.pixelized.rplexicon.LocalSnack
|
import com.pixelized.rplexicon.LocalSnack
|
||||||
import com.pixelized.rplexicon.R
|
import com.pixelized.rplexicon.R
|
||||||
import kotlinx.coroutines.flow.SharedFlow
|
import kotlinx.coroutines.flow.SharedFlow
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
sealed class FetchErrorUio {
|
sealed class FetchErrorUio {
|
||||||
@Stable
|
@Stable
|
||||||
data class Permission(val intent: Intent) : FetchErrorUio()
|
data object Structure : FetchErrorUio()
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
object Structure : FetchErrorUio()
|
data object Default : FetchErrorUio()
|
||||||
|
|
||||||
@Stable
|
|
||||||
object Default : FetchErrorUio()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun HandleFetchError(
|
fun HandleFetchError(
|
||||||
errors: SharedFlow<FetchErrorUio>,
|
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 context = LocalContext.current
|
||||||
val snack = LocalSnack.current
|
val snack = LocalSnack.current
|
||||||
val scope = rememberCoroutineScope()
|
|
||||||
|
|
||||||
val launcher = rememberLauncherForActivityResult(
|
val currentOnStructureError = rememberUpdatedState(newValue = onStructureError)
|
||||||
contract = ActivityResultContracts.StartActivityForResult(),
|
val currentOnDefaultError = rememberUpdatedState(newValue = onDefaultError)
|
||||||
) { result ->
|
|
||||||
if (result.resultCode == Activity.RESULT_OK) {
|
|
||||||
scope.launch {
|
|
||||||
onPermissionGranted()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LaunchedEffect(key1 = "LexiconErrorManagement") {
|
LaunchedEffect(key1 = "HandleFetchError") {
|
||||||
errors.collect { error ->
|
errors.collect { error ->
|
||||||
when (error) {
|
when (error) {
|
||||||
is FetchErrorUio.Permission -> launcher.launch(error.intent)
|
is FetchErrorUio.Structure -> currentOnStructureError.value.invoke(
|
||||||
is FetchErrorUio.Structure -> snack.showSnackbar(message = context.getString(R.string.error_structure))
|
context, snack, error,
|
||||||
is FetchErrorUio.Default -> snack.showSnackbar(message = context.getString(R.string.error_generic))
|
)
|
||||||
|
|
||||||
|
is FetchErrorUio.Default -> currentOnDefaultError.value.invoke(
|
||||||
|
context, snack, error,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -90,9 +90,6 @@ fun LexiconScreen(
|
||||||
|
|
||||||
HandleFetchError(
|
HandleFetchError(
|
||||||
errors = viewModel.error,
|
errors = viewModel.error,
|
||||||
onPermissionGranted = {
|
|
||||||
viewModel.updateLexicon(force = true)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,14 +5,10 @@ import androidx.compose.runtime.State
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.google.api.client.googleapis.extensions.android.gms.auth.UserRecoverableAuthIOException
|
|
||||||
import com.pixelized.rplexicon.R
|
import com.pixelized.rplexicon.R
|
||||||
import com.pixelized.rplexicon.model.Lexicon
|
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.CharacterSheetRepository
|
||||||
import com.pixelized.rplexicon.repository.data.LexiconRepository
|
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.ui.composable.error.FetchErrorUio
|
||||||
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
|
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
|
@ -102,11 +98,6 @@ class LexiconViewModel @Inject constructor(
|
||||||
lexiconRepository.fetchLexicon()
|
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
|
// the data sheet structure is not as expected
|
||||||
catch (exception: IncompatibleSheetStructure) {
|
catch (exception: IncompatibleSheetStructure) {
|
||||||
Log.e(TAG, exception.message, exception)
|
Log.e(TAG, exception.message, exception)
|
||||||
|
|
@ -125,11 +116,6 @@ class LexiconViewModel @Inject constructor(
|
||||||
characterSheetRepository.fetchCharacterSheet()
|
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
|
// the data sheet structure is not as expected
|
||||||
catch (exception: IncompatibleSheetStructure) {
|
catch (exception: IncompatibleSheetStructure) {
|
||||||
Log.e(TAG, exception.message, exception)
|
Log.e(TAG, exception.message, exception)
|
||||||
|
|
|
||||||
|
|
@ -65,9 +65,6 @@ fun LocationScreen(
|
||||||
|
|
||||||
HandleFetchError(
|
HandleFetchError(
|
||||||
errors = viewModel.error,
|
errors = viewModel.error,
|
||||||
onPermissionGranted = {
|
|
||||||
viewModel.update(force = true)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,11 +54,8 @@ class LocationViewModel @Inject constructor(
|
||||||
repository.fetchLocation()
|
repository.fetchLocation()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// user need to accept OAuth2 permission.
|
// the data sheet structure is not as expected
|
||||||
catch (exception: UserRecoverableAuthIOException) {
|
catch (exception: IncompatibleSheetStructure) {
|
||||||
Log.e(TAG, exception.message, exception)
|
|
||||||
_error.emit(FetchErrorUio.Permission(intent = exception.intent))
|
|
||||||
} catch (exception: IncompatibleSheetStructure) {
|
|
||||||
Log.e(TAG, exception.message, exception)
|
Log.e(TAG, exception.message, exception)
|
||||||
_error.emit(FetchErrorUio.Structure)
|
_error.emit(FetchErrorUio.Structure)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,9 +65,6 @@ fun QuestListScreen(
|
||||||
|
|
||||||
HandleFetchError(
|
HandleFetchError(
|
||||||
errors = viewModel.error,
|
errors = viewModel.error,
|
||||||
onPermissionGranted = {
|
|
||||||
viewModel.update(force = true)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,11 +55,8 @@ class QuestListViewModel @Inject constructor(
|
||||||
repository.fetchQuests()
|
repository.fetchQuests()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// user need to accept OAuth2 permission.
|
// the data sheet structure is not as expected
|
||||||
catch (exception: UserRecoverableAuthIOException) {
|
catch (exception: IncompatibleSheetStructure) {
|
||||||
Log.e(TAG, exception.message, exception)
|
|
||||||
_error.emit(FetchErrorUio.Permission(intent = exception.intent))
|
|
||||||
} catch (exception: IncompatibleSheetStructure) {
|
|
||||||
Log.e(TAG, exception.message, exception)
|
Log.e(TAG, exception.message, exception)
|
||||||
_error.emit(FetchErrorUio.Structure)
|
_error.emit(FetchErrorUio.Structure)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,6 +41,7 @@ import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import com.pixelized.rplexicon.R
|
import com.pixelized.rplexicon.R
|
||||||
import com.pixelized.rplexicon.ui.composable.BackgroundImage
|
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.navigation.LocalScreenNavHost
|
||||||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
|
import com.pixelized.rplexicon.ui.theme.LexiconTheme
|
||||||
import com.pixelized.rplexicon.utilitary.extentions.scrollOffset
|
import com.pixelized.rplexicon.utilitary.extentions.scrollOffset
|
||||||
|
|
@ -72,6 +73,10 @@ fun SpellDetailScreen(
|
||||||
spell = viewModel.spell,
|
spell = viewModel.spell,
|
||||||
onBack = { screen.popBackStack() },
|
onBack = { screen.popBackStack() },
|
||||||
)
|
)
|
||||||
|
|
||||||
|
HandleFetchError(
|
||||||
|
errors = viewModel.error,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,16 @@ import androidx.compose.runtime.State
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.lifecycle.SavedStateHandle
|
import androidx.lifecycle.SavedStateHandle
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.pixelized.rplexicon.repository.data.SpellRepository
|
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.ui.navigation.screens.spellDetailArgument
|
||||||
import com.pixelized.rplexicon.utilitary.extentions.local.icon
|
import com.pixelized.rplexicon.utilitary.extentions.local.icon
|
||||||
import com.pixelized.rplexicon.utilitary.extentions.local.label
|
import com.pixelized.rplexicon.utilitary.extentions.local.label
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.SharedFlow
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
|
|
@ -17,12 +22,11 @@ class SpellDetailViewModel @Inject constructor(
|
||||||
repository: SpellRepository
|
repository: SpellRepository
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
val spell: State<SpellDetailUio?>
|
val spell: State<SpellDetailUio?>
|
||||||
|
val error: SharedFlow<FetchErrorUio>
|
||||||
|
|
||||||
init {
|
init {
|
||||||
val argument = savedStateHandle.spellDetailArgument
|
val argument = savedStateHandle.spellDetailArgument
|
||||||
|
val assignedSpell = repository.findAssignedSpell(
|
||||||
spell = mutableStateOf(
|
|
||||||
repository.findAssignedSpell(
|
|
||||||
character = argument.character,
|
character = argument.character,
|
||||||
spell = argument.spell,
|
spell = argument.spell,
|
||||||
)?.let {
|
)?.let {
|
||||||
|
|
@ -39,6 +43,14 @@ class SpellDetailViewModel @Inject constructor(
|
||||||
description = it.spell.description,
|
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