diff --git a/app/src/main/java/com/pixelized/rplexicon/LauncherViewModel.kt b/app/src/main/java/com/pixelized/rplexicon/LauncherViewModel.kt index e9dc555..365d3cd 100644 --- a/app/src/main/java/com/pixelized/rplexicon/LauncherViewModel.kt +++ b/app/src/main/java/com/pixelized/rplexicon/LauncherViewModel.kt @@ -5,9 +5,11 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.pixelized.rplexicon.model.Description 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.DescriptionRepository import com.pixelized.rplexicon.repository.data.LexiconRepository import com.pixelized.rplexicon.repository.data.LocationRepository import com.pixelized.rplexicon.repository.data.QuestRepository @@ -29,6 +31,7 @@ class LauncherViewModel @Inject constructor( characterSheetRepository: CharacterSheetRepository, actionRepository: ActionRepository, spellRepository: SpellRepository, + descriptionRepository: DescriptionRepository, ) : ViewModel() { private val _error = MutableSharedFlow() @@ -67,7 +70,14 @@ class LauncherViewModel @Inject constructor( _error.tryEmit("CharacterSheet fail to update") } } - awaitAll(lexicon, location, quest, characterSheet) + val descriptionSheet = async { + try { + descriptionRepository.fetchDescription() + } catch (exception: Exception) { + _error.tryEmit("Skill/Spell description fail to update") + } + } + awaitAll(lexicon, location, quest, characterSheet, descriptionSheet) val alteration = async { try { diff --git a/app/src/main/java/com/pixelized/rplexicon/model/Alteration.kt b/app/src/main/java/com/pixelized/rplexicon/model/Alteration.kt index d65a2d9..8ee8765 100644 --- a/app/src/main/java/com/pixelized/rplexicon/model/Alteration.kt +++ b/app/src/main/java/com/pixelized/rplexicon/model/Alteration.kt @@ -5,7 +5,6 @@ data class Alteration( val source: String, val target: String, val active: Boolean = false, - val description: String, val status: Map, ) { data class Status( diff --git a/app/src/main/java/com/pixelized/rplexicon/model/Counter.kt b/app/src/main/java/com/pixelized/rplexicon/model/Counter.kt deleted file mode 100644 index 8c7f5d0..0000000 --- a/app/src/main/java/com/pixelized/rplexicon/model/Counter.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.pixelized.rplexicon.model - -data class Counter( - val title: String? = null, - val value: Int, - val max: Int?, -) \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/model/Description.kt b/app/src/main/java/com/pixelized/rplexicon/model/Description.kt new file mode 100644 index 0000000..f6873be --- /dev/null +++ b/app/src/main/java/com/pixelized/rplexicon/model/Description.kt @@ -0,0 +1,7 @@ +package com.pixelized.rplexicon.model + +data class Description( + val name: String, + val original: String, + val description: String, +) \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/model/Spell.kt b/app/src/main/java/com/pixelized/rplexicon/model/Spell.kt index ecd2648..5e6627c 100644 --- a/app/src/main/java/com/pixelized/rplexicon/model/Spell.kt +++ b/app/src/main/java/com/pixelized/rplexicon/model/Spell.kt @@ -9,7 +9,6 @@ data class Spell( val range: String, val requirement: String, val duration: String, - val description: String, val ritual: Boolean, ) { enum class School(val key: String) { diff --git a/app/src/main/java/com/pixelized/rplexicon/repository/data/DescriptionRepository.kt b/app/src/main/java/com/pixelized/rplexicon/repository/data/DescriptionRepository.kt new file mode 100644 index 0000000..6bf8b85 --- /dev/null +++ b/app/src/main/java/com/pixelized/rplexicon/repository/data/DescriptionRepository.kt @@ -0,0 +1,36 @@ +package com.pixelized.rplexicon.repository.data + +import com.pixelized.rplexicon.model.Description +import com.pixelized.rplexicon.repository.GoogleSheetServiceRepository +import com.pixelized.rplexicon.repository.parser.DescriptionParser +import com.pixelized.rplexicon.utilitary.Update +import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class DescriptionRepository @Inject constructor( + private val googleRepository: GoogleSheetServiceRepository, + private val parser: DescriptionParser, +) { + private val _data = MutableStateFlow>(emptyMap()) + val data: StateFlow> get() = _data + + var lastSuccessFullUpdate: Update = Update.INITIAL + private set + + fun find(name: String?): Description? = _data.value[name] + + @Throws(IncompatibleSheetStructure::class, Exception::class) + suspend fun fetchDescription() { + googleRepository.fetch { sheet -> + val request = sheet.get(Sheet.Character.ID, Sheet.Character.DESCRIPTION) + val data = parser.parse(data = request.execute()) + _data.tryEmit(data) + + lastSuccessFullUpdate = Update.currentTime() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/repository/data/Sheet.kt b/app/src/main/java/com/pixelized/rplexicon/repository/data/Sheet.kt index 1e1d0e8..fec8f54 100644 --- a/app/src/main/java/com/pixelized/rplexicon/repository/data/Sheet.kt +++ b/app/src/main/java/com/pixelized/rplexicon/repository/data/Sheet.kt @@ -1,5 +1,7 @@ package com.pixelized.rplexicon.repository.data +import com.pixelized.rplexicon.model.Description + object Sheet { object Lexicon { const val ID = "1oL9Nu5y37BPEbKxHre4TN9o8nrgy2JQoON4RRkdAHMs" @@ -17,7 +19,7 @@ object Sheet { const val ATTACK = "Attaques" const val MAGIC = "Magies" const val MAGIC_LEXICON = "Lexique magique" - const val STATUS = "État des personnages" const val ALTERATION = "Altérations" + const val DESCRIPTION = "Descriptions" } } \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/repository/parser/DescriptionParser.kt b/app/src/main/java/com/pixelized/rplexicon/repository/parser/DescriptionParser.kt new file mode 100644 index 0000000..e949086 --- /dev/null +++ b/app/src/main/java/com/pixelized/rplexicon/repository/parser/DescriptionParser.kt @@ -0,0 +1,55 @@ +package com.pixelized.rplexicon.repository.parser + +import com.google.api.services.sheets.v4.model.ValueRange +import com.pixelized.rplexicon.model.Description +import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure +import com.pixelized.rplexicon.utilitary.extentions.local.checkSheetStructure +import com.pixelized.rplexicon.utilitary.extentions.sheet +import javax.inject.Inject + +class DescriptionParser @Inject constructor() { + + @Throws(IncompatibleSheetStructure::class) + fun parse(data: ValueRange): Map { + val sheet = data.values.sheet() + val values = hashMapOf() + + lateinit var structure: Map + + sheet?.forEachIndexed { index, item -> + when { + index == 0 -> { + structure = item.checkSheetStructure(model = COLUMNS) + } + + item is List<*> -> { + val name = item[structure.getValue(NAME)] as? String + val translate = item[structure.getValue(TRANSLATE)] as? String + val description = item[structure.getValue(DESCRIPTION)] as? String + + if (name != null && translate != null && description != null) { + values[name] = Description( + name = name, + original = translate, + description = description, + ) + } + } + } + } + + return values + } + + companion object { + private const val NAME = "Nom" + private const val TRANSLATE = "Traduction" + private const val DESCRIPTION = "Description" + + private val COLUMNS = listOf( + NAME, + TRANSLATE, + DESCRIPTION, + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/repository/parser/alteration/AlterationParser.kt b/app/src/main/java/com/pixelized/rplexicon/repository/parser/alteration/AlterationParser.kt index 1b46a57..d230a1d 100644 --- a/app/src/main/java/com/pixelized/rplexicon/repository/parser/alteration/AlterationParser.kt +++ b/app/src/main/java/com/pixelized/rplexicon/repository/parser/alteration/AlterationParser.kt @@ -46,7 +46,6 @@ class AlterationParser @Inject constructor( source = source, target = target, active = false, - description = row.parseString(DESCRIPTION) ?: "", status = properties .mapNotNull { property -> val column = alterationStructure.getValue(property.key) @@ -128,8 +127,7 @@ class AlterationParser @Inject constructor( private const val TARGET = "Cible" private const val SOURCE = "Source" - private const val DESCRIPTION = "Description" private val COLUMNS - get() = listOf(SOURCE, TARGET, DESCRIPTION) + Property.values().map { it.key } + get() = listOf(SOURCE, TARGET) + Property.values().map { it.key } } } \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/repository/parser/alteration/CounterParser.kt b/app/src/main/java/com/pixelized/rplexicon/repository/parser/alteration/CounterParser.kt deleted file mode 100644 index 6ba6933..0000000 --- a/app/src/main/java/com/pixelized/rplexicon/repository/parser/alteration/CounterParser.kt +++ /dev/null @@ -1,60 +0,0 @@ -package com.pixelized.rplexicon.repository.parser.alteration - -import com.google.api.services.sheets.v4.model.ValueRange -import com.pixelized.rplexicon.model.Counter -import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure -import com.pixelized.rplexicon.utilitary.extentions.sheet -import javax.inject.Inject - -class CounterParser @Inject constructor() { - - @Throws(IncompatibleSheetStructure::class) - fun parse(values: ValueRange): Map> { - val sheet = values.values.sheet() - lateinit var characters: List - val counters = hashMapOf>() - - sheet?.mapNotNull { it as? List<*> }?.forEachIndexed { columnIndex, row -> - when (columnIndex) { - 0 -> characters = row - .subList(fromIndex = 1, toIndex = row.size) - .map { it.toString() } - - else -> { - row.getOrNull(0)?.toString()?.let { title -> - row.subList(fromIndex = 1, toIndex = row.size) - .forEachIndexed { rowIndex, value -> - val counter = parseCounter(title = title, value = value?.toString()) - if (counter != null) { - counters - .getOrPut(characters[rowIndex]) { mutableListOf() } - .add(counter) - } - } - } - } - } - } - - return counters - } - - fun parseCounter(title: String? = null, value: String?): Counter? { - return if (value != null) { - COUNTER_REGEX.find(value)?.let { - val (actual, max) = it.destructured - Counter( - title = title, - value = actual.toIntOrNull() ?: 0, - max = max.toIntOrNull(), - ) - } - } else { - null - } - } - - companion object { - val COUNTER_REGEX = Regex("(\\d+)\\/(\\d+)") - } -} \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/repository/parser/spell/SpellBookParser.kt b/app/src/main/java/com/pixelized/rplexicon/repository/parser/spell/SpellBookParser.kt index 6aaee01..a4d40ba 100644 --- a/app/src/main/java/com/pixelized/rplexicon/repository/parser/spell/SpellBookParser.kt +++ b/app/src/main/java/com/pixelized/rplexicon/repository/parser/spell/SpellBookParser.kt @@ -37,7 +37,6 @@ class SpellBookParser @Inject constructor() { val range = row.parse(RANGE) val requirement = row.parse(REQUIREMENT) val duration = row.parse(DURATION) - val description = row.parse(DESCRIPTION) val ritual = row.parse(RITUAL)?.toBoolean() ?: false if (name != null && level != null @@ -47,7 +46,6 @@ class SpellBookParser @Inject constructor() { && range != null && requirement != null && duration != null - && description != null ) { Spell( name = name, @@ -58,7 +56,6 @@ class SpellBookParser @Inject constructor() { range = range, requirement = requirement, duration = duration, - description = description, ritual = ritual, ) } else { @@ -88,7 +85,6 @@ class SpellBookParser @Inject constructor() { private const val REQUIREMENT = "Composantes" private const val DURATION = "Durée" private const val RITUAL = "Rituel" - private const val DESCRIPTION = "Description" private val COLUMNS get() = listOf( @@ -101,7 +97,6 @@ class SpellBookParser @Inject constructor() { REQUIREMENT, DURATION, RITUAL, - DESCRIPTION, ) } } \ No newline at end of file diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/composable/error/FetchErrorUio.kt b/app/src/main/java/com/pixelized/rplexicon/ui/composable/error/FetchErrorUio.kt index 3a1b8d2..5ee87f7 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/composable/error/FetchErrorUio.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/composable/error/FetchErrorUio.kt @@ -9,7 +9,7 @@ import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.platform.LocalContext import com.pixelized.rplexicon.LocalSnack import com.pixelized.rplexicon.R -import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.Flow @Stable sealed class FetchErrorUio { @@ -25,7 +25,7 @@ sealed class FetchErrorUio { @Composable fun HandleFetchError( - errors: SharedFlow, + errors: Flow, onStructureError: suspend (context: Context, snack: SnackbarHostState, error: FetchErrorUio.Structure) -> Unit = { context, snack, _ -> snack.showSnackbar(message = context.getString(R.string.error_structure)) }, diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/CharacterSheetScreen.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/CharacterSheetScreen.kt index 9c74f19..d8a9f4b 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/CharacterSheetScreen.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/CharacterSheetScreen.kt @@ -8,7 +8,6 @@ import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.navigationBarsPadding @@ -42,7 +41,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.shadow import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource -import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameterProvider @@ -53,7 +51,6 @@ import com.pixelized.rplexicon.LocalSnack import com.pixelized.rplexicon.NO_WINDOW_INSETS import com.pixelized.rplexicon.R import com.pixelized.rplexicon.ui.composable.IndicatorStep -import com.pixelized.rplexicon.ui.composable.IndicatorStepPreview import com.pixelized.rplexicon.ui.composable.Loader import com.pixelized.rplexicon.ui.navigation.LocalScreenNavHost import com.pixelized.rplexicon.ui.screens.character.composable.character.ProficiencyUio.ID.* @@ -112,6 +109,7 @@ fun CharacterSheetScreen( pagerState = pagerState, sheetState = sheetState, refreshState = refresh, + name = viewModel.character, onRefresh = { scope.launch { viewModel.update(force = true) } }, @@ -120,6 +118,9 @@ fun CharacterSheetScreen( }, indicator = { IndicatorStep( + modifier = Modifier + .align(alignment = Alignment.BottomCenter) + .padding(all = 4.dp), count = pagerState.pageCount, selectedIndex = pagerState.currentPage, ) @@ -191,12 +192,13 @@ private fun CharacterSheetContent( sheetState: ModalBottomSheetState, refreshState: PullRefreshState, onRefresh: () -> Unit, + name: String, onBack: () -> Unit, loader: @Composable BoxScope.() -> Unit, proficiencies: @Composable PagerScope.() -> Unit, actions: @Composable PagerScope.() -> Unit, alterations: @Composable PagerScope.() -> Unit, - indicator: @Composable ColumnScope.() -> Unit, + indicator: @Composable BoxScope.() -> Unit, sheet: @Composable () -> Unit, ) { Scaffold( @@ -223,9 +225,7 @@ private fun CharacterSheetContent( } }, title = { - Text( - text = stringResource(id = R.string.character_sheet_title), - ) + Text(text = name) }, ) }, @@ -266,8 +266,8 @@ private fun CharacterSheetContent( } ) loader() + indicator() } - indicator() } } ) @@ -296,13 +296,22 @@ private fun CharacterScreenPreview( sheetState = sheetState, pagerState = rememberPagerState(initialPage = preview) { 2 }, refreshState = rememberPullRefreshState(refreshing = false, onRefresh = { }), - onRefresh = { }, + name = "Brulkhai", onBack = { }, + onRefresh = { }, loader = { }, proficiencies = { ProficiencyPreview() }, actions = { ActionPagePreview() }, alterations = { AlterationPagePreview() }, - indicator = { IndicatorStepPreview() }, + indicator = { + IndicatorStep( + modifier = Modifier + .align(alignment = Alignment.BottomCenter) + .padding(all = 4.dp), + count = 3, + selectedIndex = 0, + ) + }, sheet = { SpellLevelChooserPreview() }, ) } diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/CharacterSheetViewModel.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/CharacterSheetViewModel.kt index 4868b2a..a629980 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/CharacterSheetViewModel.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/CharacterSheetViewModel.kt @@ -3,6 +3,7 @@ package com.pixelized.rplexicon.ui.screens.character import android.util.Log import androidx.compose.runtime.State import androidx.compose.runtime.mutableStateOf +import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.pixelized.rplexicon.repository.authentication.FirebaseRepository @@ -11,6 +12,7 @@ import com.pixelized.rplexicon.repository.data.AlterationRepository import com.pixelized.rplexicon.repository.data.CharacterSheetRepository import com.pixelized.rplexicon.repository.data.SpellRepository import com.pixelized.rplexicon.ui.composable.error.FetchErrorUio +import com.pixelized.rplexicon.ui.navigation.screens.characterSheetArgument import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll @@ -27,6 +29,7 @@ class CharacterSheetViewModel @Inject constructor( private val actionRepository: ActionRepository, private val spellRepository: SpellRepository, private val firebaseRepository: FirebaseRepository, + savedStateHandle: SavedStateHandle, ) : ViewModel() { private val _isLoading = mutableStateOf(false) val isLoading: State get() = _isLoading @@ -34,6 +37,8 @@ class CharacterSheetViewModel @Inject constructor( private val _error = MutableSharedFlow() val errors: SharedFlow get() = _error + val character = savedStateHandle.characterSheetArgument.name + init { viewModelScope.launch { launch { diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/actions/ActionsPage.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/actions/ActionsPage.kt index d77731a..ddb8a91 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/actions/ActionsPage.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/actions/ActionsPage.kt @@ -149,7 +149,7 @@ fun ActionsPageContent( LazyColumn( modifier = modifier, state = lazyListState, - contentPadding = PaddingValues(bottom = 8.dp), + contentPadding = PaddingValues(bottom = 16.dp), ) { stickyHeader { CharacterSheetHeader( diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/actions/SpellsActionViewModel.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/actions/SpellsActionViewModel.kt index 8ce9614..2ebffd3 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/actions/SpellsActionViewModel.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/actions/SpellsActionViewModel.kt @@ -45,8 +45,8 @@ class SpellsActionViewModel @Inject constructor( savedStateHandle: SavedStateHandle, ) : AndroidViewModel(application) { private val characterName = savedStateHandle.characterSheetArgument.name - private val character: CharacterSheet - get() = characterRepository.data.value.getValue(characterName) + private var character: CharacterSheet? = null + private var characterFire: CharacterSheetFire? = null private val _spells = mutableStateOf>>>(emptyList()) val spells: State>>> get() = _spells @@ -60,9 +60,11 @@ class SpellsActionViewModel @Inject constructor( launch(Dispatchers.IO) { characterRepository.data .combine(spellRepository.spells) { sheets, spells -> + character = sheets.getValue(characterName) Struct(sheets = sheets, spells = spells) } .combine(firebaseRepository.getCharacter(character = characterName)) { struct, fire -> + characterFire = fire struct.also { it.fire = fire } } .collect { data -> @@ -136,18 +138,20 @@ class SpellsActionViewModel @Inject constructor( character = characterName, spell = name, ) - return when (character.isWarlock) { + return when (character?.isWarlock?: false) { true -> false - else -> character.highestSpellLevel() > (assignedSpell?.spell?.level ?: 1) + else -> (character?.highestSpellLevel() ?: 1) > (assignedSpell?.spell?.level ?: 1) } } fun prepareSpellCast(name: String) { + val character = character + val characterFire = characterFire val assignedSpell = spellRepository.find( character = characterName, spell = name, ) - if (assignedSpell != null) { + if (assignedSpell != null && character != null && characterFire != null) { val icon = assignedSpell.effect?.faces?.icon ?: R.drawable.ic_d20_24 val base = assignedSpell.effect?.toString(character = character, level = 1) ?: "" _preparedSpellLevel.value = SpellChooserUio( @@ -156,7 +160,7 @@ class SpellsActionViewModel @Inject constructor( size = max(0, character.highestSpellLevel() + 1 - assignedSpell.spell.level) ) { index -> val level = assignedSpell.spell.level + index - val remaining = character.spell(level) + val remaining = characterFire.spell(level) val max = character.spell(level) SpellLevelUio( @@ -179,8 +183,8 @@ class SpellsActionViewModel @Inject constructor( val spell = spellRepository.find(character = characterName, spell = id) return onCastSpell( id = id, - level = when (character.isWarlock) { - true -> character.firstSpellSlot() ?: 1 + level = when (character?.isWarlock ?: false) { + true -> character?.firstSpellSlot() ?: 1 else -> spell?.spell?.level ?: 1 }, ) diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/alteration/AlterationPage.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/alteration/AlterationPage.kt index c75a68e..6209705 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/alteration/AlterationPage.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/alteration/AlterationPage.kt @@ -59,7 +59,7 @@ fun AlterationPageContent( ) { LazyColumn( modifier = modifier, - contentPadding = PaddingValues(vertical = 8.dp), + contentPadding = PaddingValues(top = 8.dp, bottom = 16.dp), ) { items(items = alterations.value) { RollAlteration( diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/alteration/AlterationViewModel.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/alteration/AlterationViewModel.kt index 3c1bfba..644c688 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/alteration/AlterationViewModel.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/character/pages/alteration/AlterationViewModel.kt @@ -1,17 +1,19 @@ package com.pixelized.rplexicon.ui.screens.character.pages.alteration +import android.app.Application import androidx.compose.runtime.State import androidx.compose.runtime.mutableStateOf +import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.SavedStateHandle -import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.pixelized.rplexicon.repository.authentication.FirebaseRepository +import com.pixelized.rplexicon.R import com.pixelized.rplexicon.repository.data.AlterationRepository -import com.pixelized.rplexicon.repository.data.CharacterSheetRepository +import com.pixelized.rplexicon.repository.data.DescriptionRepository import com.pixelized.rplexicon.ui.navigation.screens.characterSheetArgument import com.pixelized.rplexicon.ui.screens.rolls.composable.AlterationDetailUio import com.pixelized.rplexicon.ui.screens.rolls.composable.RollAlterationUio import com.pixelized.rplexicon.ui.screens.rolls.factory.AlterationFactory +import com.pixelized.rplexicon.utilitary.extentions.context import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -21,9 +23,11 @@ import javax.inject.Inject @HiltViewModel class AlterationViewModel @Inject constructor( private val alterationRepository: AlterationRepository, + private val descriptionRepository: DescriptionRepository, private val factory: AlterationFactory, savedStateHandle: SavedStateHandle, -) : ViewModel() { + application: Application +) : AndroidViewModel(application) { private val character = savedStateHandle.characterSheetArgument.name private val _alterations = mutableStateOf>(emptyList()) @@ -56,12 +60,16 @@ class AlterationViewModel @Inject constructor( fun showAlterationDetail(id: String) { val alteration = alterationRepository.getAlterations(character = character) .firstOrNull { it.name == id } + val description = descriptionRepository.find(name = alteration?.name) + if (alteration != null) { _alterationDetail.value = AlterationDetailUio( name = id, + original = description?.original, source = alteration.source, target = alteration.target, - description = alteration.description + description = description?.description + ?: context.getString(R.string.no_available_description) ) } } diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/rolls/RollOverlayViewModel.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/rolls/RollOverlayViewModel.kt index 96de94d..9294e3c 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/rolls/RollOverlayViewModel.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/rolls/RollOverlayViewModel.kt @@ -5,15 +5,18 @@ import androidx.compose.runtime.State import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope +import com.pixelized.rplexicon.R import com.pixelized.rplexicon.business.DiceThrowUseCase import com.pixelized.rplexicon.model.DiceThrow import com.pixelized.rplexicon.repository.data.AlterationRepository +import com.pixelized.rplexicon.repository.data.DescriptionRepository import com.pixelized.rplexicon.ui.screens.rolls.composable.AlterationDetailUio import com.pixelized.rplexicon.ui.screens.rolls.composable.RollAlterationUio import com.pixelized.rplexicon.ui.screens.rolls.composable.RollDiceUio 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.switch import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Job @@ -24,6 +27,7 @@ import javax.inject.Inject @HiltViewModel class RollOverlayViewModel @Inject constructor( private val alterationRepository: AlterationRepository, + private val descriptionRepository: DescriptionRepository, private val rollUseCase: DiceThrowUseCase, private val diceFactory: DiceFactory, private val alterationFactory: AlterationFactory, @@ -98,12 +102,16 @@ class RollOverlayViewModel @Inject constructor( val alteration = diceThrow?.character?.let { character -> alterationRepository.getAlterations(character = character).firstOrNull { it.name == id } } + val description = descriptionRepository.find(name = alteration?.name) + if (alteration != null) { _alterationDetail.value = AlterationDetailUio( name = id, + original = description?.original, source = alteration.source, target = alteration.target, - description = alteration.description + description = description?.description + ?: context.getString(R.string.no_available_description) ) } } diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/rolls/composable/AlterationDetail.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/rolls/composable/AlterationDetail.kt index 1632d0b..912f406 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/rolls/composable/AlterationDetail.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/rolls/composable/AlterationDetail.kt @@ -4,6 +4,8 @@ import android.content.res.Configuration.UI_MODE_NIGHT_NO import android.content.res.Configuration.UI_MODE_NIGHT_YES import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ExperimentalLayoutApi +import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -25,6 +27,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview @@ -37,11 +40,13 @@ import com.pixelized.rplexicon.utilitary.extentions.lexicon @Stable data class AlterationDetailUio( val name: String, + val original: String?, val source: String, val target: String, val description: String, ) +@OptIn(ExperimentalLayoutApi::class) @Composable fun AlterationDetail( modifier: Modifier = Modifier, @@ -65,22 +70,38 @@ fun AlterationDetail( horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.Top, ) { - Text( + FlowRow( modifier = Modifier.padding(top = 16.dp), - style = MaterialTheme.typography.titleMedium, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - text = AnnotatedString( - text = detail.name, - spanStyles = listOf( - AnnotatedString.Range( - item = MaterialTheme.lexicon.typography.bodyDropCapSpan, - start = 0, - end = Integer.min(1, detail.name.length), + horizontalArrangement = Arrangement.spacedBy(4.dp), + ) { + Text( + modifier = Modifier.alignByBaseline(), + style = MaterialTheme.typography.titleMedium, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + text = AnnotatedString( + text = detail.name, + spanStyles = listOf( + AnnotatedString.Range( + item = MaterialTheme.lexicon.typography.bodyDropCapSpan, + start = 0, + end = Integer.min(1, detail.name.length), + ) ) + ), + ) + detail.original?.let { + Text( + modifier = Modifier.alignByBaseline(), + fontWeight = FontWeight.Light, + fontStyle = FontStyle.Italic, + style = MaterialTheme.typography.labelSmall, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + text = it, ) - ), - ) + } + } IconButton(onClick = onClose) { Icon( imageVector = Icons.Default.Close, @@ -123,6 +144,7 @@ private fun AlterationDetailPreview() { AlterationDetail( detail = AlterationDetailUio( name = "Rage", + original = "Rage", source = "Barbare", target = "Barbare", description = "\"En combat, vous vous battez avec une férocité bestiale. Durant votre tour, vous pouvez entrer en rage en utilisant une action bonus. En rage, vous gagnez les bénéfices suivants si vous ne portez pas d'armure lourde :\n" + diff --git a/app/src/main/java/com/pixelized/rplexicon/ui/screens/spell/SpellDetailViewModel.kt b/app/src/main/java/com/pixelized/rplexicon/ui/screens/spell/SpellDetailViewModel.kt index f951dd0..9f34120 100644 --- a/app/src/main/java/com/pixelized/rplexicon/ui/screens/spell/SpellDetailViewModel.kt +++ b/app/src/main/java/com/pixelized/rplexicon/ui/screens/spell/SpellDetailViewModel.kt @@ -5,48 +5,58 @@ import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.pixelized.rplexicon.repository.data.DescriptionRepository import com.pixelized.rplexicon.repository.data.SpellRepository import com.pixelized.rplexicon.ui.composable.error.FetchErrorUio import com.pixelized.rplexicon.ui.navigation.screens.spellDetailArgument import com.pixelized.rplexicon.utilitary.extentions.local.icon import com.pixelized.rplexicon.utilitary.extentions.local.label import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel class SpellDetailViewModel @Inject constructor( + spellRepository: SpellRepository, + descriptionRepository: DescriptionRepository, savedStateHandle: SavedStateHandle, - repository: SpellRepository ) : ViewModel() { val spell: State - val error: SharedFlow + val error: Flow init { val argument = savedStateHandle.spellDetailArgument - val assignedSpell = repository.find( + val description = descriptionRepository.find( + name = argument.spell, + ) + val spell = spellRepository.find( character = argument.character, spell = argument.spell, - )?.let { + ) + + val assignedSpell = if (spell != null && description != null) { SpellDetailUio( - icon = it.spell.school.icon, - name = it.spell.name, - translated = it.spell.originalName, - level = "${it.spell.level}", - school = it.spell.school.label, - castingTime = it.spell.castingTime, - range = it.spell.range, - requirement = it.spell.requirement, - duration = it.spell.duration, - description = it.spell.description, - ritual = it.spell.ritual, + icon = spell.spell.school.icon, + name = spell.spell.name, + translated = description.original, + level = "${spell.spell.level}", + school = spell.spell.school.label, + castingTime = spell.spell.castingTime, + range = spell.spell.range, + requirement = spell.spell.requirement, + duration = spell.spell.duration, + description = description.description, + ritual = spell.spell.ritual, ) + } else { + null } - spell = mutableStateOf(assignedSpell) - error = MutableSharedFlow().also { flow -> + this.spell = mutableStateOf(assignedSpell) + + this.error = MutableSharedFlow().also { flow -> if (assignedSpell == null) { viewModelScope.launch { flow.tryEmit(FetchErrorUio.Default) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 6672c0b..39698fa 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -155,6 +155,8 @@ Source : %1$s Cible : %1$s + Aucune description disponnible + Capacité Rage Endurance Implacable diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c6b8dca..dd942fc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -155,6 +155,8 @@ Source: %1$s Target: %1$s + No available description + Skill Rage Relentless Endurance