Add client inventory sheet management.
This commit is contained in:
parent
8982bab22d
commit
05a376aea8
10 changed files with 325 additions and 118 deletions
|
|
@ -184,6 +184,8 @@
|
||||||
<string name="character_sheet__delete_dialog__title">Supprimer la feuille de personnage</string>
|
<string name="character_sheet__delete_dialog__title">Supprimer la feuille de personnage</string>
|
||||||
<string name="character_sheet__delete_dialog__description">Êtes-vous sûr de vouloir supprimer "%1$s" ?</string>
|
<string name="character_sheet__delete_dialog__description">Êtes-vous sûr de vouloir supprimer "%1$s" ?</string>
|
||||||
|
|
||||||
|
<string name="character__inventory__add_to_inventory__action">Ajouter un objet</string>
|
||||||
|
<string name="character__inventory__filter_inventory__label">Filtrer l'inventaire</string>
|
||||||
<string name="character__inventory__add_to_purse__title">Ajouter à la bourse</string>
|
<string name="character__inventory__add_to_purse__title">Ajouter à la bourse</string>
|
||||||
<string name="character__inventory__remove_from_purse__title">Retirer de la bourse</string>
|
<string name="character__inventory__remove_from_purse__title">Retirer de la bourse</string>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,26 @@
|
||||||
package com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.inventory
|
package com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.inventory
|
||||||
|
|
||||||
import androidx.compose.animation.AnimatedContent
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.AnimatedContentTransitionScope
|
|
||||||
import androidx.compose.animation.ContentTransform
|
|
||||||
import androidx.compose.animation.SizeTransform
|
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
import androidx.compose.animation.slideInVertically
|
|
||||||
import androidx.compose.animation.slideOutVertically
|
|
||||||
import androidx.compose.animation.togetherWith
|
|
||||||
import androidx.compose.foundation.Image
|
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.clickable
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
import androidx.compose.material.Button
|
||||||
|
import androidx.compose.material.ButtonDefaults
|
||||||
|
import androidx.compose.material.Icon
|
||||||
|
import androidx.compose.material.IconButton
|
||||||
import androidx.compose.material.MaterialTheme
|
import androidx.compose.material.MaterialTheme
|
||||||
import androidx.compose.material.Text
|
import androidx.compose.material.Text
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Add
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
import androidx.compose.runtime.State
|
import androidx.compose.runtime.State
|
||||||
|
|
@ -31,45 +28,57 @@ import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.pixelized.desktop.lwa.LocalBlurController
|
import com.pixelized.desktop.lwa.LocalBlurController
|
||||||
import com.pixelized.desktop.lwa.ui.composable.character.purse.PurseDialog
|
import com.pixelized.desktop.lwa.ui.composable.character.purse.PurseDialog
|
||||||
import com.pixelized.desktop.lwa.ui.composable.character.purse.PurseDialogViewModel
|
import com.pixelized.desktop.lwa.ui.composable.character.purse.PurseDialogViewModel
|
||||||
import com.pixelized.desktop.lwa.ui.composable.error.ErrorSnackHandler
|
import com.pixelized.desktop.lwa.ui.composable.error.ErrorSnackHandler
|
||||||
|
import com.pixelized.desktop.lwa.ui.composable.textfield.LwaTextField
|
||||||
|
import com.pixelized.desktop.lwa.ui.composable.textfield.LwaTextFieldUio
|
||||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.inventory.item.InventoryItem
|
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.inventory.item.InventoryItem
|
||||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.inventory.item.InventoryItemUio
|
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.inventory.item.InventoryItemUio
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.inventory.item.InventoryPurse
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.inventory.item.PurseUio
|
||||||
|
import com.pixelized.desktop.lwa.ui.theme.color.component.LwaButtonColors
|
||||||
|
import com.pixelized.desktop.lwa.ui.theme.color.component.LwaTextFieldColors
|
||||||
import com.pixelized.desktop.lwa.ui.theme.lwa
|
import com.pixelized.desktop.lwa.ui.theme.lwa
|
||||||
|
import com.pixelized.desktop.lwa.utils.extention.plus
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import lwacharactersheet.composeapp.generated.resources.Res
|
import lwacharactersheet.composeapp.generated.resources.Res
|
||||||
import lwacharactersheet.composeapp.generated.resources.ic_copper_32px
|
import lwacharactersheet.composeapp.generated.resources.character__inventory__add_to_inventory__action
|
||||||
import lwacharactersheet.composeapp.generated.resources.ic_gold_32px
|
import lwacharactersheet.composeapp.generated.resources.ic_cancel_24dp
|
||||||
import lwacharactersheet.composeapp.generated.resources.ic_silver_32px
|
|
||||||
import org.jetbrains.compose.resources.painterResource
|
import org.jetbrains.compose.resources.painterResource
|
||||||
|
import org.jetbrains.compose.resources.stringResource
|
||||||
import org.koin.compose.viewmodel.koinViewModel
|
import org.koin.compose.viewmodel.koinViewModel
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
data class CharacterDetailInventoryUio(
|
data class CharacterDetailInventoryUio(
|
||||||
val characterSheetId: String,
|
val characterSheetId: String,
|
||||||
|
val filter: LwaTextFieldUio,
|
||||||
val purse: PurseUio,
|
val purse: PurseUio,
|
||||||
val items: List<InventoryItemUio>,
|
val items: List<InventoryItemUio>,
|
||||||
) {
|
)
|
||||||
@Stable
|
|
||||||
data class PurseUio(
|
|
||||||
val gold: Int,
|
|
||||||
val silver: Int,
|
|
||||||
val copper: Int,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@Stable
|
||||||
object CharacterDetailInventoryDefault {
|
object CharacterDetailInventoryDefault {
|
||||||
val padding = PaddingValues(horizontal = 16.dp, vertical = 8.dp)
|
@Stable
|
||||||
|
val padding = PaddingValues(
|
||||||
|
start = 16.dp,
|
||||||
|
end = 16.dp,
|
||||||
|
top = 8.dp,
|
||||||
|
bottom = 8.dp,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
val spacing: Dp = 8.dp
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CharacterDetailInventory(
|
fun CharacterDetailInventory(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
paddings: PaddingValues = CharacterDetailInventoryDefault.padding,
|
paddings: PaddingValues = CharacterDetailInventoryDefault.padding,
|
||||||
|
spacing: Dp = CharacterDetailInventoryDefault.spacing,
|
||||||
purseViewModel: PurseDialogViewModel = koinViewModel(),
|
purseViewModel: PurseDialogViewModel = koinViewModel(),
|
||||||
inventory: State<CharacterDetailInventoryUio?>,
|
inventory: State<CharacterDetailInventoryUio?>,
|
||||||
) {
|
) {
|
||||||
|
|
@ -82,6 +91,7 @@ fun CharacterDetailInventory(
|
||||||
else -> CharacterDetailInventoryContent(
|
else -> CharacterDetailInventoryContent(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
paddings = paddings,
|
paddings = paddings,
|
||||||
|
spacing = spacing,
|
||||||
inventory = unWrap,
|
inventory = unWrap,
|
||||||
onPurse = {
|
onPurse = {
|
||||||
blur.show()
|
blur.show()
|
||||||
|
|
@ -118,119 +128,95 @@ fun CharacterDetailInventory(
|
||||||
private fun CharacterDetailInventoryContent(
|
private fun CharacterDetailInventoryContent(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
paddings: PaddingValues,
|
paddings: PaddingValues,
|
||||||
|
spacing: Dp,
|
||||||
inventory: CharacterDetailInventoryUio,
|
inventory: CharacterDetailInventoryUio,
|
||||||
onPurse: (String) -> Unit,
|
onPurse: (String) -> Unit,
|
||||||
) {
|
) {
|
||||||
Column(
|
Box(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
) {
|
) {
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.matchParentSize(),
|
||||||
contentPadding = paddings,
|
contentPadding = paddings + PaddingValues(bottom = 56.dp),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(space = spacing),
|
||||||
) {
|
) {
|
||||||
|
item(
|
||||||
|
key = "purse",
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.animateItem()
|
||||||
|
.fillMaxWidth(),
|
||||||
|
verticalAlignment = Alignment.Bottom,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(space = spacing),
|
||||||
|
) {
|
||||||
|
InventoryPurse(
|
||||||
|
modifier = Modifier.weight(weight = 1f),
|
||||||
|
purse = inventory.purse,
|
||||||
|
onPurse = { onPurse(inventory.characterSheetId) },
|
||||||
|
)
|
||||||
|
LwaTextField(
|
||||||
|
modifier = Modifier.weight(weight = 1f),
|
||||||
|
colors = LwaTextFieldColors(
|
||||||
|
backgroundColor = MaterialTheme.lwa.colorScheme.elevated.base2dp,
|
||||||
|
),
|
||||||
|
field = inventory.filter,
|
||||||
|
trailingIcon = {
|
||||||
|
val value = inventory.filter.valueFlow.collectAsState()
|
||||||
|
AnimatedVisibility(
|
||||||
|
visible = value.value.isNotBlank(),
|
||||||
|
enter = fadeIn(),
|
||||||
|
exit = fadeOut(),
|
||||||
|
) {
|
||||||
|
IconButton(
|
||||||
|
onClick = { inventory.filter.onValueChange.invoke("") },
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(Res.drawable.ic_cancel_24dp),
|
||||||
|
tint = MaterialTheme.lwa.colorScheme.base.primary,
|
||||||
|
contentDescription = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
items(
|
items(
|
||||||
items = inventory.items,
|
items = inventory.items,
|
||||||
key = { it.inventoryId },
|
key = { it.inventoryId },
|
||||||
) {
|
) {
|
||||||
InventoryItem(
|
InventoryItem(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier
|
||||||
|
.animateItem()
|
||||||
|
.fillMaxWidth(),
|
||||||
item = it,
|
item = it,
|
||||||
onClick = { },
|
onClick = { },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Purse(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
paddings = paddings,
|
|
||||||
purse = inventory.purse,
|
|
||||||
onPurse = { onPurse(inventory.characterSheetId) },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
private fun Purse(
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
paddings: PaddingValues,
|
|
||||||
purse: CharacterDetailInventoryUio.PurseUio,
|
|
||||||
onPurse: () -> Unit,
|
|
||||||
) {
|
|
||||||
Row(
|
|
||||||
modifier = Modifier
|
|
||||||
.background(color = MaterialTheme.lwa.colorScheme.elevated.base2dp)
|
|
||||||
.then(other = modifier),
|
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
|
||||||
horizontalArrangement = Arrangement.End,
|
|
||||||
) {
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.clickable { onPurse() }
|
.align(alignment = Alignment.BottomEnd)
|
||||||
.padding(paddingValues = paddings),
|
.padding(paddingValues = paddings),
|
||||||
horizontalArrangement = Arrangement.spacedBy(space = 8.dp),
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
) {
|
) {
|
||||||
Row(verticalAlignment = Alignment.Bottom) {
|
Button(
|
||||||
Image(
|
colors = LwaButtonColors(),
|
||||||
painter = painterResource(Res.drawable.ic_gold_32px),
|
elevation = ButtonDefaults.elevation(4.dp),
|
||||||
modifier = Modifier.padding(bottom = 2.dp).size(size = 16.dp),
|
shape = CircleShape,
|
||||||
|
onClick = { },
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
modifier = Modifier.padding(end = 4.dp),
|
||||||
|
text = stringResource(Res.string.character__inventory__add_to_inventory__action),
|
||||||
|
)
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Add,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
)
|
)
|
||||||
AnimatedContent(
|
|
||||||
targetState = purse.gold,
|
|
||||||
transitionSpec = coinTransitionSpec(),
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
style = MaterialTheme.lwa.typography.base.body1,
|
|
||||||
fontWeight = FontWeight.Bold,
|
|
||||||
text = "$it",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Row(verticalAlignment = Alignment.Bottom) {
|
|
||||||
Image(
|
|
||||||
painter = painterResource(Res.drawable.ic_silver_32px),
|
|
||||||
modifier = Modifier.padding(bottom = 2.dp).size(size = 16.dp),
|
|
||||||
contentDescription = null,
|
|
||||||
)
|
|
||||||
AnimatedContent(
|
|
||||||
targetState = purse.silver,
|
|
||||||
transitionSpec = coinTransitionSpec(),
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
style = MaterialTheme.lwa.typography.base.body1,
|
|
||||||
fontWeight = FontWeight.Bold,
|
|
||||||
text = "$it",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Row(verticalAlignment = Alignment.Bottom) {
|
|
||||||
Image(
|
|
||||||
painter = painterResource(Res.drawable.ic_copper_32px),
|
|
||||||
modifier = Modifier.padding(bottom = 2.dp).size(size = 16.dp),
|
|
||||||
contentDescription = null,
|
|
||||||
)
|
|
||||||
AnimatedContent(
|
|
||||||
targetState = purse.copper,
|
|
||||||
transitionSpec = coinTransitionSpec(),
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
style = MaterialTheme.lwa.typography.base.body1,
|
|
||||||
fontWeight = FontWeight.Bold,
|
|
||||||
text = "$it",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
@Stable
|
|
||||||
private fun coinTransitionSpec(): AnimatedContentTransitionScope<Int>.() -> ContentTransform = {
|
|
||||||
val enter = fadeIn() + slideInVertically { -16 }
|
|
||||||
val exit = fadeOut() + slideOutVertically { 16 }
|
|
||||||
enter togetherWith exit using SizeTransform(clip = false)
|
|
||||||
}
|
}
|
||||||
|
|
@ -2,35 +2,55 @@ package com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.inventory
|
||||||
|
|
||||||
import com.pixelized.desktop.lwa.repository.inventory.InventoryRepository
|
import com.pixelized.desktop.lwa.repository.inventory.InventoryRepository
|
||||||
import com.pixelized.desktop.lwa.repository.item.ItemRepository
|
import com.pixelized.desktop.lwa.repository.item.ItemRepository
|
||||||
|
import com.pixelized.desktop.lwa.ui.composable.textfield.LwaTextFieldUio
|
||||||
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.inventory.item.InventoryItemUio
|
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.inventory.item.InventoryItemUio
|
||||||
|
import com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.inventory.item.PurseUio
|
||||||
|
import com.pixelized.desktop.lwa.utils.extention.unAccent
|
||||||
import com.pixelized.shared.lwa.model.inventory.Inventory
|
import com.pixelized.shared.lwa.model.inventory.Inventory
|
||||||
import com.pixelized.shared.lwa.model.item.Item
|
import com.pixelized.shared.lwa.model.item.Item
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.Res
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.character__inventory__filter_inventory__label
|
||||||
|
import org.jetbrains.compose.resources.getString
|
||||||
|
import java.text.Collator
|
||||||
|
|
||||||
class CharacterDetailInventoryFactory(
|
class CharacterDetailInventoryFactory(
|
||||||
private val inventoryRepository: InventoryRepository,
|
private val inventoryRepository: InventoryRepository,
|
||||||
private val itemRepository: ItemRepository,
|
private val itemRepository: ItemRepository,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
fun convertToCharacterInventoryUioFlow(
|
suspend fun convertToCharacterInventoryUioFlow(
|
||||||
characterSheetId: String,
|
characterSheetId: String,
|
||||||
scope: CoroutineScope,
|
scope: CoroutineScope,
|
||||||
started: SharingStarted = SharingStarted.Eagerly,
|
started: SharingStarted = SharingStarted.Eagerly,
|
||||||
initialValue: () -> CharacterDetailInventoryUio?,
|
initialValue: () -> CharacterDetailInventoryUio?,
|
||||||
): StateFlow<CharacterDetailInventoryUio?> {
|
): StateFlow<CharacterDetailInventoryUio?> {
|
||||||
|
val filterFlow = MutableStateFlow("")
|
||||||
|
val filterField = LwaTextFieldUio(
|
||||||
|
enable = true,
|
||||||
|
isError = MutableStateFlow(false),
|
||||||
|
valueFlow = filterFlow,
|
||||||
|
label = getString(Res.string.character__inventory__filter_inventory__label),
|
||||||
|
placeHolder = null,
|
||||||
|
onValueChange = { filterFlow.value = it },
|
||||||
|
)
|
||||||
return combine(
|
return combine(
|
||||||
inventoryRepository.inventoryFlow(characterSheetId = characterSheetId),
|
inventoryRepository.inventoryFlow(characterSheetId = characterSheetId),
|
||||||
itemRepository.itemFlow,
|
itemRepository.itemFlow,
|
||||||
) { inventory, items ->
|
filterFlow.map { it.unAccent() },
|
||||||
|
) { inventory, items, filter ->
|
||||||
convertToCharacterInventoryUio(
|
convertToCharacterInventoryUio(
|
||||||
characterSheetId = characterSheetId,
|
characterSheetId = characterSheetId,
|
||||||
|
filter = filterField,
|
||||||
purse = inventory?.purse,
|
purse = inventory?.purse,
|
||||||
inventory = inventory?.items,
|
inventory = inventory?.items,
|
||||||
items = items,
|
items = items.filterValues { it.metadata.name.unAccent().contains(filter, true) },
|
||||||
)
|
)
|
||||||
}.stateIn(
|
}.stateIn(
|
||||||
scope = scope,
|
scope = scope,
|
||||||
|
|
@ -41,6 +61,7 @@ class CharacterDetailInventoryFactory(
|
||||||
|
|
||||||
private suspend fun convertToCharacterInventoryUio(
|
private suspend fun convertToCharacterInventoryUio(
|
||||||
characterSheetId: String?,
|
characterSheetId: String?,
|
||||||
|
filter: LwaTextFieldUio,
|
||||||
purse: Inventory.Purse?,
|
purse: Inventory.Purse?,
|
||||||
inventory: List<Inventory.Item>?,
|
inventory: List<Inventory.Item>?,
|
||||||
items: Map<String, Item>,
|
items: Map<String, Item>,
|
||||||
|
|
@ -49,19 +70,22 @@ class CharacterDetailInventoryFactory(
|
||||||
|
|
||||||
return CharacterDetailInventoryUio(
|
return CharacterDetailInventoryUio(
|
||||||
characterSheetId = characterSheetId,
|
characterSheetId = characterSheetId,
|
||||||
purse = CharacterDetailInventoryUio.PurseUio(
|
purse = PurseUio(
|
||||||
gold = purse?.gold ?: 0,
|
gold = purse?.gold ?: 0,
|
||||||
silver = purse?.silver ?: 0,
|
silver = purse?.silver ?: 0,
|
||||||
copper = purse?.copper ?: 0,
|
copper = purse?.copper ?: 0,
|
||||||
),
|
),
|
||||||
|
filter = filter,
|
||||||
items = inventory
|
items = inventory
|
||||||
?.mapNotNull {
|
?.mapNotNull {
|
||||||
val label = items[it.itemId]?.metadata?.name ?: return@mapNotNull null
|
val label = items[it.itemId]?.metadata?.name ?: return@mapNotNull null
|
||||||
InventoryItemUio(
|
InventoryItemUio(
|
||||||
inventoryId = it.inventoryId,
|
inventoryId = it.inventoryId,
|
||||||
label = label,
|
label = label,
|
||||||
|
equipped = it.equipped,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
?.sortedWith(compareBy(Collator.getInstance()) { it.label })
|
||||||
?: emptyList()
|
?: emptyList()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,13 +12,16 @@ import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.pixelized.desktop.lwa.ui.theme.lwa
|
import com.pixelized.desktop.lwa.ui.theme.lwa
|
||||||
|
import com.pixelized.desktop.lwa.utils.extention.ribbon
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
data class InventoryItemUio(
|
data class InventoryItemUio(
|
||||||
val inventoryId: String,
|
val inventoryId: String,
|
||||||
val label: String,
|
val label: String,
|
||||||
|
val equipped: Boolean,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Stable
|
@Stable
|
||||||
|
|
@ -36,9 +39,15 @@ fun InventoryItem(
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.clip(shape = MaterialTheme.lwa.shapes.gameMaster)
|
.clip(shape = MaterialTheme.lwa.shapes.item)
|
||||||
.clickable(onClick = onClick)
|
.clickable(onClick = onClick)
|
||||||
.background(color = MaterialTheme.lwa.colorScheme.elevated.base2dp)
|
.background(color = MaterialTheme.lwa.colorScheme.elevated.base2dp)
|
||||||
|
.ribbon(
|
||||||
|
color = when (item.equipped) {
|
||||||
|
true -> MaterialTheme.lwa.colorScheme.base.primary
|
||||||
|
else -> Color.Transparent
|
||||||
|
}
|
||||||
|
)
|
||||||
.minimumInteractiveComponentSize()
|
.minimumInteractiveComponentSize()
|
||||||
.padding(paddingValues = padding)
|
.padding(paddingValues = padding)
|
||||||
.then(other = modifier),
|
.then(other = modifier),
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,130 @@
|
||||||
|
package com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.inventory.item
|
||||||
|
|
||||||
|
import androidx.compose.animation.AnimatedContent
|
||||||
|
import androidx.compose.animation.AnimatedContentTransitionScope
|
||||||
|
import androidx.compose.animation.ContentTransform
|
||||||
|
import androidx.compose.animation.SizeTransform
|
||||||
|
import androidx.compose.animation.fadeIn
|
||||||
|
import androidx.compose.animation.fadeOut
|
||||||
|
import androidx.compose.animation.slideInHorizontally
|
||||||
|
import androidx.compose.animation.slideOutHorizontally
|
||||||
|
import androidx.compose.animation.togetherWith
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.material.MaterialTheme
|
||||||
|
import androidx.compose.material.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.Stable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.pixelized.desktop.lwa.ui.theme.lwa
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.Res
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.ic_copper_32px
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.ic_gold_32px
|
||||||
|
import lwacharactersheet.composeapp.generated.resources.ic_silver_32px
|
||||||
|
import org.jetbrains.compose.resources.painterResource
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
data class PurseUio(
|
||||||
|
val gold: Int,
|
||||||
|
val silver: Int,
|
||||||
|
val copper: Int,
|
||||||
|
)
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
object InventoryPurseDefault {
|
||||||
|
@Stable
|
||||||
|
val paddings = PaddingValues(horizontal = 0.dp, vertical = 4.dp)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun InventoryPurse(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
paddings: PaddingValues = InventoryPurseDefault.paddings,
|
||||||
|
purse: PurseUio,
|
||||||
|
onPurse: () -> Unit,
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = modifier,
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.Start,
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.clip(shape = MaterialTheme.lwa.shapes.item)
|
||||||
|
.clickable { onPurse() }
|
||||||
|
.padding(paddingValues = paddings),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(space = 8.dp),
|
||||||
|
) {
|
||||||
|
Row(verticalAlignment = Alignment.Bottom) {
|
||||||
|
Image(
|
||||||
|
painter = painterResource(Res.drawable.ic_gold_32px),
|
||||||
|
modifier = Modifier.padding(bottom = 2.dp).size(size = 16.dp),
|
||||||
|
contentDescription = null,
|
||||||
|
)
|
||||||
|
AnimatedContent(
|
||||||
|
targetState = purse.gold,
|
||||||
|
transitionSpec = coinTransitionSpec(),
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
style = MaterialTheme.lwa.typography.base.body1,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
text = "$it",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row(verticalAlignment = Alignment.Bottom) {
|
||||||
|
Image(
|
||||||
|
painter = painterResource(Res.drawable.ic_silver_32px),
|
||||||
|
modifier = Modifier.padding(bottom = 2.dp).size(size = 16.dp),
|
||||||
|
contentDescription = null,
|
||||||
|
)
|
||||||
|
AnimatedContent(
|
||||||
|
targetState = purse.silver,
|
||||||
|
transitionSpec = coinTransitionSpec(),
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
style = MaterialTheme.lwa.typography.base.body1,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
text = "$it",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row(verticalAlignment = Alignment.Bottom) {
|
||||||
|
Image(
|
||||||
|
painter = painterResource(Res.drawable.ic_copper_32px),
|
||||||
|
modifier = Modifier.padding(bottom = 2.dp).size(size = 16.dp),
|
||||||
|
contentDescription = null,
|
||||||
|
)
|
||||||
|
AnimatedContent(
|
||||||
|
targetState = purse.copper,
|
||||||
|
transitionSpec = coinTransitionSpec(),
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
style = MaterialTheme.lwa.typography.base.body1,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
text = "$it",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
@Stable
|
||||||
|
private fun coinTransitionSpec(): AnimatedContentTransitionScope<Int>.() -> ContentTransform = {
|
||||||
|
val enter = fadeIn() + slideInHorizontally { -16 }
|
||||||
|
val exit = fadeOut() + slideOutHorizontally { 16 }
|
||||||
|
enter togetherWith exit using SizeTransform(clip = false)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
package com.pixelized.desktop.lwa.utils.extention
|
||||||
|
|
||||||
|
import androidx.compose.animation.animateColorAsState
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.composed
|
||||||
|
import androidx.compose.ui.draw.drawWithContent
|
||||||
|
import androidx.compose.ui.geometry.Size
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.unit.Dp
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
|
fun Modifier.ribbon(
|
||||||
|
width: Dp = 4.dp,
|
||||||
|
color: Color,
|
||||||
|
): Modifier = composed {
|
||||||
|
val animatedColor = animateColorAsState(
|
||||||
|
targetValue = color,
|
||||||
|
)
|
||||||
|
return@composed drawWithContent {
|
||||||
|
drawContent()
|
||||||
|
drawRect(
|
||||||
|
color = animatedColor.value,
|
||||||
|
size = Size(
|
||||||
|
width = width.toPx(),
|
||||||
|
height = size.height,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
package com.pixelized.desktop.lwa.utils.extention
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
|
import androidx.compose.foundation.layout.calculateEndPadding
|
||||||
|
import androidx.compose.foundation.layout.calculateStartPadding
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.Stable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
@Composable
|
||||||
|
operator fun PaddingValues.plus(other: PaddingValues): PaddingValues {
|
||||||
|
val direction = LocalLayoutDirection.current
|
||||||
|
return remember(this, other, direction) {
|
||||||
|
PaddingValues(
|
||||||
|
start = calculateStartPadding(direction) + other.calculateStartPadding(direction),
|
||||||
|
top = calculateTopPadding() + other.calculateTopPadding(),
|
||||||
|
end = calculateEndPadding(direction) + other.calculateEndPadding(direction),
|
||||||
|
bottom = calculateBottomPadding() + other.calculateBottomPadding(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -15,6 +15,7 @@ data class Inventory(
|
||||||
val inventoryId: String,
|
val inventoryId: String,
|
||||||
val itemId: String,
|
val itemId: String,
|
||||||
val count: Int,
|
val count: Int,
|
||||||
|
val equipped: Boolean,
|
||||||
)
|
)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
||||||
|
|
@ -21,5 +21,6 @@ data class InventoryJsonV1(
|
||||||
val inventoryId: String,
|
val inventoryId: String,
|
||||||
val itemId: String,
|
val itemId: String,
|
||||||
val count: Int,
|
val count: Int,
|
||||||
|
val equipped: Boolean?,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -18,6 +18,7 @@ class InventoryJsonFactoryV1 {
|
||||||
inventoryId = it.inventoryId,
|
inventoryId = it.inventoryId,
|
||||||
itemId = it.itemId,
|
itemId = it.itemId,
|
||||||
count = it.count,
|
count = it.count,
|
||||||
|
equipped = it.equipped ?: false,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
@ -36,6 +37,7 @@ class InventoryJsonFactoryV1 {
|
||||||
inventoryId = it.inventoryId,
|
inventoryId = it.inventoryId,
|
||||||
itemId = it.itemId,
|
itemId = it.itemId,
|
||||||
count = it.count,
|
count = it.count,
|
||||||
|
equipped = it.equipped,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue