Rework the data update throughout the app.
This commit is contained in:
		
							parent
							
								
									cbe945d7f5
								
							
						
					
					
						commit
						b36cb9b601
					
				
					 19 changed files with 323 additions and 121 deletions
				
			
		| 
						 | 
				
			
			@ -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
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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)
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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())
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue