Rework the detail system for object / skill / spell / alteration.

This commit is contained in:
Andres Gomez, Thomas (ITDV RL) 2024-06-13 10:55:10 +02:00
parent 6481afb732
commit 244bed9ded
26 changed files with 672 additions and 357 deletions

View file

@ -32,6 +32,7 @@ class SpellRepository @Inject constructor(
) {
private var _spellsBook = MutableStateFlow<List<Spell>>(emptyList())
val spellsBook: StateFlow<List<Spell>> get() = _spellsBook
private val _spells = MutableStateFlow<Map<String, List<AssignedSpell>>>(emptyMap())
val spells: StateFlow<Map<String, List<AssignedSpell>>> =
combine(_spells, firebaseRepository.getAlterationStatus()) { spells, status ->

View file

@ -82,8 +82,8 @@ import com.pixelized.rplexicon.ui.screens.character.pages.actions.SpellsViewMode
import com.pixelized.rplexicon.ui.screens.character.pages.alteration.AlterationPage
import com.pixelized.rplexicon.ui.screens.character.pages.alteration.AlterationPagePreview
import com.pixelized.rplexicon.ui.screens.character.pages.alteration.AlterationViewModel
import com.pixelized.rplexicon.ui.screens.character.pages.chooser.SpellLevelChooser
import com.pixelized.rplexicon.ui.screens.character.pages.chooser.SpellLevelChooserPreview
import com.pixelized.rplexicon.ui.screens.character.composable.chooser.SpellLevelChooser
import com.pixelized.rplexicon.ui.screens.character.composable.chooser.SpellLevelChooserPreview
import com.pixelized.rplexicon.ui.screens.character.pages.inventory.InventoryPage
import com.pixelized.rplexicon.ui.screens.character.pages.inventory.InventoryPagePreview
import com.pixelized.rplexicon.ui.screens.character.pages.inventory.InventoryViewModel

View file

@ -67,7 +67,7 @@ fun SpellLevelItem(
text = stringResource(id = R.string.spell_level_chooser_available)
)
Text(
modifier = Modifier.alignByBaseline(),
modifier = Modifier.alignByBaseline().padding(start = 4.dp),
style = MaterialTheme.typography.bodySmall,
fontWeight = FontWeight.Bold,
text = "${spell.remaining ?: 0}",

View file

@ -1,27 +0,0 @@
package com.pixelized.rplexicon.ui.screens.character.composable.alteration
import androidx.compose.runtime.Composable
import androidx.compose.runtime.State
import androidx.compose.runtime.remember
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import com.pixelized.rplexicon.ui.screens.character.pages.alteration.AlterationDetail
import com.pixelized.rplexicon.ui.screens.character.pages.alteration.AlterationDetailUio
@Composable
fun AlterationDetailHandler(
detail: State<AlterationDetailUio?>,
onDismissRequest: () -> Unit,
) {
detail.value?.let {
Dialog(
properties = remember { DialogProperties(usePlatformDefaultWidth = false) },
onDismissRequest = onDismissRequest,
) {
AlterationDetail(
detail = it,
onClose = onDismissRequest,
)
}
}
}

View file

@ -1,11 +1,10 @@
package com.pixelized.rplexicon.ui.screens.character.pages.chooser
package com.pixelized.rplexicon.ui.screens.character.composable.chooser
import android.content.res.Configuration.UI_MODE_NIGHT_NO
import android.content.res.Configuration.UI_MODE_NIGHT_YES
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material3.Divider
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface

View file

@ -1,44 +1,47 @@
package com.pixelized.rplexicon.ui.screens.character.pages.alteration
package com.pixelized.rplexicon.ui.screens.character.composable.dialogs
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 android.net.Uri
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
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.PaddingValues
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CutCornerShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
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
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.composable.BackgroundImage
import com.pixelized.rplexicon.ui.theme.LexiconTheme
import com.pixelized.rplexicon.utilitary.annotateWithDropCap
import com.pixelized.rplexicon.utilitary.extentions.lexicon
import com.pixelized.rplexicon.utilitary.extentions.modifier.ddBorder
@Stable
data class AlterationDetailUio(
data class AlterationDialogDetailUio(
val icon: Uri?,
val name: String,
val original: String?,
val source: String,
@ -46,71 +49,81 @@ data class AlterationDetailUio(
val description: String,
)
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun AlterationDetail(
fun AlterationDetailDialog(
paddingValues: PaddingValues = PaddingValues(horizontal = 16.dp, vertical = 128.dp),
dialog: State<AlterationDialogDetailUio?>,
onDismissRequest: () -> Unit,
) {
dialog.value?.let {
Dialog(
properties = remember { DialogProperties(usePlatformDefaultWidth = false) },
onDismissRequest = onDismissRequest,
) {
Box(
modifier = Modifier
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = null,
onClick = onDismissRequest,
)
.padding(paddingValues = paddingValues),
) {
AlterationDetailDialogContent(
modifier = Modifier
.clickable(
enabled = false,
onClick = { },
)
.ddBorder(
inner = remember { RoundedCornerShape(size = 8.dp) },
outline = remember { CutCornerShape(size = 16.dp) },
),
detail = it,
)
}
}
}
}
@Composable
private fun AlterationDetailDialogContent(
modifier: Modifier = Modifier,
detail: AlterationDetailUio,
onClose: () -> Unit,
scrollState: ScrollState = rememberScrollState(),
paddingValues: PaddingValues = PaddingValues(horizontal = 24.dp, vertical = 16.dp),
detail: AlterationDialogDetailUio,
) {
Surface(
modifier = Modifier
.padding(all = 16.dp)
.ddBorder(
inner = remember { RoundedCornerShape(size = 8.dp) },
outline = remember { CutCornerShape(size = 16.dp) },
)
.then(other = modifier),
modifier = modifier,
) {
Column {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(start = 24.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.Top,
) {
FlowRow(
modifier = Modifier.padding(top = 16.dp),
horizontalArrangement = Arrangement.spacedBy(4.dp),
) {
Text(
modifier = Modifier.alignByBaseline(),
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.Bold,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
text = annotateWithDropCap(
text = detail.name,
style = MaterialTheme.lexicon.typography.dropCap.titleMedium,
),
)
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,
contentDescription = null
)
}
Box {
detail.icon?.let { uri ->
BackgroundImage(
modifier = Modifier
.size(size = 144.dp)
.align(alignment = Alignment.TopEnd),
model = uri,
)
}
Column(
modifier = Modifier
.padding(top = 8.dp)
.verticalScroll(rememberScrollState())
.padding(horizontal = 24.dp),
.verticalScroll(state = scrollState)
.padding(paddingValues = paddingValues),
) {
Text(
style = MaterialTheme.lexicon.typography.base.headlineSmall,
text = annotateWithDropCap(
text = detail.name,
style = MaterialTheme.lexicon.typography.dropCap.headlineSmall,
),
)
Text(
modifier = Modifier.offset(y = -(4.dp)),
style = MaterialTheme.typography.bodyMedium,
fontStyle = FontStyle.Italic,
fontWeight = FontWeight.Light,
text = detail.original ?: "",
)
Text(
fontWeight = FontWeight.Light,
style = MaterialTheme.typography.labelSmall,
@ -122,7 +135,7 @@ fun AlterationDetail(
text = stringResource(id = R.string.alteration_target, detail.target),
)
Text(
modifier = Modifier.padding(top = 8.dp, bottom = 24.dp),
modifier = Modifier.padding(top = 12.dp),
style = MaterialTheme.typography.bodyMedium,
text = detail.description,
)
@ -136,8 +149,9 @@ fun AlterationDetail(
@Preview(uiMode = UI_MODE_NIGHT_YES)
private fun AlterationDetailPreview() {
LexiconTheme {
AlterationDetail(
detail = AlterationDetailUio(
AlterationDetailDialogContent(
detail = AlterationDialogDetailUio(
icon = null,
name = "Rage",
original = "Rage",
source = "Barbare",
@ -154,7 +168,6 @@ private fun AlterationDetailPreview() {
"Condition \"\"Rage inhibée\"\" :\n" +
"Votre rage vous demande des efforts de concentration ou se dissipe. Vous devez réussir un jet de sauvegarde de constitution dont la difficulté augmente à chaque tour (DC 6 + 2 par tour) sous peine de voir votre rage cesser. Sous certaines conditions, la difficulté pourra augmenter ou baisser au-delà des valeurs indiquées.\""
),
onClose = { },
)
}
}

View file

@ -0,0 +1,147 @@
package com.pixelized.rplexicon.ui.screens.character.composable.dialogs
import android.content.res.Configuration
import android.net.Uri
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CutCornerShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import com.pixelized.rplexicon.ui.composable.BackgroundImage
import com.pixelized.rplexicon.ui.theme.LexiconTheme
import com.pixelized.rplexicon.utilitary.annotateWithDropCap
import com.pixelized.rplexicon.utilitary.extentions.lexicon
import com.pixelized.rplexicon.utilitary.extentions.modifier.ddBorder
import com.pixelized.rplexicon.utilitary.extentions.string.skillIcon
@Stable
data class SkillDialogDetailUio(
val icon: Uri?,
val name: String,
val original: String?,
val description: String,
)
@Composable
fun SkillDetailDialog(
dialog: State<SkillDialogDetailUio?>,
paddingValues: PaddingValues = PaddingValues(horizontal = 16.dp, vertical = 128.dp),
onDismissRequest: () -> Unit,
) {
dialog.value?.let {
Dialog(
properties = remember { DialogProperties(usePlatformDefaultWidth = false) },
onDismissRequest = onDismissRequest,
) {
Box(
modifier = Modifier
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = null,
onClick = onDismissRequest,
)
.padding(paddingValues = paddingValues),
) {
SkillDetailDialogContent(
modifier = Modifier
.clickable(
enabled = false,
onClick = { },
)
.ddBorder(
inner = remember { RoundedCornerShape(size = 8.dp) },
outline = remember { CutCornerShape(size = 16.dp) },
),
detail = it,
)
}
}
}
}
@Composable
fun SkillDetailDialogContent(
modifier: Modifier = Modifier,
scrollState: ScrollState = rememberScrollState(),
paddingValues: PaddingValues = PaddingValues(vertical = 16.dp, horizontal = 24.dp),
detail: SkillDialogDetailUio,
) {
Surface(
modifier = modifier,
) {
Box {
detail.icon?.let { uri ->
BackgroundImage(
modifier = Modifier
.size(size = 144.dp)
.align(alignment = Alignment.TopEnd),
model = uri,
)
}
Column(
modifier = Modifier
.verticalScroll(state = scrollState)
.padding(paddingValues = paddingValues),
) {
Text(
style = MaterialTheme.lexicon.typography.base.headlineSmall,
text = annotateWithDropCap(
text = detail.name,
style = MaterialTheme.lexicon.typography.dropCap.headlineSmall,
),
)
Text(
modifier = Modifier.offset(y = -(4.dp)),
style = MaterialTheme.typography.bodyMedium,
fontStyle = FontStyle.Italic,
fontWeight = FontWeight.Light,
text = detail.original ?: "",
)
Text(
modifier = Modifier.padding(top = 12.dp),
style = MaterialTheme.typography.bodyMedium,
text = detail.description,
)
}
}
}
}
@Composable
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun AlterationDetailPreview() {
LexiconTheme {
SkillDetailDialogContent(
detail = SkillDialogDetailUio(
icon = "Endurance implacable".skillIcon(),
name = "Endurance implacable",
original = "Relentless Endurance",
description = "Lorsque vous tombez à 0 point de vie, mais que vous n'êtes pas tué sur le coup, vous pouvez passer à 1 point de vie à la place. Vous devez terminer un repos long avant de pouvoir utiliser cette capacité de nouveau."
),
)
}
}

View file

@ -0,0 +1,271 @@
package com.pixelized.rplexicon.ui.screens.character.composable.dialogs
import android.content.res.Configuration
import android.net.Uri
import androidx.annotation.StringRes
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CutCornerShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.composable.BackgroundImage
import com.pixelized.rplexicon.ui.theme.LexiconTheme
import com.pixelized.rplexicon.utilitary.annotateWithDropCap
import com.pixelized.rplexicon.utilitary.extentions.lexicon
import com.pixelized.rplexicon.utilitary.extentions.modifier.ddBorder
@Stable
data class SpellDialogDetailUio(
val icon: Uri?,
val name: String,
val translated: String,
val level: String,
@StringRes val school: Int,
val castingTime: String,
val range: String,
val requirement: String,
val duration: String,
val description: String,
val ritual: Boolean,
)
@Composable
fun SpellDetailDialog(
dialog: State<SpellDialogDetailUio?>,
paddingValues: PaddingValues = PaddingValues(horizontal = 16.dp, vertical = 128.dp),
onDismissRequest: () -> Unit,
) {
dialog.value?.let {
Dialog(
properties = remember { DialogProperties(usePlatformDefaultWidth = false) },
onDismissRequest = onDismissRequest,
) {
Box(
modifier = Modifier
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = null,
onClick = onDismissRequest,
)
.padding(paddingValues = paddingValues),
) {
SpellDetailDialogContent(
modifier = Modifier
.clickable(
enabled = false,
onClick = { },
)
.ddBorder(
inner = remember { RoundedCornerShape(size = 8.dp) },
outline = remember { CutCornerShape(size = 16.dp) },
),
detail = it,
)
}
}
}
}
@OptIn(ExperimentalLayoutApi::class)
@Composable
private fun SpellDetailDialogContent(
modifier: Modifier = Modifier,
scrollState: ScrollState = rememberScrollState(),
paddingValues: PaddingValues = PaddingValues(vertical = 16.dp, horizontal = 24.dp),
detail: SpellDialogDetailUio,
) {
val typography = MaterialTheme.lexicon.typography
Surface(
modifier = modifier,
) {
Box {
detail.icon?.let { uri ->
BackgroundImage(
modifier = Modifier
.size(size = 144.dp)
.align(alignment = Alignment.TopEnd),
model = uri,
)
}
Column(
modifier = Modifier
.verticalScroll(state = scrollState)
.padding(paddingValues = paddingValues),
) {
Text(
style = typography.base.headlineSmall,
text = annotateWithDropCap(
text = detail.name,
style = typography.dropCap.headlineSmall,
),
)
Text(
modifier = Modifier.offset(y = -(4.dp)),
style = MaterialTheme.typography.bodyMedium,
fontStyle = FontStyle.Italic,
fontWeight = FontWeight.Light,
text = detail.translated,
)
FlowRow(
modifier = Modifier.padding(top = 12.dp),
horizontalArrangement = Arrangement.spacedBy(space = 4.dp),
) {
Text(
modifier = Modifier.alignByBaseline(),
style = MaterialTheme.typography.bodyMedium,
fontWeight = FontWeight.Bold,
text = stringResource(id = R.string.spell_detail_school),
)
Text(
modifier = Modifier.alignByBaseline(),
style = MaterialTheme.typography.bodyMedium,
text = stringResource(id = detail.school),
)
if (detail.ritual) {
Text(
modifier = Modifier.alignByBaseline(),
style = MaterialTheme.typography.bodyMedium,
text = "-",
)
Text(
modifier = Modifier.alignByBaseline(),
style = MaterialTheme.typography.bodyMedium,
text = stringResource(id = R.string.spell_detail_ritual),
)
}
}
FlowRow(
horizontalArrangement = Arrangement.spacedBy(space = 4.dp),
) {
Text(
style = MaterialTheme.typography.bodyMedium,
fontWeight = FontWeight.Bold,
text = stringResource(id = R.string.spell_detail_level),
)
Text(
style = MaterialTheme.typography.bodyMedium,
text = detail.level,
)
}
FlowRow(
horizontalArrangement = Arrangement.spacedBy(space = 4.dp),
) {
Text(
style = MaterialTheme.typography.bodyMedium,
fontWeight = FontWeight.Bold,
text = stringResource(id = R.string.spell_detail_casting_time),
)
Text(
style = MaterialTheme.typography.bodyMedium,
text = detail.castingTime,
)
}
FlowRow(
horizontalArrangement = Arrangement.spacedBy(space = 4.dp),
) {
Text(
style = MaterialTheme.typography.bodyMedium,
fontWeight = FontWeight.Bold,
text = stringResource(id = R.string.spell_detail_range),
)
Text(
style = MaterialTheme.typography.bodyMedium,
text = detail.range,
)
}
FlowRow(
horizontalArrangement = Arrangement.spacedBy(space = 4.dp),
) {
Text(
style = MaterialTheme.typography.bodyMedium,
fontWeight = FontWeight.Bold,
text = stringResource(id = R.string.spell_detail_components),
)
Text(
style = MaterialTheme.typography.bodyMedium,
text = detail.requirement,
)
}
FlowRow(
horizontalArrangement = Arrangement.spacedBy(space = 4.dp),
) {
Text(
style = MaterialTheme.typography.bodyMedium,
fontWeight = FontWeight.Bold,
text = stringResource(id = R.string.spell_detail_duration),
)
Text(
style = MaterialTheme.typography.bodyMedium,
text = detail.duration,
)
}
Text(
modifier = Modifier.padding(top = 16.dp),
style = MaterialTheme.typography.bodyMedium,
fontWeight = FontWeight.Bold,
text = stringResource(id = R.string.spell_detail_description),
)
Text(
style = MaterialTheme.typography.bodyMedium,
text = detail.description,
)
}
}
}
}
@Composable
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun SpellDetailDialogPreview() {
LexiconTheme {
SpellDetailDialogContent(
detail = SpellDialogDetailUio(
icon = null,
name = "Représailles infernales",
translated = "Hellish Rebuke",
level = "1",
school = R.string.spell_school_evocation,
castingTime = "1 réaction, que vous prenez après avoir subi des dégâts par une créature située à 18 mètres maximum de vous et que vous pouvez voir.",
range = "18 mètres",
requirement = "V, S",
duration = "Instantanée",
description = "Vous pointez votre doigt, et la créature qui vous a infligé des dégâts est momentanément entourée de flammes infernales. La créature doit effectuer un jet de sauvegarde de Dextérité, subissant 2d10 dégâts de fue en cas d'échecn ou la moitié de ces dégâts en cas de réussite.\n\nAux niveaux supérieurs. Lorsque vous lancez ce sort en utilisant un emplacement de sort de niveau 2 ou supérieur, les dégâts sont augmentés de 1d10 pour chaque niveau d'emplacement au-dela du niveau 1.",
ritual = false,
),
)
}
}

View file

@ -6,7 +6,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.screens.character.composable.actions.SpellLevelUio
import com.pixelized.rplexicon.ui.screens.character.pages.chooser.SpellChooserUio
import com.pixelized.rplexicon.ui.screens.character.composable.chooser.SpellChooserUio
@Composable
@Stable

View file

@ -30,7 +30,7 @@ class CharacterSheetHeaderUioFactory @Inject constructor(
),
armorClass = LabelPointUio(
label = R.string.character_sheet_title_ca,
value = sheetHeaderData?.ca?.let { "$it" } ?: " ",
value = sheetHeaderData?.ca?.let { "$it" } ?: "?",
max = null,
),
hitPoint = LabelPointUio(
@ -80,7 +80,7 @@ class CharacterSheetHeaderUioFactory @Inject constructor(
}
else -> when {
fireHeaderData?.wildShapeHp == null -> sheetHeaderData?.hpMax?.let { "$it" } ?: "?"
fireHeaderData.wildShapeHp == null -> sheetHeaderData?.hpMax?.let { "$it" } ?: "?"
fireHeaderData.extraHp == 0 -> "${fireHeaderData.wildShapeHp}"
else -> "${fireHeaderData.wildShapeHp}+${fireHeaderData.extraHp}"
}

View file

@ -27,8 +27,6 @@ import com.pixelized.rplexicon.LocalRollOverlay
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.composable.edit.HandleSkillEditDialog
import com.pixelized.rplexicon.ui.composable.edit.HandleSpellEditDialog
import com.pixelized.rplexicon.ui.navigation.LocalScreenNavHost
import com.pixelized.rplexicon.ui.navigation.screens.navigateToSpellDetail
import com.pixelized.rplexicon.ui.screens.character.composable.actions.Attack
import com.pixelized.rplexicon.ui.screens.character.composable.actions.AttackUio
import com.pixelized.rplexicon.ui.screens.character.composable.actions.GenericHeader
@ -44,6 +42,8 @@ import com.pixelized.rplexicon.ui.screens.character.composable.actions.rememberS
import com.pixelized.rplexicon.ui.screens.character.composable.preview.rememberAttackListStatePreview
import com.pixelized.rplexicon.ui.screens.character.composable.preview.rememberObjectListStatePreview
import com.pixelized.rplexicon.ui.screens.character.composable.preview.rememberSpellListStatePreview
import com.pixelized.rplexicon.ui.screens.character.composable.dialogs.SkillDetailDialog
import com.pixelized.rplexicon.ui.screens.character.composable.dialogs.SpellDetailDialog
import com.pixelized.rplexicon.ui.theme.LexiconTheme
import kotlinx.coroutines.launch
@ -56,7 +56,6 @@ fun ActionPage(
spellsViewModel: SpellsViewModel = hiltViewModel(),
skillViewModel: SkillsViewModel = hiltViewModel(),
) {
val screen = LocalScreenNavHost.current
val overlay = LocalRollOverlay.current
val scope = rememberCoroutineScope()
@ -83,7 +82,7 @@ fun ActionPage(
}
},
onObject = {
skillViewModel.showSkillDetailDialog(item = it.name)
objectsViewModel.showObjectDetailDialog(item = it.name)
},
onUseObject = {
objectsViewModel.onUse(it.name)?.let { throws ->
@ -103,13 +102,17 @@ fun ActionPage(
}
},
onSkillInfo = {
skillViewModel.showSkillDetailDialog(item = it.label)
if (spellsViewModel.isSpell(name = it.label)) {
spellsViewModel.showSpellDetailDialog(item = it.label)
} else {
skillViewModel.showSkillDetailDialog(item = it.label)
}
},
onSpellLevel = { level: Int, value: Int, max: Int ->
spellsViewModel.showSpellEditDialog(level = level, value = value, max = max)
},
onSpell = { spell ->
screen.navigateToSpellDetail(spell = spell)
spellsViewModel.showSpellDetailDialog(item = spell)
},
onSpellHit = { id ->
scope.launch {
@ -142,16 +145,26 @@ fun ActionPage(
onConfirm = spellsViewModel::applySpellChange
)
SpellDetailDialog(
dialog = spellsViewModel.spellDetailDialog,
onDismissRequest = spellsViewModel::hideSpellDetailDialog
)
HandleSkillEditDialog(
dialog = skillViewModel.skillEditDialog,
onDismissRequest = skillViewModel::hideSkillEditDialog,
onConfirm = skillViewModel::applySkillChange
)
HandleSkillDetailDialog(
SkillDetailDialog(
dialog = skillViewModel.skillDetailDialog,
onDismissRequest = skillViewModel::hideSkillDetailDialog
)
SkillDetailDialog(
dialog = objectsViewModel.objectDetailDialog,
onDismissRequest = objectsViewModel::hideObjectDetailDialog
)
}
@OptIn(ExperimentalFoundationApi::class)

View file

@ -10,6 +10,7 @@ import com.pixelized.rplexicon.data.repository.character.DescriptionRepository
import com.pixelized.rplexicon.data.repository.character.ObjectActionRepository
import com.pixelized.rplexicon.ui.navigation.screens.characterSheetArgument
import com.pixelized.rplexicon.ui.screens.character.composable.actions.ObjectItemUio
import com.pixelized.rplexicon.ui.screens.character.composable.dialogs.SkillDialogDetailUio
import com.pixelized.rplexicon.utilitary.extentions.string.objectIcon
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
@ -28,8 +29,8 @@ class ObjectsViewModel @Inject constructor(
private val _objects = mutableStateOf<List<ObjectItemUio>>(emptyList())
val objects: State<List<ObjectItemUio>> get() = _objects
private val _dialog = mutableStateOf<SkillDetailUio?>(null)
val dialog: State<SkillDetailUio?> get() = _dialog
private val _objectDetailDialog = mutableStateOf<SkillDialogDetailUio?>(null)
val objectDetailDialog: State<SkillDialogDetailUio?> get() = _objectDetailDialog
init {
viewModelScope.launch(Dispatchers.Default) {
@ -60,4 +61,21 @@ class ObjectsViewModel @Inject constructor(
)
}
}
fun showObjectDetailDialog(item: String) {
val description = descriptionRepository.getDescription(name = item)
val item = objectsRepository.find(character = character, item = item)
if (item != null && description != null) {
_objectDetailDialog.value = SkillDialogDetailUio(
icon = item.icon ?: item.name.objectIcon(),
name = item.name,
original = description.original,
description = description.description
)
}
}
fun hideObjectDetailDialog() {
_objectDetailDialog.value = null
}
}

View file

@ -1,161 +0,0 @@
package com.pixelized.rplexicon.ui.screens.character.pages.actions
import android.content.res.Configuration
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
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CutCornerShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
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
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import com.pixelized.rplexicon.ui.theme.LexiconTheme
import com.pixelized.rplexicon.utilitary.extentions.lexicon
import com.pixelized.rplexicon.utilitary.extentions.modifier.ddBorder
@Stable
data class SkillDetailUio(
val name: String,
val original: String?,
val description: String,
)
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun SkillDetail(
modifier: Modifier = Modifier,
detail: SkillDetailUio,
onClose: () -> Unit,
) {
Surface(
modifier = Modifier
.padding(all = 16.dp)
.ddBorder(
inner = remember { RoundedCornerShape(size = 8.dp) },
outline = remember { CutCornerShape(size = 16.dp) },
)
.then(other = modifier),
) {
Column {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(start = 24.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.Top,
) {
FlowRow(
modifier = Modifier
.padding(top = 16.dp)
.weight(weight = 1f),
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.dropCap.titleMedium,
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,
contentDescription = null
)
}
}
Column(
modifier = Modifier
.padding(top = 8.dp)
.verticalScroll(rememberScrollState())
.padding(horizontal = 24.dp),
) {
Text(
modifier = Modifier.padding(top = 8.dp, bottom = 24.dp),
style = MaterialTheme.typography.bodyMedium,
text = detail.description,
)
}
}
}
}
@Composable
fun HandleSkillDetailDialog(
dialog: State<SkillDetailUio?>,
onDismissRequest: () -> Unit,
) {
dialog.value?.let {
Dialog(
properties = remember { DialogProperties(usePlatformDefaultWidth = false) },
onDismissRequest = onDismissRequest,
) {
SkillDetail(
detail = it,
onClose = onDismissRequest,
)
}
}
}
@Composable
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun AlterationDetailPreview() {
LexiconTheme {
SkillDetail(
detail = SkillDetailUio(
name = "Endurance implacable",
original = "Relentless Endurance",
description = "Lorsque vous tombez à 0 point de vie, mais que vous n'êtes pas tué sur le coup, vous pouvez passer à 1 point de vie à la place. Vous devez terminer un repos long avant de pouvoir utiliser cette capacité de nouveau."
),
onClose = { },
)
}
}

View file

@ -18,6 +18,8 @@ import com.pixelized.rplexicon.ui.composable.edit.SkillEditDialogUio
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
@ -41,8 +43,8 @@ class SkillsViewModel @Inject constructor(
private val _skillEditDialog = mutableStateOf<SkillEditDialogUio?>(null)
val skillEditDialog: State<SkillEditDialogUio?> get() = _skillEditDialog
private val _skillDetailDialog = mutableStateOf<SkillDetailUio?>(null)
val skillDetailDialog: State<SkillDetailUio?> get() = _skillDetailDialog
private val _skillDetailDialog = mutableStateOf<SkillDialogDetailUio?>(null)
val skillDetailDialog: State<SkillDialogDetailUio?> get() = _skillDetailDialog
private val _skills = mutableStateOf<List<SkillItemUio>>(emptyList())
val skills: State<List<SkillItemUio>> get() = _skills
@ -81,15 +83,16 @@ class SkillsViewModel @Inject constructor(
)
fun showSkillDetailDialog(item: String) {
_skillDetailDialog.value = descriptionRepository
.getDescription(name = item)
?.let { description ->
SkillDetailUio(
name = item,
original = description.original,
description = description.description
)
}
val description = descriptionRepository.getDescription(name = item)
val skill = skillRepository.find(character = character, skill = item)
if (skill != null && description != null) {
_skillDetailDialog.value = SkillDialogDetailUio(
icon = skill.icon ?: skill.name.skillIcon(),
name = skill.name,
original = description.original,
description = description.description
)
}
}
fun hideSkillDetailDialog() {

View file

@ -16,19 +16,23 @@ 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.character.CharacterSheetRepository
import com.pixelized.rplexicon.data.repository.character.DescriptionRepository
import com.pixelized.rplexicon.data.repository.character.SpellRepository
import com.pixelized.rplexicon.ui.composable.edit.SpellEditDialogUio
import com.pixelized.rplexicon.ui.navigation.screens.characterSheetArgument
import com.pixelized.rplexicon.ui.screens.character.composable.actions.SpellHeaderUio
import com.pixelized.rplexicon.ui.screens.character.composable.actions.SpellLevelUio
import com.pixelized.rplexicon.ui.screens.character.composable.actions.SpellUio
import com.pixelized.rplexicon.ui.screens.character.pages.chooser.SpellChooserUio
import com.pixelized.rplexicon.ui.screens.character.composable.dialogs.SpellDialogDetailUio
import com.pixelized.rplexicon.ui.screens.character.composable.chooser.SpellChooserUio
import com.pixelized.rplexicon.utilitary.extentions.icon
import com.pixelized.rplexicon.utilitary.extentions.local.firstSpellSlot
import com.pixelized.rplexicon.utilitary.extentions.local.highestSpellLevel
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
@ -44,6 +48,7 @@ class SpellsViewModel @Inject constructor(
private val characterRepository: CharacterSheetRepository,
private val firebaseRepository: FirebaseRepository,
private val spellRepository: SpellRepository,
private val descriptionRepository: DescriptionRepository,
application: Application,
spellBookUseCase: SpellBookUseCase,
savedStateHandle: SavedStateHandle,
@ -52,6 +57,9 @@ class SpellsViewModel @Inject constructor(
private var characterFire: CharacterSheetFire? = null
private val characterName = savedStateHandle.characterSheetArgument.name
private val _spellDetailDialog = mutableStateOf<SpellDialogDetailUio?>(null)
val spellDetailDialog: State<SpellDialogDetailUio?> get() = _spellDetailDialog
private val _editDialog = mutableStateOf<SpellEditDialogUio?>(null)
val spellEditDialog: State<SpellEditDialogUio?> get() = _editDialog
@ -171,6 +179,34 @@ class SpellsViewModel @Inject constructor(
hideSpellEditDialog()
}
fun isSpell(name: String): Boolean {
return spellRepository.findSpell(name = name) != null
}
fun showSpellDetailDialog(item: String) {
val description = descriptionRepository.getDescription(name = item)
val spell = spellRepository.findSpell(name = item)
if (spell != null && description != null) {
_spellDetailDialog.value = SpellDialogDetailUio(
icon = spell.icon ?: spell.name.spellIcon(),
name = spell.name,
translated = description.original,
level = "${spell.level}",
school = spell.school.label,
castingTime = spell.castingTime,
range = spell.range,
requirement = spell.requirement,
duration = spell.duration,
description = description.description,
ritual = spell.ritual,
)
}
}
fun hideSpellDetailDialog() {
_spellDetailDialog.value = null
}
/**
* Helper method to build a readable String from a Throw.
* Create a string following the format "amount 'd' faces '+' modifiers + amount * level 'd' faces"

View file

@ -20,7 +20,7 @@ import androidx.hilt.navigation.compose.hiltViewModel
import com.pixelized.rplexicon.ui.composable.CategoryHeader
import com.pixelized.rplexicon.ui.screens.character.composable.actions.AlterationItem
import com.pixelized.rplexicon.ui.screens.character.composable.actions.AlterationItemUio
import com.pixelized.rplexicon.ui.screens.character.composable.alteration.AlterationDetailHandler
import com.pixelized.rplexicon.ui.screens.character.composable.dialogs.AlterationDetailDialog
import com.pixelized.rplexicon.ui.screens.rolls.preview.rememberRollAlterations
import com.pixelized.rplexicon.ui.theme.LexiconTheme
import kotlinx.coroutines.launch
@ -52,9 +52,9 @@ fun AlterationPage(
},
)
AlterationDetailHandler(
detail = viewModel.alterationDetail,
onDismissRequest = { viewModel.hideAlterationDetail() },
AlterationDetailDialog(
dialog = viewModel.alterationDetailDialog,
onDismissRequest = viewModel::hideAlterationDetail,
)
}

View file

@ -19,8 +19,10 @@ import com.pixelized.rplexicon.data.repository.character.AlterationRepository
import com.pixelized.rplexicon.data.repository.character.CharacterSheetRepository
import com.pixelized.rplexicon.data.repository.character.DescriptionRepository
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
@ -45,8 +47,8 @@ class AlterationViewModel @Inject constructor(
private val _alterations = mutableStateOf<List<AlterationGroupUio>>(emptyList())
val alterations: State<List<AlterationGroupUio>> get() = _alterations
private val _alterationDetail = mutableStateOf<AlterationDetailUio?>(null)
val alterationDetail: State<AlterationDetailUio?> get() = _alterationDetail
private val _alterationDetailDialog = mutableStateOf<AlterationDialogDetailUio?>(null)
val alterationDetailDialog: State<AlterationDialogDetailUio?> get() = _alterationDetailDialog
init {
viewModelScope.launch {
@ -114,7 +116,8 @@ class AlterationViewModel @Inject constructor(
val description = descriptionRepository.getDescription(
name = alteration.name,
)
_alterationDetail.value = AlterationDetailUio(
_alterationDetailDialog.value = AlterationDialogDetailUio(
icon = alteration.icon ?: alteration.name.alterationIcon(),
name = id,
original = description?.original,
source = alteration.source,
@ -126,7 +129,7 @@ class AlterationViewModel @Inject constructor(
}
fun hideAlterationDetail() {
_alterationDetail.value = null
_alterationDetailDialog.value = null
}
private data class AlterationStruct(

View file

@ -27,7 +27,7 @@ import com.pixelized.rplexicon.ui.screens.character.composable.actions.Inventory
import com.pixelized.rplexicon.ui.screens.character.composable.actions.InventoryItemUio
import com.pixelized.rplexicon.ui.screens.character.composable.preview.rememberEquipmentState
import com.pixelized.rplexicon.ui.screens.character.composable.preview.rememberInventoryListState
import com.pixelized.rplexicon.ui.screens.character.pages.actions.HandleSkillDetailDialog
import com.pixelized.rplexicon.ui.screens.character.composable.dialogs.SkillDetailDialog
import com.pixelized.rplexicon.ui.theme.LexiconTheme
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
@ -50,7 +50,7 @@ fun InventoryPage(
},
)
HandleSkillDetailDialog(
SkillDetailDialog(
dialog = viewModel.dialog,
onDismissRequest = viewModel::hideSkillDetailDialog,
)

View file

@ -14,8 +14,9 @@ import com.pixelized.rplexicon.ui.navigation.screens.characterSheetArgument
import com.pixelized.rplexicon.ui.screens.character.composable.actions.EquipmentItemUio
import com.pixelized.rplexicon.ui.screens.character.composable.actions.InventoryItemUio
import com.pixelized.rplexicon.ui.screens.character.factory.ItemUioFactory
import com.pixelized.rplexicon.ui.screens.character.pages.actions.SkillDetailUio
import com.pixelized.rplexicon.ui.screens.character.composable.dialogs.SkillDialogDetailUio
import com.pixelized.rplexicon.utilitary.extentions.context
import com.pixelized.rplexicon.utilitary.extentions.string.equipmentsIcon
import com.pixelized.rplexicon.utilitary.extentions.uri
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
@ -42,8 +43,8 @@ class InventoryViewModel @Inject constructor(
private val _inventory = mutableStateOf<List<InventoryItemUio>>(emptyList())
val inventory: State<List<InventoryItemUio>> get() = _inventory
private val _dialog = mutableStateOf<SkillDetailUio?>(null)
val dialog: State<SkillDetailUio?> get() = _dialog
private val _dialog = mutableStateOf<SkillDialogDetailUio?>(null)
val dialog: State<SkillDialogDetailUio?> get() = _dialog
private val _snack = MutableSharedFlow<String?>()
val snack: SharedFlow<String?> get() = _snack
@ -86,12 +87,12 @@ class InventoryViewModel @Inject constructor(
}
}
}
fun showSkillDetailDialog(item: String) {
val description = descriptionRepository.getDescription(name = item)
if (description != null) {
_dialog.value = SkillDetailUio(
_dialog.value = SkillDialogDetailUio(
icon = item.equipmentsIcon(),
name = item,
original = description.original,
description = description.description

View file

@ -53,7 +53,7 @@ import com.pixelized.rplexicon.ui.screens.character.composable.character.Profici
import com.pixelized.rplexicon.ui.screens.character.composable.character.Stat
import com.pixelized.rplexicon.ui.screens.character.composable.character.StatUio
import com.pixelized.rplexicon.ui.screens.character.composable.preview.rememberCharacterSheetStatePreview
import com.pixelized.rplexicon.ui.screens.character.pages.actions.HandleSkillDetailDialog
import com.pixelized.rplexicon.ui.screens.character.composable.dialogs.SkillDetailDialog
import com.pixelized.rplexicon.ui.theme.LexiconTheme
import com.pixelized.rplexicon.utilitary.extentions.modifier.ddBorder
import kotlinx.coroutines.launch
@ -113,7 +113,7 @@ fun ProficiencyPage(
onConfirm = viewModel::applySkillChange
)
HandleSkillDetailDialog(
SkillDetailDialog(
dialog = viewModel.skillDetailDialog,
onDismissRequest = viewModel::hideSkillDetailDialog
)

View file

@ -24,8 +24,9 @@ import com.pixelized.rplexicon.ui.screens.character.composable.character.Profici
import com.pixelized.rplexicon.ui.screens.character.composable.character.StatUio
import com.pixelized.rplexicon.ui.screens.character.factory.CharacterSheetUioFactory
import com.pixelized.rplexicon.ui.screens.character.factory.SkillFactoryUioFactory
import com.pixelized.rplexicon.ui.screens.character.pages.actions.SkillDetailUio
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
@ -53,8 +54,8 @@ class ProficiencyViewModel @Inject constructor(
private val _skillEditDialog = mutableStateOf<SkillEditDialogUio?>(null)
val skillEditDialog: State<SkillEditDialogUio?> get() = _skillEditDialog
private val _skillDetailDialog = mutableStateOf<SkillDetailUio?>(null)
val skillDetailDialog: State<SkillDetailUio?> get() = _skillDetailDialog
private val _skillDetailDialog = mutableStateOf<SkillDialogDetailUio?>(null)
val skillDetailDialog: State<SkillDialogDetailUio?> get() = _skillDetailDialog
private val _skills = mutableStateOf<List<SkillItemUio>>(emptyList())
val skills: State<List<SkillItemUio>> get() = _skills
@ -154,15 +155,16 @@ class ProficiencyViewModel @Inject constructor(
)
fun showSkillDetailDialog(item: String) {
_skillDetailDialog.value = descriptionRepository
.getDescription(name = item)
?.let { description ->
SkillDetailUio(
name = item,
original = description.original,
description = description.description
)
}
val description = descriptionRepository.getDescription(name = item)
val skill = skillRepository.find(character = character, skill = item)
if (skill != null && description != null) {
_skillDetailDialog.value = SkillDialogDetailUio(
icon = skill.icon ?: skill.name.skillIcon(),
name = skill.name,
original = description.original,
description = description.description
)
}
}
fun hideSkillDetailDialog() {

View file

@ -66,8 +66,6 @@ import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import androidx.hilt.navigation.compose.hiltViewModel
import com.pixelized.rplexicon.LocalRollOverlay
import com.pixelized.rplexicon.NO_WINDOW_INSETS
@ -77,9 +75,8 @@ import com.pixelized.rplexicon.ui.composable.BlurredOverlayHostState
import com.pixelized.rplexicon.ui.composable.CategoryHeader
import com.pixelized.rplexicon.ui.composable.ModalNavigationDrawer
import com.pixelized.rplexicon.ui.screens.character.composable.actions.AlterationItem
import com.pixelized.rplexicon.ui.screens.character.pages.alteration.AlterationDetail
import com.pixelized.rplexicon.ui.screens.character.pages.alteration.AlterationDetailUio
import com.pixelized.rplexicon.ui.screens.character.pages.alteration.AlterationGroupUio
import com.pixelized.rplexicon.ui.screens.character.composable.dialogs.AlterationDetailDialog
import com.pixelized.rplexicon.ui.screens.rolls.composable.RollDice
import com.pixelized.rplexicon.ui.screens.rolls.composable.RollDiceUio
import com.pixelized.rplexicon.ui.screens.rolls.composable.ThrowsCard
@ -136,9 +133,9 @@ fun RollOverlay(
onBack = { scope.launch { drawer.close() } },
)
AlterationDetailHandler(
detail = viewModel.alterationDetail,
onDismissRequest = { viewModel.hideAlterationDetail() },
AlterationDetailDialog(
dialog = viewModel.alterationDetailDialog,
onDismissRequest = viewModel::hideAlterationDetail,
)
}
@ -344,24 +341,6 @@ private fun Modifier.detailPaddingBottom(
this.then(other = Modifier.padding(bottom = padding))
}
@Composable
private fun AlterationDetailHandler(
detail: State<AlterationDetailUio?>,
onDismissRequest: () -> Unit,
) {
detail.value?.let {
Dialog(
properties = remember { DialogProperties(usePlatformDefaultWidth = false) },
onDismissRequest = onDismissRequest,
) {
AlterationDetail(
detail = it,
onClose = onDismissRequest,
)
}
}
}
@Composable
@Preview(uiMode = UI_MODE_NIGHT_NO)
@Preview(uiMode = UI_MODE_NIGHT_YES)

View file

@ -20,14 +20,15 @@ import com.pixelized.rplexicon.data.repository.character.AlterationRepository
import com.pixelized.rplexicon.data.repository.character.CharacterSheetRepository
import com.pixelized.rplexicon.data.repository.character.DescriptionRepository
import com.pixelized.rplexicon.ui.screens.character.composable.actions.AlterationItemUio
import com.pixelized.rplexicon.ui.screens.character.pages.alteration.AlterationDetailUio
import com.pixelized.rplexicon.ui.screens.character.pages.alteration.AlterationGroupUio
import com.pixelized.rplexicon.ui.screens.character.composable.dialogs.AlterationDialogDetailUio
import com.pixelized.rplexicon.ui.screens.rolls.composable.RollDiceUio
import com.pixelized.rplexicon.ui.screens.rolls.composable.ThrowCardFactory
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
@ -85,8 +86,8 @@ class RollOverlayViewModel @Inject constructor(
private val _isThrowHidden = mutableStateOf(false)
val isThrowHidden: State<Boolean> get() = _isThrowHidden
private val _alterationDetail = mutableStateOf<AlterationDetailUio?>(null)
val alterationDetail: State<AlterationDetailUio?> get() = _alterationDetail
private val _alterationDetailDialog = mutableStateOf<AlterationDialogDetailUio?>(null)
val alterationDetailDialog: State<AlterationDialogDetailUio?> get() = _alterationDetailDialog
suspend fun prepareRoll(diceThrow: DiceThrow) {
this.diceThrow = diceThrow
@ -190,7 +191,8 @@ class RollOverlayViewModel @Inject constructor(
name = alteration?.name,
)
if (alteration != null) {
_alterationDetail.value = AlterationDetailUio(
_alterationDetailDialog.value = AlterationDialogDetailUio(
icon = alteration.icon ?: alteration.name.alterationIcon(),
name = id,
original = description?.original,
source = alteration.source,
@ -202,7 +204,7 @@ class RollOverlayViewModel @Inject constructor(
}
fun hideAlterationDetail() {
_alterationDetail.value = null
_alterationDetailDialog.value = null
}
private data class AlterationStruct(

View file

@ -44,7 +44,7 @@ import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.composable.KeepOnScreen
import com.pixelized.rplexicon.ui.navigation.LocalScreenNavHost
import com.pixelized.rplexicon.ui.navigation.screens.navigateToCharacterSheet
import com.pixelized.rplexicon.ui.screens.character.composable.alteration.AlterationDetailHandler
import com.pixelized.rplexicon.ui.screens.character.composable.dialogs.AlterationDetailDialog
import com.pixelized.rplexicon.ui.screens.rolls.composable.ThrowsCard
import com.pixelized.rplexicon.ui.screens.rolls.composable.ThrowsCardUio
import com.pixelized.rplexicon.ui.screens.summary.pages.statistic.StatisticSummary
@ -124,8 +124,8 @@ fun SummaryScreen(
onDetail = { statisticsViewModel.hideDetail() },
)
AlterationDetailHandler(
detail = statisticsViewModel.alterationDetail,
AlterationDetailDialog(
dialog = statisticsViewModel.alterationDetailDialog,
onDismissRequest = { statisticsViewModel.hideAlterationDetail() },
)
}

View file

@ -9,11 +9,12 @@ import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.data.repository.authentication.FirebaseRepository
import com.pixelized.rplexicon.data.repository.character.AlterationRepository
import com.pixelized.rplexicon.data.repository.character.DescriptionRepository
import com.pixelized.rplexicon.ui.screens.character.pages.alteration.AlterationDetailUio
import com.pixelized.rplexicon.ui.screens.character.composable.dialogs.AlterationDialogDetailUio
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
@ -38,8 +39,8 @@ class StatisticViewModel @Inject constructor(
private val _detail = mutableStateOf<ThrowsCardUio?>(null)
val detail: State<ThrowsCardUio?> get() = _detail
private val _alterationDetail = mutableStateOf<AlterationDetailUio?>(null)
val alterationDetail: State<AlterationDetailUio?> get() = _alterationDetail
private val _alterationDetailDialog = mutableStateOf<AlterationDialogDetailUio?>(null)
val alterationDetailDialog: State<AlterationDialogDetailUio?> get() = _alterationDetailDialog
fun showDetail(dice: ClassHeaderSummaryUio.Dice) {
detailJob?.cancel()
@ -70,7 +71,8 @@ class StatisticViewModel @Inject constructor(
val description = descriptionRepository.getDescription(name = alteration?.name)
if (alteration != null) {
_alterationDetail.value = AlterationDetailUio(
_alterationDetailDialog.value = AlterationDialogDetailUio(
icon = alteration.icon ?: alteration.name.alterationIcon(),
name = name,
original = description?.original,
source = alteration.source,
@ -82,6 +84,6 @@ class StatisticViewModel @Inject constructor(
}
fun hideAlterationDetail() {
_alterationDetail.value = null
_alterationDetailDialog.value = null
}
}

View file

@ -12,6 +12,10 @@ fun String.objectIcon(): Uri? {
return ResourcesCache.objects[this]?.uri?.toUriOrNull()
}
fun String.equipmentsIcon(): Uri? {
return ResourcesCache.equipments[this]?.uri?.toUriOrNull()
}
fun String.alterationIcon(): Uri? {
return ResourcesCache.alterations[this]?.uri?.toUriOrNull()
}
@ -112,6 +116,15 @@ private object ResourcesCache {
"Baies nourricières" to R.drawable.icbg_food_goodberry_unfaded,
)
val equipments = hashMapOf(
"Dague" to R.drawable.icbg_dagger_unfaded,
"Hache de guerre en argent" to R.drawable.icbg_battleaxe_plus_one_unfaded,
"Bouclier" to R.drawable.icbg_studded_shield_unfaded,
"Cape de protection" to R.drawable.icbg_cloak_of_protection_unfaded,
"Armure d'écailles" to R.drawable.icbg_scale_mail_unfaded,
"Armure de cuir" to R.drawable.icbg_leather_armour_rogue_unfaded,
)
val skills = hashMapOf(
"Dé de vie (Barbare)" to R.drawable.icbg_bolstering_magic_boon,
"Dé de vie (Guerrier)" to R.drawable.icbg_bolstering_magic_boon,