Rework the data update throughout the app.

This commit is contained in:
Thomas Andres Gomez 2023-09-26 11:50:44 +02:00
parent cbe945d7f5
commit b36cb9b601
19 changed files with 323 additions and 121 deletions

View file

@ -0,0 +1,99 @@
package com.pixelized.rplexicon
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
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.LocationRepository
import com.pixelized.rplexicon.repository.data.QuestRepository
import com.pixelized.rplexicon.repository.data.SpellRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class LauncherViewModel @Inject constructor(
lexiconRepository: LexiconRepository,
locationRepository: LocationRepository,
questRepository: QuestRepository,
alterationRepository: AlterationRepository,
characterSheetRepository: CharacterSheetRepository,
actionRepository: ActionRepository,
spellRepository: SpellRepository,
) : ViewModel() {
private val _error = MutableSharedFlow<String>()
val error: SharedFlow<String> get() = _error
var isLoading by mutableStateOf(true)
private set
init {
viewModelScope.launch {
val lexicon = async {
try {
lexiconRepository.fetchLexicon()
} catch (exception: Exception) {
_error.tryEmit("Lexicon fail to update")
}
}
val location = async {
try {
locationRepository.fetchLocation()
} catch (exception: Exception) {
_error.tryEmit("Location fail to update")
}
}
val quest = async {
try {
questRepository.fetchQuests()
} catch (exception: Exception) {
_error.tryEmit("Quest fail to update")
}
}
val alteration = async {
try {
alterationRepository.fetchAlterationSheet()
alterationRepository.fetchStatusSheet()
} catch (exception: Exception) {
_error.tryEmit("Alteration fail to update")
}
}
val characterSheet = async {
try {
characterSheetRepository.fetchCharacterSheet()
} catch (exception: Exception) {
_error.tryEmit("CharacterSheet fail to update")
}
}
awaitAll(lexicon, location, quest, alteration, characterSheet)
val action = async {
try {
actionRepository.fetchActions()
} catch (exception: Exception) {
_error.tryEmit("Action fail to update")
}
}
val spell = async {
try {
spellRepository.fetchSpells()
} catch (exception: Exception) {
_error.tryEmit("Spell fail to update")
}
}
awaitAll(action, spell)
isLoading = false
}
}
}

View file

@ -5,6 +5,7 @@ import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.BackHandler
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.navigationBarsPadding
@ -15,6 +16,7 @@ import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Surface
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.staticCompositionLocalOf
@ -22,6 +24,7 @@ import androidx.compose.ui.Modifier
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.WindowCompat
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.viewmodel.compose.viewModel
import com.pixelized.rplexicon.ui.composable.BlurredOverlayHost
import com.pixelized.rplexicon.ui.navigation.ScreenNavHost
import com.pixelized.rplexicon.ui.screens.rolls.BlurredRollOverlayHostState
@ -45,6 +48,9 @@ val NO_WINDOW_INSETS = WindowInsets(0, 0, 0, 0)
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
private val launcherViewModel: LauncherViewModel by viewModels()
private val rollViewModel: RollOverlayViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -53,19 +59,19 @@ class MainActivity : ComponentActivity() {
// splashscreen management
installSplashScreen().apply {
setKeepOnScreenCondition { false }
setKeepOnScreenCondition { launcherViewModel.isLoading }
}
setContent {
LexiconTheme {
val rollViewModel = hiltViewModel<RollOverlayViewModel>()
val snack = remember { SnackbarHostState() }
val overlay = rememberBlurredRollOverlayHostState(
viewModel = rollViewModel,
)
CompositionLocalProvider(
LocalActivity provides this,
LocalSnack provides remember { SnackbarHostState() },
LocalSnack provides snack,
LocalRollOverlay provides overlay,
) {
Scaffold(
@ -101,6 +107,12 @@ class MainActivity : ComponentActivity() {
overlay.hideOverlay()
}
}
LaunchedEffect(key1 = "LauncherError") {
launcherViewModel.error.collect {
snack.showSnackbar(message = it)
}
}
}
}
}

View file

@ -3,6 +3,7 @@ package com.pixelized.rplexicon.repository.data
import com.pixelized.rplexicon.repository.parser.AttackParser
import com.pixelized.rplexicon.model.Attack
import com.pixelized.rplexicon.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.utilitary.Update
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@ -18,6 +19,9 @@ class ActionRepository @Inject constructor(
private val _data = MutableStateFlow<Map<String, List<Attack>>>(emptyMap())
val data: StateFlow<Map<String, List<Attack>>> get() = _data
var lastSuccessFullUpdate: Update = Update.INITIAL
private set
fun find(name: String?): List<Attack>? {
return name?.let { _data.value[it] }
}
@ -31,6 +35,7 @@ class ActionRepository @Inject constructor(
charactersSheets = characterSheetRepository.data.value
)
_data.emit(data)
lastSuccessFullUpdate = Update.currentTime()
}
}
}

View file

@ -7,6 +7,7 @@ import com.pixelized.rplexicon.model.Alteration
import com.pixelized.rplexicon.model.Counter
import com.pixelized.rplexicon.model.Property
import com.pixelized.rplexicon.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.utilitary.Update
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@ -29,6 +30,12 @@ class AlterationRepository @Inject constructor(
private val _counter = MutableStateFlow<Map<String, List<Counter>>>(emptyMap())
val counter: StateFlow<Map<String, List<Counter>>> get() = _counter
var lastAlterationSuccessFullUpdate: Update = Update.INITIAL
private set
var lastStatusSuccessFullUpdate: Update = Update.INITIAL
private set
fun getAlterations(character: String): List<Alteration> {
return assignedAlterations.value[character]?.mapNotNull { alterationName ->
alterationsLexicon.value.firstOrNull { it.name == alterationName }
@ -49,6 +56,7 @@ class AlterationRepository @Inject constructor(
val request = sheet.get(Sheet.Character.ID, Sheet.Character.ALTERATION)
val data = alterationParser.parse(value = request.execute())
_alterationsLexicon.emit(data)
lastAlterationSuccessFullUpdate = Update.currentTime()
}
}
@ -60,6 +68,7 @@ class AlterationRepository @Inject constructor(
_assignedAlterations.emit(status)
val counter = counterParser.parse(values = request.execute())
_counter.emit(counter)
lastStatusSuccessFullUpdate = Update.currentTime()
}
}
}

View file

@ -3,6 +3,7 @@ package com.pixelized.rplexicon.repository.data
import com.pixelized.rplexicon.repository.parser.CharacterSheetParser
import com.pixelized.rplexicon.model.CharacterSheet
import com.pixelized.rplexicon.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.utilitary.Update
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@ -17,6 +18,13 @@ class CharacterSheetRepository @Inject constructor(
private val _data = MutableStateFlow<Map<String, CharacterSheet>>(emptyMap())
val data: StateFlow<Map<String, CharacterSheet>> get() = _data
var lastSuccessFullUpdate: Update = Update.INITIAL
private set
fun haveSheet(name: String?): Boolean {
return _data.value.containsKey(name)
}
fun find(name: String?): CharacterSheet? {
return name?.let { _data.value[name] }
}
@ -27,6 +35,7 @@ class CharacterSheetRepository @Inject constructor(
val request = sheet.get(Sheet.Character.ID, Sheet.Character.CHARACTER)
val data = characterSheetParser.parse(value = request.execute())
_data.emit(data)
lastSuccessFullUpdate = Update.currentTime()
}
}
}

View file

@ -3,6 +3,7 @@ package com.pixelized.rplexicon.repository.data
import com.pixelized.rplexicon.repository.parser.LexiconParser
import com.pixelized.rplexicon.model.Lexicon
import com.pixelized.rplexicon.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.utilitary.Update
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@ -17,6 +18,9 @@ class LexiconRepository @Inject constructor(
private val _data = MutableStateFlow<List<Lexicon>>(emptyList())
val data: StateFlow<List<Lexicon>> get() = _data
var lastSuccessFullUpdate: Update = Update.INITIAL
private set
fun findId(name: String?): Int? {
return name?.let { _data.value.firstOrNull { item -> item.name == it }?.id }
}
@ -27,6 +31,8 @@ class LexiconRepository @Inject constructor(
val request = sheet.get(Sheet.Lexicon.ID, Sheet.Lexicon.LEXICON)
val data = lexiconParser.parse(data = request.execute())
_data.tryEmit(data)
lastSuccessFullUpdate = Update.currentTime()
}
}
}

View file

@ -5,6 +5,7 @@ import com.pixelized.rplexicon.repository.parser.LocationParser
import com.pixelized.rplexicon.repository.parser.MarqueeParser
import com.pixelized.rplexicon.model.Location
import com.pixelized.rplexicon.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.utilitary.Update
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
@ -22,6 +23,9 @@ class LocationRepository @Inject constructor(
private val _data = MutableStateFlow<List<Location>>(emptyList())
val data: StateFlow<List<Location>> get() = _data
var lastSuccessFullUpdate: Update = Update.INITIAL
private set
fun findId(name: String?): Int? {
return name?.let { _data.value.firstOrNull { item -> item.name == it }?.id }
}
@ -34,6 +38,7 @@ class LocationRepository @Inject constructor(
async { sheet.get(Sheet.Lexicon.ID, Sheet.Lexicon.MARQUEE).execute() },
)
updateData(map = map, marquee = marquee)
lastSuccessFullUpdate = Update.currentTime()
}
}

View file

@ -4,6 +4,7 @@ import com.google.api.services.sheets.v4.model.ValueRange
import com.pixelized.rplexicon.repository.parser.QuestParser
import com.pixelized.rplexicon.model.Quest
import com.pixelized.rplexicon.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.utilitary.Update
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@ -18,12 +19,16 @@ class QuestRepository @Inject constructor(
private val _data = MutableStateFlow<List<Quest>>(emptyList())
val data: StateFlow<List<Quest>> get() = _data
var lastSuccessFullUpdate: Update = Update.INITIAL
private set
@Throws(IncompatibleSheetStructure::class, Exception::class)
suspend fun fetchQuests() {
googleRepository.fetch { sheet ->
val request = sheet.get(Sheet.Lexicon.ID, Sheet.Lexicon.QUEST_JOURNAL)
val data = request.execute()
updateData(data = data)
lastSuccessFullUpdate = Update.currentTime()
}
}

View file

@ -5,6 +5,7 @@ import com.pixelized.rplexicon.repository.parser.spell.SpellBookParser
import com.pixelized.rplexicon.model.AssignedSpell
import com.pixelized.rplexicon.model.Spell
import com.pixelized.rplexicon.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.utilitary.Update
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
@ -23,6 +24,9 @@ class SpellRepository @Inject constructor(
private val _spells = MutableStateFlow<Map<String, List<AssignedSpell>>>(emptyMap())
val spells: StateFlow<Map<String, List<AssignedSpell>>> get() = _spells
var lastSuccessFullUpdate: Update = Update.INITIAL
private set
fun findAssignedSpells(character: String?): List<AssignedSpell>? {
return character?.let { _spells.value[it] }
}
@ -42,6 +46,7 @@ class SpellRepository @Inject constructor(
val assignedSpells = assignedSpellParser.parse(data = magic, spells = spellsBook)
this@SpellRepository.spellsBook = spellsBook
_spells.emit(assignedSpells)
lastSuccessFullUpdate = Update.currentTime()
}
}
}

View file

