Add a specific tab for description.
This commit is contained in:
parent
d8f79b92f9
commit
6528b89f6b
23 changed files with 242 additions and 138 deletions
|
|
@ -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<String>()
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ data class Alteration(
|
|||
val source: String,
|
||||
val target: String,
|
||||
val active: Boolean = false,
|
||||
val description: String,
|
||||
val status: Map<Property, Status>,
|
||||
) {
|
||||
data class Status(
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
package com.pixelized.rplexicon.model
|
||||
|
||||
data class Counter(
|
||||
val title: String? = null,
|
||||
val value: Int,
|
||||
val max: Int?,
|
||||
)
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package com.pixelized.rplexicon.model
|
||||
|
||||
data class Description(
|
||||
val name: String,
|
||||
val original: String,
|
||||
val description: String,
|
||||
)
|
||||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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<Map<String, Description>>(emptyMap())
|
||||
val data: StateFlow<Map<String, Description>> 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
@ -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<String, Description> {
|
||||
val sheet = data.values.sheet()
|
||||
val values = hashMapOf<String, Description>()
|
||||
|
||||
lateinit var structure: Map<String, Int>
|
||||
|
||||
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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
|
@ -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<String, List<Counter>> {
|
||||
val sheet = values.values.sheet()
|
||||
lateinit var characters: List<String>
|
||||
val counters = hashMapOf<String, MutableList<Counter>>()
|
||||
|
||||
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+)")
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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<FetchErrorUio>,
|
||||
errors: Flow<FetchErrorUio>,
|
||||
onStructureError: suspend (context: Context, snack: SnackbarHostState, error: FetchErrorUio.Structure) -> Unit = { context, snack, _ ->
|
||||
snack.showSnackbar(message = context.getString(R.string.error_structure))
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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() },
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Boolean> get() = _isLoading
|
||||
|
|
@ -34,6 +37,8 @@ class CharacterSheetViewModel @Inject constructor(
|
|||
private val _error = MutableSharedFlow<FetchErrorUio.Firebase>()
|
||||
val errors: SharedFlow<FetchErrorUio.Firebase> get() = _error
|
||||
|
||||
val character = savedStateHandle.characterSheetArgument.name
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
launch {
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ fun ActionsPageContent(
|
|||
LazyColumn(
|
||||
modifier = modifier,
|
||||
state = lazyListState,
|
||||
contentPadding = PaddingValues(bottom = 8.dp),
|
||||
contentPadding = PaddingValues(bottom = 16.dp),
|
||||
) {
|
||||
stickyHeader {
|
||||
CharacterSheetHeader(
|
||||
|
|
|
|||
|
|
@ -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<List<Pair<SpellHeaderUio, List<SpellUio>>>>(emptyList())
|
||||
val spells: State<List<Pair<SpellHeaderUio, List<SpellUio>>>> 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
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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<List<RollAlterationUio>>(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)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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" +
|
||||
|
|
|
|||
|
|
@ -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<SpellDetailUio?>
|
||||
val error: SharedFlow<FetchErrorUio>
|
||||
val error: Flow<FetchErrorUio>
|
||||
|
||||
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<FetchErrorUio>().also { flow ->
|
||||
this.spell = mutableStateOf(assignedSpell)
|
||||
|
||||
this.error = MutableSharedFlow<FetchErrorUio>().also { flow ->
|
||||
if (assignedSpell == null) {
|
||||
viewModelScope.launch {
|
||||
flow.tryEmit(FetchErrorUio.Default)
|
||||
|
|
|
|||
|
|
@ -155,6 +155,8 @@
|
|||
<string name="alteration_source">Source : %1$s</string>
|
||||
<string name="alteration_target">Cible : %1$s</string>
|
||||
|
||||
<string name="no_available_description">Aucune description disponnible</string>
|
||||
|
||||
<string name="token_label_title">Capacité</string>
|
||||
<string name="token_label_rage">Rage</string>
|
||||
<string name="token_label_relentless_endurance">Endurance Implacable</string>
|
||||
|
|
|
|||
|
|
@ -155,6 +155,8 @@
|
|||
<string name="alteration_source">Source: %1$s</string>
|
||||
<string name="alteration_target">Target: %1$s</string>
|
||||
|
||||
<string name="no_available_description">No available description</string>
|
||||
|
||||
<string name="token_label_title">Skill</string>
|
||||
<string name="token_label_rage">Rage</string>
|
||||
<string name="token_label_relentless_endurance">Relentless Endurance</string>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue