Add RemoteConfig feature and like it to the LandingPage.

This commit is contained in:
Andres Gomez, Thomas (ITDV RL) 2024-06-21 15:41:18 +02:00
parent e62b558c21
commit 1040f54d23
23 changed files with 485 additions and 298 deletions

View file

@ -148,6 +148,7 @@ dependencies {
implementation("com.google.firebase:firebase-analytics-ktx")
implementation("com.google.firebase:firebase-auth-ktx")
implementation("com.google.firebase:firebase-database-ktx")
implementation("com.google.firebase:firebase-config-ktx")
// Hilt: Dependency injection
implementation("androidx.hilt:hilt-navigation-compose:1.2.0")

View file

@ -6,7 +6,6 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.pixelized.rplexicon.data.repository.adventure.AdventureRepository
import com.pixelized.rplexicon.data.repository.character.ActionRepository
import com.pixelized.rplexicon.data.repository.character.AlterationRepository
import com.pixelized.rplexicon.data.repository.character.CharacterSheetRepository
@ -16,6 +15,7 @@ import com.pixelized.rplexicon.data.repository.character.InventoryRepository
import com.pixelized.rplexicon.data.repository.character.ObjectActionRepository
import com.pixelized.rplexicon.data.repository.character.SkillRepository
import com.pixelized.rplexicon.data.repository.character.SpellRepository
import com.pixelized.rplexicon.data.repository.firebase.RemoteConfigRepository
import com.pixelized.rplexicon.data.repository.lexicon.CategoryOrderRepository
import com.pixelized.rplexicon.data.repository.lexicon.LexiconRepository
import com.pixelized.rplexicon.data.repository.lexicon.LocationRepository
@ -47,7 +47,7 @@ class LauncherViewModel @Inject constructor(
descriptionRepository: DescriptionRepository,
inventoryRepository: InventoryRepository,
equipmentRepository: EquipmentRepository,
adventureRepository: AdventureRepository,
removeConRepository: RemoteConfigRepository // Unused but injected to initialize it.
) : ViewModel() {
private val _error = MutableStateFlow<FetchErrorUio?>(null)

View file

@ -0,0 +1,10 @@
package com.pixelized.rplexicon.data.model
data class LexiconConfig(
val featureAdventure: Boolean,
val featureLexicon: Boolean,
val featureMaps: Boolean,
val featureQuests: Boolean,
val featureSummary: Boolean,
val featureSearch: Boolean,
)

View file

@ -5,7 +5,7 @@ import com.pixelized.rplexicon.data.model.CharacterSheet
import com.pixelized.rplexicon.data.parser.AttackParser
import com.pixelized.rplexicon.data.repository.CharacterBinder
import com.pixelized.rplexicon.data.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.data.repository.authentication.FirebaseRepository
import com.pixelized.rplexicon.data.repository.firebase.RealtimeDatabaseRepository
import com.pixelized.rplexicon.script.ScriptExecutor
import com.pixelized.rplexicon.utilitary.Update
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
@ -24,7 +24,7 @@ import javax.inject.Singleton
class ActionRepository @Inject constructor(
private val googleRepository: GoogleSheetServiceRepository,
private val actionParser: AttackParser,
firebaseRepository: FirebaseRepository,
firebaseRepository: RealtimeDatabaseRepository,
) {
private val _actions = MutableStateFlow<Map<String, List<Attack>>>(emptyMap())
val actions: StateFlow<Map<String, List<Attack>>> =

View file

@ -2,7 +2,7 @@ package com.pixelized.rplexicon.data.repository.character
import com.pixelized.rplexicon.data.model.alteration.Alteration
import com.pixelized.rplexicon.data.model.alteration.AlterationStatus
import com.pixelized.rplexicon.data.repository.authentication.FirebaseRepository
import com.pixelized.rplexicon.data.repository.firebase.RealtimeDatabaseRepository
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
@ -18,7 +18,7 @@ import javax.inject.Singleton
@Singleton
class ActiveAlterationRepository @Inject constructor(
alterationRepository: AlterationRepository,
firebaseRepository: FirebaseRepository,
firebaseRepository: RealtimeDatabaseRepository,
) {
private val activeAlterations: StateFlow<Map<String, List<Alteration>>> =
alterationRepository.getAlterations()

View file

@ -6,7 +6,7 @@ import com.pixelized.rplexicon.data.model.alteration.Alteration
import com.pixelized.rplexicon.data.parser.alteration.AlterationParser
import com.pixelized.rplexicon.data.repository.CharacterBinder
import com.pixelized.rplexicon.data.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.data.repository.authentication.FirebaseRepository
import com.pixelized.rplexicon.data.repository.firebase.RealtimeDatabaseRepository
import com.pixelized.rplexicon.script.ScriptExecutor
import com.pixelized.rplexicon.utilitary.Update
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
@ -27,7 +27,7 @@ import javax.inject.Singleton
class AlterationRepository @Inject constructor(
private val googleRepository: GoogleSheetServiceRepository,
private val alterationParser: AlterationParser,
firebaseRepository: FirebaseRepository,
firebaseRepository: RealtimeDatabaseRepository,
) {
private val alterations = MutableStateFlow<Map<String, List<Alteration>>>(emptyMap())
private val assignedAlterations: StateFlow<Map<String, List<Alteration>>> =

View file

@ -5,7 +5,7 @@ import com.pixelized.rplexicon.data.model.Skill
import com.pixelized.rplexicon.data.parser.SkillParser
import com.pixelized.rplexicon.data.repository.CharacterBinder
import com.pixelized.rplexicon.data.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.data.repository.authentication.FirebaseRepository
import com.pixelized.rplexicon.data.repository.firebase.RealtimeDatabaseRepository
import com.pixelized.rplexicon.script.ScriptExecutor
import com.pixelized.rplexicon.utilitary.Update
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
@ -24,7 +24,7 @@ import javax.inject.Singleton
class SkillRepository @Inject constructor(
private val googleRepository: GoogleSheetServiceRepository,
private val skillParser: SkillParser,
firebaseRepository: FirebaseRepository,
firebaseRepository: RealtimeDatabaseRepository,
) {
private val _skills = MutableStateFlow<Map<String, List<Skill>>>(emptyMap())
val skills: StateFlow<Map<String, List<Skill>>> =

View file

@ -6,7 +6,7 @@ import com.pixelized.rplexicon.data.parser.spell.AssignedSpellParser
import com.pixelized.rplexicon.data.parser.spell.SpellBookParser
import com.pixelized.rplexicon.data.repository.CharacterBinder
import com.pixelized.rplexicon.data.repository.GoogleSheetServiceRepository
import com.pixelized.rplexicon.data.repository.authentication.FirebaseRepository
import com.pixelized.rplexicon.data.repository.firebase.RealtimeDatabaseRepository
import com.pixelized.rplexicon.script.ScriptExecutor
import com.pixelized.rplexicon.utilitary.Update
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
@ -28,7 +28,7 @@ class SpellRepository @Inject constructor(
private val googleRepository: GoogleSheetServiceRepository,
private val spellBookParser: SpellBookParser,
private val assignedSpellParser: AssignedSpellParser,
firebaseRepository: FirebaseRepository,
firebaseRepository: RealtimeDatabaseRepository,
) {
private var _spellsBook = MutableStateFlow<List<Spell>>(emptyList())
val spellsBook: StateFlow<List<Spell>> get() = _spellsBook

View file

@ -1,4 +1,4 @@
package com.pixelized.rplexicon.data.repository.authentication
package com.pixelized.rplexicon.data.repository.firebase
import android.app.Application
import android.util.Log
@ -32,7 +32,7 @@ import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class FirebaseRepository @Inject constructor(
class RealtimeDatabaseRepository @Inject constructor(
application: Application,
) {
private val database = Firebase.database(

View file

@ -0,0 +1,83 @@
package com.pixelized.rplexicon.data.repository.firebase
import com.google.firebase.ktx.Firebase
import com.google.firebase.remoteconfig.FirebaseRemoteConfig
import com.google.firebase.remoteconfig.FirebaseRemoteConfigSettings
import com.google.firebase.remoteconfig.ktx.remoteConfig
import com.google.firebase.remoteconfig.ktx.remoteConfigSettings
import com.pixelized.rplexicon.BuildConfig
import com.pixelized.rplexicon.data.model.LexiconConfig
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class RemoteConfigRepository @Inject constructor() {
private val firebase: FirebaseRemoteConfig = Firebase.remoteConfig
private val _config = MutableStateFlow(
LexiconConfig(
featureAdventure = DEFAULT[FEATURE_ADVENTURE] as Boolean,
featureLexicon = DEFAULT[FEATURE_LEXICON] as Boolean,
featureMaps = DEFAULT[FEATURE_MAPS] as Boolean,
featureQuests = DEFAULT[FEATURE_QUESTS] as Boolean,
featureSummary = DEFAULT[FEATURE_SUMMARY] as Boolean,
featureSearch = DEFAULT[FEATURE_SEARCH] as Boolean,
)
)
val config: StateFlow<LexiconConfig> get() = _config
init {
val configSettings = when {
BuildConfig.DEBUG -> remoteConfigSettings {
minimumFetchIntervalInSeconds = MINIMUM_FETCH_INTERVAL_IN_SECONDS
}
else -> {
FirebaseRemoteConfigSettings.Builder().build()
}
}
firebase.apply {
setConfigSettingsAsync(configSettings)
setDefaultsAsync(DEFAULT)
refreshConfig()
}
}
private fun refreshConfig() {
firebase.fetchAndActivate().addOnCompleteListener { remoteConfig ->
if (remoteConfig.isSuccessful) {
val config = LexiconConfig(
featureAdventure = firebase.getBoolean(FEATURE_ADVENTURE),
featureLexicon = firebase.getBoolean(FEATURE_LEXICON),
featureMaps = firebase.getBoolean(FEATURE_MAPS),
featureQuests = firebase.getBoolean(FEATURE_QUESTS),
featureSummary = firebase.getBoolean(FEATURE_SUMMARY),
featureSearch = firebase.getBoolean(FEATURE_SEARCH),
)
_config.value = config
}
}
}
companion object {
private const val MINIMUM_FETCH_INTERVAL_IN_SECONDS: Long = 0
// The feature key
private const val FEATURE_ADVENTURE = "feature_adventure"
private const val FEATURE_LEXICON = "feature_lexicon"
private const val FEATURE_MAPS = "feature_maps"
private const val FEATURE_QUESTS = "feature_quests"
private const val FEATURE_SUMMARY = "feature_summary"
private const val FEATURE_SEARCH = "feature_search"
private val DEFAULT: HashMap<String, Any?> = hashMapOf(
FEATURE_ADVENTURE to false,
FEATURE_LEXICON to false,
FEATURE_MAPS to false,
FEATURE_QUESTS to false,
FEATURE_SUMMARY to false,
FEATURE_SEARCH to false,
)
}
}

View file

@ -12,7 +12,7 @@ import com.google.firebase.auth.GoogleAuthProvider
import com.google.firebase.auth.ktx.auth
import com.google.firebase.ktx.Firebase
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.data.repository.authentication.FirebaseRepository
import com.pixelized.rplexicon.data.repository.firebase.RealtimeDatabaseRepository
import com.pixelized.rplexicon.data.repository.character.CharacterSheetRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
@ -28,7 +28,7 @@ import javax.inject.Inject
*/
@HiltViewModel
class AuthenticationViewModel @Inject constructor(
private val firebaseRepository: FirebaseRepository,
private val firebaseRepository: RealtimeDatabaseRepository,
private val sheetRepository: CharacterSheetRepository,
) : ViewModel() {

View file

@ -6,7 +6,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.pixelized.rplexicon.data.repository.authentication.FirebaseRepository
import com.pixelized.rplexicon.data.repository.firebase.RealtimeDatabaseRepository
import com.pixelized.rplexicon.data.repository.character.ActionRepository
import com.pixelized.rplexicon.data.repository.character.AlterationRepository
import com.pixelized.rplexicon.data.repository.character.CharacterSheetRepository
@ -39,7 +39,7 @@ class CharacterSheetViewModel @Inject constructor(
private val objectRepository: ObjectActionRepository,
private val spellRepository: SpellRepository,
private val skillRepository: SkillRepository,
private val firebaseRepository: FirebaseRepository,
private val firebaseRepository: RealtimeDatabaseRepository,
savedStateHandle: SavedStateHandle,
) : ViewModel() {
private val _isLoading = mutableStateOf(false)

View file

@ -14,7 +14,7 @@ import com.pixelized.rplexicon.data.model.Property
import com.pixelized.rplexicon.data.model.alteration.Alteration
import com.pixelized.rplexicon.data.model.alteration.AlterationStatus
import com.pixelized.rplexicon.data.network.CharacterSheetFire
import com.pixelized.rplexicon.data.repository.authentication.FirebaseRepository
import com.pixelized.rplexicon.data.repository.firebase.RealtimeDatabaseRepository
import com.pixelized.rplexicon.data.repository.character.ActiveAlterationRepository
import com.pixelized.rplexicon.data.repository.character.CharacterSheetRepository
import com.pixelized.rplexicon.ui.composable.edit.HpPointDialogUio
@ -37,7 +37,7 @@ import javax.inject.Inject
@HiltViewModel
class HeaderViewModel @Inject constructor(
private val headerFactory: CharacterSheetHeaderUioFactory,
private val firebaseRepository: FirebaseRepository,
private val firebaseRepository: RealtimeDatabaseRepository,
private val activeAlterationRepository: ActiveAlterationRepository,
characterRepository: CharacterSheetRepository,
savedStateHandle: SavedStateHandle,

View file

@ -10,7 +10,7 @@ import com.pixelized.rplexicon.data.model.CharacterSheet
import com.pixelized.rplexicon.data.model.DiceThrow
import com.pixelized.rplexicon.data.model.Skill
import com.pixelized.rplexicon.data.network.CharacterSheetFire
import com.pixelized.rplexicon.data.repository.authentication.FirebaseRepository
import com.pixelized.rplexicon.data.repository.firebase.RealtimeDatabaseRepository
import com.pixelized.rplexicon.data.repository.character.CharacterSheetRepository
import com.pixelized.rplexicon.data.repository.character.DescriptionRepository
import com.pixelized.rplexicon.data.repository.character.SkillRepository
@ -19,7 +19,6 @@ import com.pixelized.rplexicon.ui.navigation.screens.characterSheetArgument
import com.pixelized.rplexicon.ui.screens.character.composable.actions.SkillItemUio
import com.pixelized.rplexicon.ui.screens.character.factory.SkillFactoryUioFactory
import com.pixelized.rplexicon.ui.screens.character.composable.dialogs.SkillDialogDetailUio
import com.pixelized.rplexicon.utilitary.extentions.string.skillIcon
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collectLatest
@ -31,7 +30,7 @@ import javax.inject.Inject
@HiltViewModel
class SkillsViewModel @Inject constructor(
private val sheetRepository: CharacterSheetRepository,
private val firebaseRepository: FirebaseRepository,
private val firebaseRepository: RealtimeDatabaseRepository,
private val skillRepository: SkillRepository,
private val descriptionRepository: DescriptionRepository,
private val skillFactory: SkillFactoryUioFactory,

View file

@ -14,7 +14,7 @@ import com.pixelized.rplexicon.data.model.DiceThrow
import com.pixelized.rplexicon.data.model.Property
import com.pixelized.rplexicon.data.model.roll.Throw
import com.pixelized.rplexicon.data.network.CharacterSheetFire
import com.pixelized.rplexicon.data.repository.authentication.FirebaseRepository
import com.pixelized.rplexicon.data.repository.firebase.RealtimeDatabaseRepository
import com.pixelized.rplexicon.data.repository.character.CharacterSheetRepository
import com.pixelized.rplexicon.data.repository.character.DescriptionRepository
import com.pixelized.rplexicon.data.repository.character.SpellRepository
@ -32,7 +32,6 @@ import com.pixelized.rplexicon.utilitary.extentions.local.label
import com.pixelized.rplexicon.utilitary.extentions.local.spell
import com.pixelized.rplexicon.utilitary.extentions.modifier
import com.pixelized.rplexicon.utilitary.extentions.signLabel
import com.pixelized.rplexicon.utilitary.extentions.string.spellIcon
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collectLatest
@ -46,7 +45,7 @@ import kotlin.math.max
@HiltViewModel
class SpellsViewModel @Inject constructor(
private val characterRepository: CharacterSheetRepository,
private val firebaseRepository: FirebaseRepository,
private val firebaseRepository: RealtimeDatabaseRepository,
private val spellRepository: SpellRepository,
private val descriptionRepository: DescriptionRepository,
application: Application,

View file

@ -13,7 +13,7 @@ import com.pixelized.rplexicon.data.model.CharacterSheet
import com.pixelized.rplexicon.data.model.Description
import com.pixelized.rplexicon.data.model.alteration.Alteration
import com.pixelized.rplexicon.data.model.alteration.AlterationStatus
import com.pixelized.rplexicon.data.repository.authentication.FirebaseRepository
import com.pixelized.rplexicon.data.repository.firebase.RealtimeDatabaseRepository
import com.pixelized.rplexicon.data.repository.character.ActiveAlterationRepository
import com.pixelized.rplexicon.data.repository.character.AlterationRepository
import com.pixelized.rplexicon.data.repository.character.CharacterSheetRepository
@ -22,7 +22,6 @@ import com.pixelized.rplexicon.ui.navigation.screens.characterSheetArgument
import com.pixelized.rplexicon.ui.screens.character.composable.dialogs.AlterationDialogDetailUio
import com.pixelized.rplexicon.ui.screens.rolls.factory.AlterationFactory
import com.pixelized.rplexicon.utilitary.extentions.context
import com.pixelized.rplexicon.utilitary.extentions.string.alterationIcon
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collectLatest
@ -36,7 +35,7 @@ class AlterationViewModel @Inject constructor(
private val characterSheetRepository: CharacterSheetRepository,
private val alterationRepository: AlterationRepository,
private val activeAlterationRepository: ActiveAlterationRepository,
private val firebaseRepository: FirebaseRepository,
private val firebaseRepository: RealtimeDatabaseRepository,
private val descriptionRepository: DescriptionRepository,
private val factory: AlterationFactory,
savedStateHandle: SavedStateHandle,

View file

@ -12,7 +12,7 @@ import com.pixelized.rplexicon.data.model.Property
import com.pixelized.rplexicon.data.model.Skill
import com.pixelized.rplexicon.data.model.alteration.Alteration
import com.pixelized.rplexicon.data.network.CharacterSheetFire
import com.pixelized.rplexicon.data.repository.authentication.FirebaseRepository
import com.pixelized.rplexicon.data.repository.firebase.RealtimeDatabaseRepository
import com.pixelized.rplexicon.data.repository.character.ActiveAlterationRepository
import com.pixelized.rplexicon.data.repository.character.CharacterSheetRepository
import com.pixelized.rplexicon.data.repository.character.DescriptionRepository
@ -26,7 +26,6 @@ import com.pixelized.rplexicon.ui.screens.character.factory.CharacterSheetUioFac
import com.pixelized.rplexicon.ui.screens.character.factory.SkillFactoryUioFactory
import com.pixelized.rplexicon.ui.screens.character.composable.dialogs.SkillDialogDetailUio
import com.pixelized.rplexicon.utilitary.extentions.local.toStatus
import com.pixelized.rplexicon.utilitary.extentions.string.skillIcon
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.combine
@ -37,7 +36,7 @@ import javax.inject.Inject
@HiltViewModel
class ProficiencyViewModel @Inject constructor(
private val characterRepository: CharacterSheetRepository,
private val firebaseRepository: FirebaseRepository,
private val firebaseRepository: RealtimeDatabaseRepository,
private val activeAlterationRepository: ActiveAlterationRepository,
private val characterSheetFactory: CharacterSheetUioFactory,
private val skillRepository: SkillRepository,

View file

@ -45,10 +45,21 @@ import com.pixelized.rplexicon.utilitary.extentions.lexicon
@Stable
data class LandingItemUio(
val feature: Feature,
@DrawableRes val icon: Int?,
val title: String?,
val subTitle: String?,
)
) {
enum class Feature {
CHARACTER,
GAME_MASTER,
SEARCH,
LEXICON,
QUEST,
MAP,
ADVENTURE,
}
}
@Composable
fun LandingItem(
@ -64,12 +75,12 @@ fun LandingItem(
@FloatRange(from = 0.0, to = 1.0) backgroundGradientTo: Float = 1f,
rotation: Float = 0f,
alpha: Float = 0.6f,
onClick: () -> Unit,
onClick: (LandingItemUio) -> Unit,
) {
Box(
modifier = Modifier
.clip(shape = shape)
.clickable(onClick = onClick)
.clickable { onClick(item) }
.padding(paddingValues = paddings)
.then(other = modifier),
contentAlignment = Alignment.BottomCenter
@ -167,26 +178,31 @@ private fun LandingItemPreview(
fun landingItems() = listOf(
LandingItemUio(
feature = LandingItemUio.Feature.CHARACTER,
icon = R.drawable.icbg_class_barbarian,
title = "Brulkhai",
subTitle = "Barbare"
),
LandingItemUio(
feature = LandingItemUio.Feature.CHARACTER,
icon = R.drawable.icbg_class_cleric,
title = "Léandre",
subTitle = "Clerc"
),
LandingItemUio(
feature = LandingItemUio.Feature.CHARACTER,
icon = R.drawable.icbg_class_ranger,
title = "Nélia",
subTitle = "Rôdeur"
),
LandingItemUio(
feature = LandingItemUio.Feature.CHARACTER,
icon = R.drawable.icbg_class_warlock,
title = "Tigrane",
subTitle = "Occultiste",
),
LandingItemUio(
feature = LandingItemUio.Feature.CHARACTER,
icon = R.drawable.icbg_class_bard,
title = "Unathana",
subTitle = "Barde",

View file

@ -30,7 +30,6 @@ import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
@ -58,6 +57,12 @@ import com.pixelized.rplexicon.ui.navigation.screens.navigateToLocation
import com.pixelized.rplexicon.ui.navigation.screens.navigateToQuestList
import com.pixelized.rplexicon.ui.navigation.screens.navigateToSearch
import com.pixelized.rplexicon.ui.navigation.screens.navigateToSummary
import com.pixelized.rplexicon.ui.screens.landing.LandingItemUio.Feature.ADVENTURE
import com.pixelized.rplexicon.ui.screens.landing.LandingItemUio.Feature.CHARACTER
import com.pixelized.rplexicon.ui.screens.landing.LandingItemUio.Feature.GAME_MASTER
import com.pixelized.rplexicon.ui.screens.landing.LandingItemUio.Feature.LEXICON
import com.pixelized.rplexicon.ui.screens.landing.LandingItemUio.Feature.MAP
import com.pixelized.rplexicon.ui.screens.landing.LandingItemUio.Feature.SEARCH
import com.pixelized.rplexicon.ui.theme.LexiconTheme
import com.pixelized.rplexicon.utilitary.annotateMajWithDropCap
import com.pixelized.rplexicon.utilitary.extentions.lexicon
@ -82,23 +87,45 @@ fun LandingScreen(
end = 16.dp,
bottom = 16.dp,
),
characters = viewModel.landing,
onCharacter = { character ->
character.title?.let { screen.navigateToCharacterSheet(name = it) }
},
onSearch = {
characters = viewModel.characterSheets,
tools = viewModel.toolFeatures,
encyclopedia = viewModel.lexiconFeatures,
onFeature = {
when (it.feature) {
CHARACTER -> it.title?.let { name ->
screen.navigateToCharacterSheet(name = name)
}
GAME_MASTER -> {
screen.navigateToSummary()
}
SEARCH -> {
screen.navigateToSearch(
enableLexicon = true,
enableQuests = true,
enableLocations = true,
enableSpells = true,
)
},
onGameMaster = { screen.navigateToSummary() },
onLexicon = { screen.navigateToLexicon() },
onQuest = { screen.navigateToQuestList() },
onMap = { screen.navigateToLocation() },
onAdventure = { screen.navigateToAdventures() },
}
LEXICON -> {
screen.navigateToLexicon()
}
LandingItemUio.Feature.QUEST -> {
screen.navigateToQuestList()
}
MAP -> {
screen.navigateToLocation()
}
ADVENTURE -> {
screen.navigateToAdventures()
}
}
}
)
}
}
@ -114,23 +141,11 @@ private fun LandingContent(
bottom = 16.dp,
),
sectionPadding: Dp = 32.dp,
characters: State<List<LandingItemUio>>,
onCharacter: (LandingItemUio) -> Unit,
onSearch: () -> Unit,
onGameMaster: () -> Unit,
onLexicon: () -> Unit,
onQuest: () -> Unit,
onMap: () -> Unit,
onAdventure: () -> Unit,
characters: State<List<List<LandingItemUio>>>,
tools: State<List<List<LandingItemUio>>>,
encyclopedia: State<List<List<LandingItemUio>>>,
onFeature: (LandingItemUio) -> Unit,
) {
val charactersSection = remember {
derivedStateOf {
characters.value
.groupBy { characters.value.indexOf(it) / 3 }
.values.toList()
}
}
Box(
modifier = modifier.verticalScroll(scrollState),
) {
@ -140,7 +155,6 @@ private fun LandingContent(
.aspectRatio(1f)
.clip(RectangleShape),
) {
Image(
modifier = Modifier
.matchParentSize()
@ -187,6 +201,7 @@ private fun LandingContent(
),
)
if (characters.value.isNotEmpty()) {
Text(
modifier = Modifier.padding(top = sectionPadding),
style = MaterialTheme.typography.labelSmall,
@ -194,8 +209,8 @@ private fun LandingContent(
fontWeight = FontWeight.Light,
text = stringResource(id = R.string.landing__caterogy__character),
)
charactersSection.value.forEach { charactersGroup ->
}
characters.value.forEach { charactersGroup ->
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(space = 8.dp),
@ -212,7 +227,7 @@ private fun LandingContent(
backgroundFilter = null,
backgroundGradientFrom = 0f,
backgroundGradientTo = 0.5f,
onClick = { onCharacter(character) },
onClick = { onFeature(character) },
)
}
repeat(3 - charactersGroup.size) {
@ -221,6 +236,7 @@ private fun LandingContent(
}
}
if (tools.value.isNotEmpty()) {
Text(
modifier = Modifier.padding(top = sectionPadding),
style = MaterialTheme.typography.labelSmall,
@ -228,11 +244,13 @@ private fun LandingContent(
fontWeight = FontWeight.Light,
text = stringResource(id = R.string.landing__caterogy__tools),
)
}
tools.value.forEach { group ->
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(space = 8.dp),
) {
group.forEach { item ->
LandingItem(
modifier = Modifier
.weight(1f)
@ -241,41 +259,22 @@ private fun LandingContent(
top = 8.dp,
start = 16.dp,
end = 16.dp,
bottom = 24.dp
),
item = LandingItemUio(
title = stringResource(id = R.string.summary__title),
subTitle = null,
icon = R.drawable.icbg_skull_b_unfaded,
bottom = 24.dp,
),
item = item,
backgroundFilter = null,
backgroundGradientFrom = 0.0f,
backgroundGradientTo = 0.5f,
onClick = onGameMaster,
)
LandingItem(
modifier = Modifier
.weight(1f)
.aspectRatio(ratio = 1f),
imagePadding = PaddingValues(
top = 8.dp,
start = 16.dp,
end = 16.dp,
bottom = 24.dp
),
item = LandingItemUio(
title = stringResource(id = R.string.default_search_label),
subTitle = null,
icon = R.drawable.icbg_magnifying_glass,
),
backgroundFilter = null,
backgroundGradientFrom = 0.0f,
backgroundGradientTo = 0.5f,
onClick = onSearch,
onClick = { onFeature(item) },
)
}
repeat(3 - group.size) {
Spacer(modifier = Modifier.weight(1f))
}
}
}
if (encyclopedia.value.isNotEmpty()) {
Text(
modifier = Modifier.padding(top = sectionPadding),
style = MaterialTheme.typography.labelSmall,
@ -283,11 +282,13 @@ private fun LandingContent(
fontWeight = FontWeight.Light,
text = stringResource(id = R.string.landing__caterogy__encyclopedia),
)
}
encyclopedia.value.forEach { group ->
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(space = 8.dp),
) {
group.forEach { item ->
LandingItem(
modifier = Modifier
.weight(1f)
@ -296,86 +297,19 @@ private fun LandingContent(
top = 8.dp,
start = 16.dp,
end = 16.dp,
bottom = 24.dp
),
item = LandingItemUio(
title = stringResource(id = R.string.lexicon_list__title),
subTitle = null,
icon = R.drawable.icbg_book_note_k,
bottom = 24.dp,
),
item = item,
backgroundFilter = null,
backgroundGradientFrom = 0.0f,
backgroundGradientTo = 0.5f,
onClick = onLexicon,
)
LandingItem(
modifier = Modifier
.weight(1f)
.aspectRatio(ratio = 1f),
imagePadding = PaddingValues(
top = 8.dp,
start = 16.dp,
end = 16.dp,
bottom = 24.dp
),
item = LandingItemUio(
title = stringResource(id = R.string.quest_list__title),
subTitle = null,
icon = R.drawable.icbg_book_parchment_e,
),
backgroundFilter = null,
backgroundGradientFrom = 0.0f,
backgroundGradientTo = 0.5f,
onClick = onQuest,
)
LandingItem(
modifier = Modifier
.weight(1f)
.aspectRatio(ratio = 1f),
imagePadding = PaddingValues(
top = 8.dp,
start = 16.dp,
end = 16.dp,
bottom = 24.dp
),
item = LandingItemUio(
title = stringResource(id = R.string.location_list__title),
subTitle = null,
icon = R.drawable.icbg_map_world_a,
),
backgroundFilter = null,
backgroundGradientFrom = 0.0f,
backgroundGradientTo = 0.5f,
onClick = onMap,
onClick = { onFeature(item) },
)
}
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(space = 8.dp),
) {
LandingItem(
modifier = Modifier
.weight(1f)
.aspectRatio(ratio = 1f),
imagePadding = PaddingValues(
top = 8.dp,
start = 16.dp,
end = 16.dp,
bottom = 24.dp
),
item = LandingItemUio(
title = stringResource(id = R.string.adventures_title),
subTitle = null,
icon = R.drawable.icbg_book_generic_c_unfaded,
),
backgroundFilter = null,
backgroundGradientFrom = 0.0f,
backgroundGradientTo = 0.5f,
onClick = onAdventure,
)
Spacer(modifier = Modifier.weight(2f))
repeat(3 - group.size) {
Spacer(modifier = Modifier.weight(1f))
}
}
}
}
}
@ -427,42 +361,99 @@ private fun LandingPreview() {
modifier = Modifier.fillMaxSize(),
characters = remember {
mutableStateOf(
listOf(
listOf(
LandingItemUio(
feature = CHARACTER,
icon = R.drawable.icbg_class_barbarian,
title = "Brulkhai",
subTitle = "Barbarian, Warrior",
),
LandingItemUio(
feature = CHARACTER,
icon = R.drawable.icbg_class_cleric,
title = "Léandre",
subTitle = "Cleric",
),
LandingItemUio(
feature = CHARACTER,
icon = R.drawable.icbg_class_ranger,
title = "Nelia",
subTitle = "Ranger, Druid",
),
),
listOf(
LandingItemUio(
feature = CHARACTER,
icon = R.drawable.icbg_class_warlock,
title = "Tigrane",
subTitle = "Warlock",
),
LandingItemUio(
feature = CHARACTER,
icon = R.drawable.icbg_class_bard,
title = "Unathana",
subTitle = "Bard",
),
)
)
)
},
onCharacter = { },
onSearch = { },
onGameMaster = { },
onLexicon = { },
onQuest = { },
onMap = { },
onAdventure = { },
tools = remember {
mutableStateOf(
listOf(
listOf(
LandingItemUio(
feature = LandingItemUio.Feature.GAME_MASTER,
title = "Game Master",
subTitle = null,
icon = R.drawable.icbg_skull_b_unfaded,
),
LandingItemUio(
feature = LandingItemUio.Feature.SEARCH,
title = "Search",
subTitle = null,
icon = R.drawable.icbg_magnifying_glass,
)
),
),
)
},
encyclopedia = remember {
mutableStateOf(
listOf(
listOf(
LandingItemUio(
feature = LandingItemUio.Feature.LEXICON,
title = "Lexicon",
subTitle = null,
icon = R.drawable.icbg_book_note_k,
),
LandingItemUio(
feature = LandingItemUio.Feature.QUEST,
title = "Quest logs",
subTitle = null,
icon = R.drawable.icbg_book_parchment_e,
),
LandingItemUio(
feature = LandingItemUio.Feature.MAP,
title = "Location",
subTitle = null,
icon = R.drawable.icbg_map_world_a,
),
),
listOf(
LandingItemUio(
feature = LandingItemUio.Feature.ADVENTURE,
title = "Stories & Adventures",
subTitle = null,
icon = R.drawable.icbg_book_generic_c_unfaded,
),
),
),
)
},
onFeature = { },
)
}
}

View file

@ -1,32 +1,33 @@
package com.pixelized.rplexicon.ui.screens.landing
import android.app.Application
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.data.repository.character.CharacterSheetRepository
import com.pixelized.rplexicon.data.repository.firebase.RemoteConfigRepository
import com.pixelized.rplexicon.utilitary.extentions.context
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.coroutines.flow.map
import javax.inject.Inject
@HiltViewModel
class LandingViewModel @Inject constructor(
private val sheetRepository: CharacterSheetRepository,
private val configRepository: RemoteConfigRepository,
application: Application,
) : AndroidViewModel(application = application) {
private val _landing = mutableStateOf<List<LandingItemUio>>(emptyList())
val landing: State<List<LandingItemUio>> = _landing
init {
viewModelScope.launch(Dispatchers.Default) {
sheetRepository.data.collectLatest { sheets ->
val landing = sheets.mapNotNull { entry ->
val characterSheets: State<List<List<LandingItemUio>>>
@Composable
get() = remember {
sheetRepository.data
.map { sheets ->
sheets.mapNotNull { entry ->
val icon = entry.value.characterClass.firstOrNull()?.icon
val title = entry.key
val subTitle = entry.value.characterClass
@ -34,15 +35,106 @@ class LandingViewModel @Inject constructor(
.joinToString(separator = ", ") { it }
LandingItemUio(
feature = LandingItemUio.Feature.CHARACTER,
icon = icon,
title = title,
subTitle = subTitle,
)
}
withContext(Dispatchers.Main) {
_landing.value = landing
}
.map { items ->
items.groupBy { items.indexOf(it) / 3 }.values.toList()
}
}.collectAsState(initial = emptyList())
val toolFeatures: State<List<List<LandingItemUio>>>
@Composable
get() {
val context = LocalContext.current
return remember {
configRepository.config
.map { config ->
listOfNotNull(
when (config.featureSummary) {
true -> LandingItemUio(
feature = LandingItemUio.Feature.GAME_MASTER,
title = context.getString(R.string.summary__title),
subTitle = null,
icon = R.drawable.icbg_skull_b_unfaded,
)
else -> null
},
when (config.featureSearch) {
true -> LandingItemUio(
feature = LandingItemUio.Feature.SEARCH,
title = context.getString(R.string.default_search_label),
subTitle = null,
icon = R.drawable.icbg_magnifying_glass,
)
else -> null
},
)
}.map { items ->
items.groupBy { items.indexOf(it) / 3 }.values.toList()
}
}.collectAsState(initial = emptyList())
}
val lexiconFeatures: State<List<List<LandingItemUio>>>
@Composable
get() {
val context = LocalContext.current
return remember {
configRepository.config
.map { config ->
listOfNotNull(
when (config.featureLexicon) {
true -> LandingItemUio(
feature = LandingItemUio.Feature.LEXICON,
title = context.getString(R.string.lexicon_list__title),
subTitle = null,
icon = R.drawable.icbg_book_note_k,
)
else -> null
},
when (config.featureQuests) {
true -> LandingItemUio(
feature = LandingItemUio.Feature.QUEST,
title = context.getString(R.string.quest_list__title),
subTitle = null,
icon = R.drawable.icbg_book_parchment_e,
)
else -> null
},
when (config.featureMaps) {
true -> LandingItemUio(
feature = LandingItemUio.Feature.MAP,
title = context.getString(R.string.location_list__title),
subTitle = null,
icon = R.drawable.icbg_map_world_a,
)
else -> null
},
when (config.featureAdventure) {
true -> LandingItemUio(
feature = LandingItemUio.Feature.ADVENTURE,
title = context.getString(R.string.adventures_title),
subTitle = null,
icon = R.drawable.icbg_book_generic_c_unfaded,
)
else -> null
},
)
}
.map { items ->
items.groupBy { items.indexOf(it) / 3 }.values.toList()
}
}.collectAsState(initial = emptyList())
}
}

View file

@ -14,7 +14,7 @@ import com.pixelized.rplexicon.data.model.CharacterSheet
import com.pixelized.rplexicon.data.model.Description
import com.pixelized.rplexicon.data.model.DiceThrow
import com.pixelized.rplexicon.data.model.alteration.AlterationStatus
import com.pixelized.rplexicon.data.repository.authentication.FirebaseRepository
import com.pixelized.rplexicon.data.repository.firebase.RealtimeDatabaseRepository
import com.pixelized.rplexicon.data.repository.character.ActiveAlterationRepository
import com.pixelized.rplexicon.data.repository.character.AlterationRepository
import com.pixelized.rplexicon.data.repository.character.CharacterSheetRepository
@ -28,7 +28,6 @@ import com.pixelized.rplexicon.ui.screens.rolls.composable.ThrowsCardUio
import com.pixelized.rplexicon.ui.screens.rolls.factory.AlterationFactory
import com.pixelized.rplexicon.ui.screens.rolls.factory.DiceFactory
import com.pixelized.rplexicon.utilitary.extentions.context
import com.pixelized.rplexicon.utilitary.extentions.string.alterationIcon
import com.pixelized.rplexicon.utilitary.extentions.switch
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
@ -50,7 +49,7 @@ class RollOverlayViewModel @Inject constructor(
private val rollUseCase: DiceThrowUseCase,
private val diceFactory: DiceFactory,
private val alterationFactory: AlterationFactory,
private val firebaseRepository: FirebaseRepository,
private val firebaseRepository: RealtimeDatabaseRepository,
private val throwCardFactory: ThrowCardFactory,
application: Application,
) : AndroidViewModel(application) {

View file

@ -6,7 +6,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.data.repository.authentication.FirebaseRepository
import com.pixelized.rplexicon.data.repository.firebase.RealtimeDatabaseRepository
import com.pixelized.rplexicon.data.repository.character.AlterationRepository
import com.pixelized.rplexicon.data.repository.character.DescriptionRepository
import com.pixelized.rplexicon.ui.screens.character.composable.dialogs.AlterationDialogDetailUio
@ -14,7 +14,6 @@ import com.pixelized.rplexicon.ui.screens.rolls.composable.ThrowCardFactory
import com.pixelized.rplexicon.ui.screens.rolls.composable.ThrowsCardUio
import com.pixelized.rplexicon.ui.screens.summary.composable.ClassHeaderSummaryUio
import com.pixelized.rplexicon.utilitary.extentions.context
import com.pixelized.rplexicon.utilitary.extentions.string.alterationIcon
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
@ -26,7 +25,7 @@ import javax.inject.Inject
@HiltViewModel
class StatisticViewModel @Inject constructor(
private val firebaseRepository: FirebaseRepository,
private val firebaseRepository: RealtimeDatabaseRepository,
private val throwCardFactory: ThrowCardFactory,
private val alterationRepository: AlterationRepository,
private val descriptionRepository: DescriptionRepository,

View file

@ -8,7 +8,7 @@ import com.pixelized.rplexicon.data.model.Property
import com.pixelized.rplexicon.data.model.alteration.Alteration
import com.pixelized.rplexicon.data.network.CharacterSheetFire
import com.pixelized.rplexicon.data.network.NetworkThrow
import com.pixelized.rplexicon.data.repository.authentication.FirebaseRepository
import com.pixelized.rplexicon.data.repository.firebase.RealtimeDatabaseRepository
import com.pixelized.rplexicon.data.repository.character.ActiveAlterationRepository
import com.pixelized.rplexicon.data.repository.character.CharacterSheetRepository
import com.pixelized.rplexicon.ui.screens.summary.composable.AttributesSummaryUio
@ -44,7 +44,7 @@ import kotlin.math.max
class SummaryFactory @Inject constructor(
private val characterSheetRepository: CharacterSheetRepository,
private val activeAlterationRepository: ActiveAlterationRepository,
private val firebaseRepository: FirebaseRepository,
private val firebaseRepository: RealtimeDatabaseRepository,
) {
fun fetchSummary(scope: CoroutineScope): StatisticSummaryUio {
val headerVisibility = mutableStateOf(false)