@ -91,7 +91,7 @@ fun CharacterSheetScreen(
)
val refresh = rememberPullRefreshState(
refreshing = false,
onRefresh = { scope.launch { viewModel.update() } },
onRefresh = { scope.launch { viewModel.update(force = true) } },
)
Surface(
modifier = Modifier.fillMaxSize(),
@ -107,7 +107,7 @@ fun CharacterSheetScreen(
sheetState = sheetState,
refreshState = refresh,
onRefresh = {
scope.launch { viewModel.update() }
scope.launch { viewModel.update(force = true) }
},
onBack = {
screen.popBackStack()

View file

@ -41,53 +41,79 @@ class CharacterSheetViewModel @Inject constructor(
init {
viewModelScope.launch {
characterRepository.data.collect { sheets ->
_header.value = withContext(Dispatchers.Default) {
headerFactory.convert(model = sheets.getValue(character))
launch {
characterRepository.data.collect { sheets ->
_header.value = withContext(Dispatchers.Default) {
headerFactory.convert(model = sheets.getValue(character))
}
}
}
launch {
update(force = false)
}
}
}
suspend fun update() = coroutineScope {
suspend fun update(force: Boolean) = coroutineScope {
_isLoading.value = true
val characterRequest = async {
try {
characterRepository.fetchCharacterSheet()
actionRepository.fetchActions()
} catch (exception: Exception) {
Log.e(TAG, exception.message, exception)
}
}
val statusRequest = async {
try {
alterationRepository.fetchStatusSheet()
if (force || characterRepository.lastSuccessFullUpdate.shouldUpdate()) {
characterRepository.fetchCharacterSheet()
} else {
Unit
}
} catch (exception: Exception) {
Log.e(TAG, exception.message, exception)
}
}
val alterationRequest = async {
try {
alterationRepository.fetchAlterationSheet()
if (force || alterationRepository.lastAlterationSuccessFullUpdate.shouldUpdate()) {
alterationRepository.fetchAlterationSheet()
} else {
Unit
}
} catch (exception: Exception) {
Log.e(TAG, exception.message, exception)
}
}
val statusRequest = async {
try {
if (force || alterationRepository.lastStatusSuccessFullUpdate.shouldUpdate()) {
alterationRepository.fetchStatusSheet()
} else {
Unit
}
} catch (exception: Exception) {
Log.e(TAG, exception.message, exception)
}
}
awaitAll(characterRequest, statusRequest, alterationRequest)
val actionRequest = async {
try {
actionRepository.fetchActions()
if (force || actionRepository.lastSuccessFullUpdate.shouldUpdate()) {
actionRepository.fetchActions()
} else {
Unit
}
} catch (exception: Exception) {
Log.e(TAG, exception.message, exception)
}
}
val spellRequest = async {
try {
spellRepository.fetchSpells()
if (force || spellRepository.lastSuccessFullUpdate.shouldUpdate()) {
spellRepository.fetchSpells()
} else {
Unit
}
} catch (exception: Exception) {
Log.e(TAG, exception.message, exception)
}
}
awaitAll(characterRequest, statusRequest, alterationRequest, actionRequest, spellRequest)
awaitAll(actionRequest, spellRequest)
_isLoading.value = false
}

View file

@ -26,6 +26,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.theme.LexiconTheme
import com.pixelized.rplexicon.utilitary.LOS_FULL
import com.pixelized.rplexicon.utilitary.LOS_HOLLOW
import com.pixelized.rplexicon.utilitary.extentions.cell
import com.pixelized.rplexicon.utilitary.extentions.lexicon
@ -38,6 +39,7 @@ data class LexiconItemUio(
val diminutive: String?,
@StringRes val gender: Int,
@StringRes val race: Int,
val isPlayingCharacter: Boolean = false,
val placeholder: Boolean = false,
) {
companion object {
@ -48,6 +50,7 @@ data class LexiconItemUio(
diminutive: String? = null,
@StringRes gender: Int = R.string.gender_female_short,
@StringRes race: Int = R.string.race_half_orc,
isPlayingCharacter: Boolean = false,
placeholder: Boolean = false,
) = LexiconItemUio(
id = id,
@ -55,6 +58,7 @@ data class LexiconItemUio(
diminutive = diminutive,
gender = gender,
race = race,
isPlayingCharacter = isPlayingCharacter,
placeholder = placeholder,
)
}
@ -79,7 +83,7 @@ fun LexiconItem(
modifier = Modifier
.alignByBaseline()
.placeholder { item.placeholder },
text = LOS_HOLLOW,
text = if (item.isPlayingCharacter) LOS_FULL else LOS_HOLLOW,
)
FlowRow(
@ -172,6 +176,7 @@ private class LexiconItemPreviewProvider : PreviewParameterProvider<LexiconItemU
name = "Mundas-Naltum-Brulkhai-Arauishi",
diminutive = "Mun-Nalt-Bru-Arahi",
placeholder = false,
isPlayingCharacter = true,
),
LexiconItemUio.preview(
name = "Brulkhai",

View file

@ -62,7 +62,7 @@ fun LexiconScreen(
refreshing = false,
onRefresh = {
scope.launch {
viewModel.updateLexicon()
viewModel.updateLexicon(force = true)
}
},
)
@ -91,7 +91,7 @@ fun LexiconScreen(
HandleFetchError(
errors = viewModel.error,
onPermissionGranted = {
viewModel.updateLexicon()
viewModel.updateLexicon(force = true)
}
)
}

View file

@ -18,6 +18,7 @@ import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.launch
@ -27,9 +28,6 @@ import javax.inject.Inject
class LexiconViewModel @Inject constructor(
private val lexiconRepository: LexiconRepository,
private val characterSheetRepository: CharacterSheetRepository,
private val alterationRepository: AlterationRepository,
private val actionRepository: ActionRepository,
private val spellRepository: SpellRepository,
) : ViewModel() {
private val _isLoading = mutableStateOf(false)
@ -43,98 +41,97 @@ class LexiconViewModel @Inject constructor(
init {
viewModelScope.launch {
lexiconRepository.data.collect { items ->
_items.value = items.map { item ->
LexiconItemUio(
id = item.id,
name = item.name,
diminutive = item.diminutive?.takeIf { it.isNotBlank() }?.let { "./ $it" },
gender = when (item.gender) {
Lexicon.Gender.MALE -> R.string.gender_male_short
Lexicon.Gender.FEMALE -> R.string.gender_female_short
Lexicon.Gender.UNDETERMINED -> R.string.gender_undetermined_short
},
race = when (item.race) {
Lexicon.Race.ELF -> R.string.race_elf
Lexicon.Race.HALFLING -> R.string.race_halfling
Lexicon.Race.HUMAN -> R.string.race_human
Lexicon.Race.DWARF -> R.string.race_dwarf
Lexicon.Race.HALF_ELF -> R.string.race_half_elf
Lexicon.Race.HALF_ORC -> R.string.race_half_orc
Lexicon.Race.DRAGONBORN -> R.string.race_dragonborn
Lexicon.Race.GNOME -> R.string.race_gnome
Lexicon.Race.TIEFLING -> R.string.race_tiefling
Lexicon.Race.AARAKOCRA -> R.string.race_aarakocra
Lexicon.Race.GENASI -> R.string.race_genasi
Lexicon.Race.DEEP_GNOME -> R.string.race_deep_gnome
Lexicon.Race.GOLIATH -> R.string.race_goliath
Lexicon.Race.UNDETERMINED -> R.string.race_undetermined
},
)
}.sortedBy { it.name }
}
}
viewModelScope.launch {
_isLoading.value = true
val characterRequest = async {
try {
characterSheetRepository.fetchCharacterSheet()
actionRepository.fetchActions()
} catch (exception: Exception) {
Log.e(TAG, exception.message, exception)
launch {
lexiconRepository.data.collect { items ->
_items.value = items
.sortedBy { it.name }
.sortedBy { !characterSheetRepository.haveSheet(it.name) }
.map { item ->
LexiconItemUio(
id = item.id,
name = item.name,
diminutive = item.diminutive?.takeIf { it.isNotBlank() }
?.let { "./ $it" },
gender = when (item.gender) {
Lexicon.Gender.MALE -> R.string.gender_male_short
Lexicon.Gender.FEMALE -> R.string.gender_female_short
Lexicon.Gender.UNDETERMINED -> R.string.gender_undetermined_short
},
race = when (item.race) {
Lexicon.Race.ELF -> R.string.race_elf
Lexicon.Race.HALFLING -> R.string.race_halfling
Lexicon.Race.HUMAN -> R.string.race_human
Lexicon.Race.DWARF -> R.string.race_dwarf
Lexicon.Race.HALF_ELF -> R.string.race_half_elf
Lexicon.Race.HALF_ORC -> R.string.race_half_orc
Lexicon.Race.DRAGONBORN -> R.string.race_dragonborn
Lexicon.Race.GNOME -> R.string.race_gnome
Lexicon.Race.TIEFLING -> R.string.race_tiefling
Lexicon.Race.AARAKOCRA -> R.string.race_aarakocra
Lexicon.Race.GENASI -> R.string.race_genasi
Lexicon.Race.DEEP_GNOME -> R.string.race_deep_gnome
Lexicon.Race.GOLIATH -> R.string.race_goliath
Lexicon.Race.UNDETERMINED -> R.string.race_undetermined
},
isPlayingCharacter = characterSheetRepository.haveSheet(item.name)
)
}
}
}
val alterationRequest = async {
try {
alterationRepository.fetchAlterationSheet()
} catch (exception: Exception) {
Log.e(TAG, exception.message, exception)
}
launch {
updateLexicon(force = false)
}
val statusRequest = async {
try {
alterationRepository.fetchStatusSheet()
} catch (exception: Exception) {
Log.e(TAG, exception.message, exception)
}
}
val magicRequest = async {
try {
spellRepository.fetchSpells()
} catch (exception: Exception) {
Log.e(TAG, exception.message, exception)
}
}
val lexiconRequest = async {
fetchLexicon()
}
awaitAll(
characterRequest,
alterationRequest,
statusRequest,
lexiconRequest,
magicRequest,
)
_isLoading.value = false
}
}
suspend fun updateLexicon() {
suspend fun updateLexicon(force: Boolean) = coroutineScope {
_isLoading.value = true
fetchLexicon()
val lexicon = async {
fetchLexicon(force = force)
}
val sheets = async {
fetchCharacterSheet(force = force)
}
awaitAll(lexicon, sheets)
_isLoading.value = false
}
private suspend fun fetchLexicon() {
private suspend fun fetchLexicon(force: Boolean) {
try {
lexiconRepository.fetchLexicon()
if (force || lexiconRepository.lastSuccessFullUpdate.shouldUpdate()) {
lexiconRepository.fetchLexicon()
}
}
// 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)
}
// default exception
catch (exception: Exception) {
Log.e(TAG, exception.message, exception)
_error.emit(FetchErrorUio.Default)
}
}
private suspend fun fetchCharacterSheet(force: Boolean) {
try {
if (force || characterSheetRepository.lastSuccessFullUpdate.shouldUpdate()) {
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)
_error.emit(FetchErrorUio.Structure)
}

View file

@ -2,7 +2,6 @@ package com.pixelized.rplexicon.ui.screens.location.list
import android.content.res.Configuration
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
@ -48,7 +47,7 @@ fun LocationScreen(
refreshing = false,
onRefresh = {
scope.launch {
viewModel.fetchLocation()
viewModel.update(force = true)
}
},
)
@ -67,7 +66,7 @@ fun LocationScreen(
HandleFetchError(
errors = viewModel.error,
onPermissionGranted = {
viewModel.fetchLocation()
viewModel.update(force = true)
}
)
}

View file

@ -42,15 +42,17 @@ class LocationViewModel @Inject constructor(
}
}
launch {
fetchLocation()
update(force = false)
}
}
}
suspend fun fetchLocation() {
suspend fun update(force: Boolean) {
_isLoading.value = true
try {
_isLoading.value = true
repository.fetchLocation()
if (force || repository.lastSuccessFullUpdate.shouldUpdate()) {
repository.fetchLocation()
}
}
// user need to accept OAuth2 permission.
catch (exception: UserRecoverableAuthIOException) {

View file

@ -2,10 +2,6 @@ package com.pixelized.rplexicon.ui.screens.quest.list
import android.content.res.Configuration
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
@ -51,7 +47,7 @@ fun QuestListScreen(
refreshing = false,
onRefresh = {
scope.launch {
viewModel.fetchQuests()
viewModel.update(force = true)
}
},
)
@ -70,7 +66,7 @@ fun QuestListScreen(
HandleFetchError(
errors = viewModel.error,
onPermissionGranted = {
viewModel.fetchQuests()
viewModel.update(force = true)
}
)
}

View file

@ -43,15 +43,17 @@ class QuestListViewModel @Inject constructor(
}
}
launch {
fetchQuests()
update(force = false)
}
}
}
suspend fun fetchQuests() {
suspend fun update(force: Boolean) {
_isLoading.value = true
try {
_isLoading.value = true
repository.fetchQuests()
if (force || repository.lastSuccessFullUpdate.shouldUpdate()) {
repository.fetchQuests()
}
}
// user need to accept OAuth2 permission.
catch (exception: UserRecoverableAuthIOException) {

View file

@ -0,0 +1,20 @@
package com.pixelized.rplexicon.utilitary
@JvmInline
value class Update(private val value: Long) {
fun shouldUpdate(): Boolean {
return System.currentTimeMillis() - value > DELTA
}
companion object {
private const val DELTA = 5 * 60 * 1000
val INITIAL = Update(0)
fun currentTime(): Update {
return Update(System.currentTimeMillis())
}
}
}