Add AddItem dialog to the inventory screen.
This commit is contained in:
		
							parent
							
								
									c94c820efb
								
							
						
					
					
						commit
						48074f3d13
					
				
					 17 changed files with 704 additions and 84 deletions
				
			
		| 
						 | 
					@ -188,6 +188,9 @@
 | 
				
			||||||
    <string name="character__inventory__filter_inventory__label">Filtrer l'inventaire</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>
 | 
				
			||||||
 | 
					    <string name="character__inventory__filter_item_inventory__label">Filtrer les objets</string>
 | 
				
			||||||
 | 
					    <string name="character__inventory__inventory__dialog__title">Ajouter à l'inventaire</string>
 | 
				
			||||||
 | 
					    <string name="character__inventory__description_empty__label">Cet objet n'a pas de description.</string>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <string name="tooltip__characteristics__characteristics">Les caractéristiques constituent les aptitudes innées d’un personnage comme son intelligence, sa force, son charisme, etc. Elles ne sont pas acquises, mais peuvent être parfois augmentées par un entraînement ou une utilisation réussie. Les caractéristiques des humains normaux varient de 2 (niveau extrêmement bas) à 20 (maximum du potentiel humain), avec une moyenne de 10 ou 11. Plus une caractéristique est élevée plus le personnage est puissant dans cette aptitude.\nÀ la création de votre personnage, répartissez les valeurs suivantes dans les différentes caractéristiques : 15, 15, 13, 11, 10, 9 et 7.</string>
 | 
					    <string name="tooltip__characteristics__characteristics">Les caractéristiques constituent les aptitudes innées d’un personnage comme son intelligence, sa force, son charisme, etc. Elles ne sont pas acquises, mais peuvent être parfois augmentées par un entraînement ou une utilisation réussie. Les caractéristiques des humains normaux varient de 2 (niveau extrêmement bas) à 20 (maximum du potentiel humain), avec une moyenne de 10 ou 11. Plus une caractéristique est élevée plus le personnage est puissant dans cette aptitude.\nÀ la création de votre personnage, répartissez les valeurs suivantes dans les différentes caractéristiques : 15, 15, 13, 11, 10, 9 et 7.</string>
 | 
				
			||||||
    <string name="tooltip__characteristics__strength">La Force représente essentiellement la puissance musculaire du personnage. Elle ne décrit pas nécessairement la masse musculaire brute, mais l’efficacité avec laquelle le personnage exerce ses muscles pour accomplir des actions physiques pénibles.\n\n- Bonus aux dégats\n- Réflexe\n- Athlétisme\n- Lancer\n- Saisie</string>
 | 
					    <string name="tooltip__characteristics__strength">La Force représente essentiellement la puissance musculaire du personnage. Elle ne décrit pas nécessairement la masse musculaire brute, mais l’efficacité avec laquelle le personnage exerce ses muscles pour accomplir des actions physiques pénibles.\n\n- Bonus aux dégats\n- Réflexe\n- Athlétisme\n- Lancer\n- Saisie</string>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,7 +5,7 @@ import com.pixelized.shared.lwa.model.item.Item
 | 
				
			||||||
class ItemRepository(
 | 
					class ItemRepository(
 | 
				
			||||||
    private val itemStore: ItemStore,
 | 
					    private val itemStore: ItemStore,
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
    val itemFlow get() = itemStore.items
 | 
					    fun itemFlow() = itemStore.items
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    suspend fun updateItemFlow() {
 | 
					    suspend fun updateItemFlow() {
 | 
				
			||||||
        itemStore.updateItemFlow()
 | 
					        itemStore.updateItemFlow()
 | 
				
			||||||
| 
						 | 
					@ -14,7 +14,7 @@ class ItemRepository(
 | 
				
			||||||
    fun item(
 | 
					    fun item(
 | 
				
			||||||
        itemId: String?,
 | 
					        itemId: String?,
 | 
				
			||||||
    ): Item? {
 | 
					    ): Item? {
 | 
				
			||||||
        return itemFlow.value[itemId]
 | 
					        return itemStore.items.value[itemId]
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Throws
 | 
					    @Throws
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,8 +1,308 @@
 | 
				
			||||||
package com.pixelized.desktop.lwa.ui.composable.character.inventory
 | 
					package com.pixelized.desktop.lwa.ui.composable.character.inventory
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import androidx.compose.animation.AnimatedVisibility
 | 
				
			||||||
 | 
					import androidx.compose.animation.fadeIn
 | 
				
			||||||
 | 
					import androidx.compose.animation.fadeOut
 | 
				
			||||||
 | 
					import androidx.compose.foundation.ExperimentalFoundationApi
 | 
				
			||||||
 | 
					import androidx.compose.foundation.background
 | 
				
			||||||
 | 
					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.PaddingValues
 | 
				
			||||||
 | 
					import androidx.compose.foundation.layout.Row
 | 
				
			||||||
 | 
					import androidx.compose.foundation.layout.calculateEndPadding
 | 
				
			||||||
 | 
					import androidx.compose.foundation.layout.calculateStartPadding
 | 
				
			||||||
 | 
					import androidx.compose.foundation.layout.fillMaxSize
 | 
				
			||||||
 | 
					import androidx.compose.foundation.layout.fillMaxWidth
 | 
				
			||||||
 | 
					import androidx.compose.foundation.layout.offset
 | 
				
			||||||
 | 
					import androidx.compose.foundation.layout.padding
 | 
				
			||||||
 | 
					import androidx.compose.foundation.layout.size
 | 
				
			||||||
 | 
					import androidx.compose.foundation.lazy.LazyColumn
 | 
				
			||||||
 | 
					import androidx.compose.foundation.lazy.items
 | 
				
			||||||
 | 
					import androidx.compose.material.Icon
 | 
				
			||||||
 | 
					import androidx.compose.material.IconButton
 | 
				
			||||||
 | 
					import androidx.compose.material.MaterialTheme
 | 
				
			||||||
 | 
					import androidx.compose.material.Surface
 | 
				
			||||||
 | 
					import androidx.compose.material.Text
 | 
				
			||||||
 | 
					import androidx.compose.material.minimumInteractiveComponentSize
 | 
				
			||||||
import androidx.compose.runtime.Composable
 | 
					import androidx.compose.runtime.Composable
 | 
				
			||||||
 | 
					import androidx.compose.runtime.Stable
 | 
				
			||||||
 | 
					import androidx.compose.runtime.State
 | 
				
			||||||
 | 
					import androidx.compose.runtime.collectAsState
 | 
				
			||||||
 | 
					import androidx.compose.runtime.remember
 | 
				
			||||||
 | 
					import androidx.compose.ui.Alignment
 | 
				
			||||||
 | 
					import androidx.compose.ui.Modifier
 | 
				
			||||||
 | 
					import androidx.compose.ui.draw.clip
 | 
				
			||||||
 | 
					import androidx.compose.ui.graphics.Color
 | 
				
			||||||
 | 
					import androidx.compose.ui.graphics.FilterQuality
 | 
				
			||||||
 | 
					import androidx.compose.ui.layout.ContentScale
 | 
				
			||||||
 | 
					import androidx.compose.ui.platform.LocalLayoutDirection
 | 
				
			||||||
 | 
					import androidx.compose.ui.text.font.FontWeight
 | 
				
			||||||
 | 
					import androidx.compose.ui.text.style.TextOverflow
 | 
				
			||||||
 | 
					import androidx.compose.ui.unit.Dp
 | 
				
			||||||
 | 
					import androidx.compose.ui.unit.dp
 | 
				
			||||||
 | 
					import androidx.compose.ui.window.Dialog
 | 
				
			||||||
 | 
					import com.pixelized.desktop.lwa.ui.composable.decoratedBox.DecoratedBox
 | 
				
			||||||
 | 
					import com.pixelized.desktop.lwa.ui.composable.image.DesaturatedAsyncImage
 | 
				
			||||||
 | 
					import com.pixelized.desktop.lwa.ui.composable.textfield.LwaTextField
 | 
				
			||||||
 | 
					import com.pixelized.desktop.lwa.ui.composable.textfield.LwaTextFieldUio
 | 
				
			||||||
 | 
					import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipLayout2
 | 
				
			||||||
 | 
					import com.pixelized.desktop.lwa.ui.theme.color.component.LwaTextFieldColors
 | 
				
			||||||
 | 
					import com.pixelized.desktop.lwa.ui.theme.lwa
 | 
				
			||||||
 | 
					import com.pixelized.desktop.lwa.utils.extention.onPreviewEscape
 | 
				
			||||||
 | 
					import com.pixelized.desktop.lwa.utils.rememberSaturationFilter
 | 
				
			||||||
 | 
					import lwacharactersheet.composeapp.generated.resources.Res
 | 
				
			||||||
 | 
					import lwacharactersheet.composeapp.generated.resources.character__inventory__inventory__dialog__title
 | 
				
			||||||
 | 
					import lwacharactersheet.composeapp.generated.resources.ic_cancel_24dp
 | 
				
			||||||
 | 
					import lwacharactersheet.composeapp.generated.resources.ic_close_24dp
 | 
				
			||||||
 | 
					import org.jetbrains.compose.resources.painterResource
 | 
				
			||||||
 | 
					import org.jetbrains.compose.resources.stringResource
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Stable
 | 
				
			||||||
 | 
					data class InventoryDialogUio(
 | 
				
			||||||
 | 
					    val characterSheetId: String,
 | 
				
			||||||
 | 
					    val filter: LwaTextFieldUio,
 | 
				
			||||||
 | 
					    val items: List<Item>,
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					    @Stable
 | 
				
			||||||
 | 
					    data class Item(
 | 
				
			||||||
 | 
					        val itemId: String,
 | 
				
			||||||
 | 
					        val label: String,
 | 
				
			||||||
 | 
					        val tooltips: ToolTip?,
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        @Stable
 | 
				
			||||||
 | 
					        data class ToolTip(
 | 
				
			||||||
 | 
					            val label: String,
 | 
				
			||||||
 | 
					            val description: String,
 | 
				
			||||||
 | 
					            val image: String?,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Stable
 | 
				
			||||||
 | 
					object InventoryDialogDefault {
 | 
				
			||||||
 | 
					    @Stable
 | 
				
			||||||
 | 
					    val paddings = PaddingValues(all = 16.dp)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Stable
 | 
				
			||||||
 | 
					    val spacings = 8.dp
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Stable
 | 
				
			||||||
 | 
					object InventoryDialogItemDefault {
 | 
				
			||||||
 | 
					    @Stable
 | 
				
			||||||
 | 
					    val paddings = PaddingValues(horizontal = 16.dp)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Stable
 | 
				
			||||||
 | 
					    val spacings = 8.dp
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Composable
 | 
					@Composable
 | 
				
			||||||
fun InventoryDialog() {
 | 
					fun InventoryDialog(
 | 
				
			||||||
 | 
					    dialog: State<InventoryDialogUio?>,
 | 
				
			||||||
 | 
					    paddings: PaddingValues = InventoryDialogDefault.paddings,
 | 
				
			||||||
 | 
					    onDismissRequest: () -> Unit,
 | 
				
			||||||
 | 
					    onItem: (String) -> Unit,
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					    dialog.value?.let {
 | 
				
			||||||
 | 
					        Dialog(
 | 
				
			||||||
 | 
					            onDismissRequest = onDismissRequest,
 | 
				
			||||||
 | 
					            content = {
 | 
				
			||||||
 | 
					                InventoryDialogContent(
 | 
				
			||||||
 | 
					                    dialog = it,
 | 
				
			||||||
 | 
					                    paddings = paddings,
 | 
				
			||||||
 | 
					                    onDismissRequest = onDismissRequest,
 | 
				
			||||||
 | 
					                    onItem = onItem,
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Composable
 | 
				
			||||||
 | 
					private fun InventoryDialogContent(
 | 
				
			||||||
 | 
					    dialog: InventoryDialogUio,
 | 
				
			||||||
 | 
					    paddings: PaddingValues = InventoryDialogDefault.paddings,
 | 
				
			||||||
 | 
					    spacing: Dp = InventoryDialogDefault.spacings,
 | 
				
			||||||
 | 
					    onDismissRequest: () -> Unit,
 | 
				
			||||||
 | 
					    onItem: (String) -> Unit,
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					    val layoutDirection = LocalLayoutDirection.current
 | 
				
			||||||
 | 
					    val start = remember(layoutDirection) { paddings.calculateStartPadding(layoutDirection) }
 | 
				
			||||||
 | 
					    val end = remember(layoutDirection) { paddings.calculateEndPadding(layoutDirection) }
 | 
				
			||||||
 | 
					    val top = remember(layoutDirection) { paddings.calculateTopPadding() }
 | 
				
			||||||
 | 
					    val bottom = remember(layoutDirection) { paddings.calculateBottomPadding() }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Box(
 | 
				
			||||||
 | 
					        modifier = Modifier
 | 
				
			||||||
 | 
					            .clickable(
 | 
				
			||||||
 | 
					                interactionSource = remember { MutableInteractionSource() },
 | 
				
			||||||
 | 
					                indication = null,
 | 
				
			||||||
 | 
					                onClick = onDismissRequest,
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            .onPreviewEscape(
 | 
				
			||||||
 | 
					                escape = onDismissRequest,
 | 
				
			||||||
 | 
					                enter = onDismissRequest,
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            .fillMaxSize()
 | 
				
			||||||
 | 
					            .padding(all = 32.dp),
 | 
				
			||||||
 | 
					        contentAlignment = Alignment.Center,
 | 
				
			||||||
 | 
					    ) {
 | 
				
			||||||
 | 
					        DecoratedBox {
 | 
				
			||||||
 | 
					            Surface {
 | 
				
			||||||
 | 
					                Column {
 | 
				
			||||||
 | 
					                    Row(
 | 
				
			||||||
 | 
					                        modifier = Modifier
 | 
				
			||||||
 | 
					                            .fillMaxWidth()
 | 
				
			||||||
 | 
					                            .padding(top = top, start = start, bottom = spacing, end = end),
 | 
				
			||||||
 | 
					                        verticalAlignment = Alignment.CenterVertically,
 | 
				
			||||||
 | 
					                        horizontalArrangement = Arrangement.SpaceBetween,
 | 
				
			||||||
 | 
					                    ) {
 | 
				
			||||||
 | 
					                        Text(
 | 
				
			||||||
 | 
					                            modifier = Modifier.weight(weight = 1f),
 | 
				
			||||||
 | 
					                            style = MaterialTheme.typography.h5,
 | 
				
			||||||
 | 
					                            fontWeight = FontWeight.Bold,
 | 
				
			||||||
 | 
					                            overflow = TextOverflow.Ellipsis,
 | 
				
			||||||
 | 
					                            maxLines = 1,
 | 
				
			||||||
 | 
					                            text = stringResource(Res.string.character__inventory__inventory__dialog__title),
 | 
				
			||||||
 | 
					                        )
 | 
				
			||||||
 | 
					                        IconButton(
 | 
				
			||||||
 | 
					                            modifier = Modifier.offset(x = end, y = -top),
 | 
				
			||||||
 | 
					                            onClick = onDismissRequest,
 | 
				
			||||||
 | 
					                        ) {
 | 
				
			||||||
 | 
					                            Icon(
 | 
				
			||||||
 | 
					                                painter = painterResource(Res.drawable.ic_close_24dp),
 | 
				
			||||||
 | 
					                                tint = MaterialTheme.lwa.colorScheme.base.primary,
 | 
				
			||||||
 | 
					                                contentDescription = null,
 | 
				
			||||||
 | 
					                            )
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    LwaTextField(
 | 
				
			||||||
 | 
					                        colors = LwaTextFieldColors(backgroundColor = Color.Transparent),
 | 
				
			||||||
 | 
					                        modifier = Modifier.fillMaxWidth(),
 | 
				
			||||||
 | 
					                        field = dialog.filter,
 | 
				
			||||||
 | 
					                        trailingIcon = {
 | 
				
			||||||
 | 
					                            val value = dialog.filter.valueFlow.collectAsState()
 | 
				
			||||||
 | 
					                            AnimatedVisibility(
 | 
				
			||||||
 | 
					                                visible = value.value.isNotBlank(),
 | 
				
			||||||
 | 
					                                enter = fadeIn(),
 | 
				
			||||||
 | 
					                                exit = fadeOut(),
 | 
				
			||||||
 | 
					                            ) {
 | 
				
			||||||
 | 
					                                IconButton(
 | 
				
			||||||
 | 
					                                    onClick = { dialog.filter.onValueChange.invoke("") },
 | 
				
			||||||
 | 
					                                ) {
 | 
				
			||||||
 | 
					                                    Icon(
 | 
				
			||||||
 | 
					                                        painter = painterResource(Res.drawable.ic_cancel_24dp),
 | 
				
			||||||
 | 
					                                        tint = MaterialTheme.lwa.colorScheme.base.primary,
 | 
				
			||||||
 | 
					                                        contentDescription = null,
 | 
				
			||||||
 | 
					                                    )
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                    LazyColumn(
 | 
				
			||||||
 | 
					                        modifier = Modifier
 | 
				
			||||||
 | 
					                            .fillMaxWidth()
 | 
				
			||||||
 | 
					                            .weight(weight = 1f),
 | 
				
			||||||
 | 
					                        contentPadding = PaddingValues(
 | 
				
			||||||
 | 
					                            start = start,
 | 
				
			||||||
 | 
					                            top = spacing,
 | 
				
			||||||
 | 
					                            end = end,
 | 
				
			||||||
 | 
					                            bottom = bottom
 | 
				
			||||||
 | 
					                        ),
 | 
				
			||||||
 | 
					                        verticalArrangement = Arrangement.spacedBy(space = spacing),
 | 
				
			||||||
 | 
					                    ) {
 | 
				
			||||||
 | 
					                        items(
 | 
				
			||||||
 | 
					                            items = dialog.items,
 | 
				
			||||||
 | 
					                            key = { it.itemId },
 | 
				
			||||||
 | 
					                        ) { item ->
 | 
				
			||||||
 | 
					                            InventoryDialogItem(
 | 
				
			||||||
 | 
					                                modifier = Modifier.animateItem(),
 | 
				
			||||||
 | 
					                                item = item,
 | 
				
			||||||
 | 
					                                onItem = onItem,
 | 
				
			||||||
 | 
					                            )
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@OptIn(ExperimentalFoundationApi::class)
 | 
				
			||||||
 | 
					@Composable
 | 
				
			||||||
 | 
					private fun InventoryDialogItem(
 | 
				
			||||||
 | 
					    modifier: Modifier = Modifier,
 | 
				
			||||||
 | 
					    paddings: PaddingValues = InventoryDialogItemDefault.paddings,
 | 
				
			||||||
 | 
					    spacings: Dp = InventoryDialogItemDefault.spacings,
 | 
				
			||||||
 | 
					    item: InventoryDialogUio.Item,
 | 
				
			||||||
 | 
					    onItem: (String) -> Unit,
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					    TooltipLayout2(
 | 
				
			||||||
 | 
					        modifier = modifier,
 | 
				
			||||||
 | 
					        delayMillis = 500,
 | 
				
			||||||
 | 
					        tips = item.tooltips,
 | 
				
			||||||
 | 
					        tooltip = { tooltips ->
 | 
				
			||||||
 | 
					            DecoratedBox {
 | 
				
			||||||
 | 
					                Surface {
 | 
				
			||||||
 | 
					                    Box(
 | 
				
			||||||
 | 
					                        modifier = Modifier.padding(all = 16.dp)
 | 
				
			||||||
 | 
					                    ) {
 | 
				
			||||||
 | 
					                        takeIf { tooltips.image?.isNotEmpty() == true }?.let {
 | 
				
			||||||
 | 
					                            DesaturatedAsyncImage(
 | 
				
			||||||
 | 
					                                modifier = Modifier
 | 
				
			||||||
 | 
					                                    .size(96.dp)
 | 
				
			||||||
 | 
					                                    .align(alignment = Alignment.TopEnd)
 | 
				
			||||||
 | 
					                                    .offset(x = 8.dp, y = (-8).dp),
 | 
				
			||||||
 | 
					                                colorFilter = rememberSaturationFilter(),
 | 
				
			||||||
 | 
					                                model = tooltips.image,
 | 
				
			||||||
 | 
					                                contentScale = ContentScale.Crop,
 | 
				
			||||||
 | 
					                                alignment = Alignment.TopCenter,
 | 
				
			||||||
 | 
					                                filterQuality = FilterQuality.High,
 | 
				
			||||||
 | 
					                                contentDescription = null,
 | 
				
			||||||
 | 
					                            )
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        Column(
 | 
				
			||||||
 | 
					                            verticalArrangement = Arrangement.spacedBy(space = 8.dp)
 | 
				
			||||||
 | 
					                        ) {
 | 
				
			||||||
 | 
					                            Text(
 | 
				
			||||||
 | 
					                                style = MaterialTheme.typography.body2,
 | 
				
			||||||
 | 
					                                fontWeight = FontWeight.Bold,
 | 
				
			||||||
 | 
					                                text = tooltips.label,
 | 
				
			||||||
 | 
					                            )
 | 
				
			||||||
 | 
					                            Text(
 | 
				
			||||||
 | 
					                                style = MaterialTheme.typography.caption,
 | 
				
			||||||
 | 
					                                fontWeight = FontWeight.Light,
 | 
				
			||||||
 | 
					                                text = tooltips.description,
 | 
				
			||||||
 | 
					                            )
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        content = {
 | 
				
			||||||
 | 
					            Row(
 | 
				
			||||||
 | 
					                modifier = Modifier
 | 
				
			||||||
 | 
					                    .clip(shape = MaterialTheme.lwa.shapes.item)
 | 
				
			||||||
 | 
					                    .clickable(onClick = { onItem(item.itemId) })
 | 
				
			||||||
 | 
					                    .background(color = MaterialTheme.lwa.colorScheme.elevated.base2dp)
 | 
				
			||||||
 | 
					                    .minimumInteractiveComponentSize()
 | 
				
			||||||
 | 
					                    .padding(paddingValues = paddings)
 | 
				
			||||||
 | 
					                    .fillMaxWidth(),
 | 
				
			||||||
 | 
					                horizontalArrangement = Arrangement.spacedBy(space = spacings),
 | 
				
			||||||
 | 
					                verticalAlignment = Alignment.CenterVertically,
 | 
				
			||||||
 | 
					            ) {
 | 
				
			||||||
 | 
					                Text(
 | 
				
			||||||
 | 
					                    modifier = Modifier.alignByBaseline(),
 | 
				
			||||||
 | 
					                    style = MaterialTheme.lwa.typography.base.body1,
 | 
				
			||||||
 | 
					                    fontWeight = FontWeight.Bold,
 | 
				
			||||||
 | 
					                    overflow = TextOverflow.Ellipsis,
 | 
				
			||||||
 | 
					                    maxLines = 1,
 | 
				
			||||||
 | 
					                    text = item.label,
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,82 @@
 | 
				
			||||||
package com.pixelized.desktop.lwa.ui.composable.character.inventory
 | 
					package com.pixelized.desktop.lwa.ui.composable.character.inventory
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.pixelized.desktop.lwa.repository.settings.model.Settings
 | 
				
			||||||
 | 
					import com.pixelized.desktop.lwa.ui.composable.textfield.LwaTextFieldUio
 | 
				
			||||||
 | 
					import com.pixelized.desktop.lwa.utils.extention.unAccent
 | 
				
			||||||
 | 
					import com.pixelized.shared.lwa.model.item.Item
 | 
				
			||||||
 | 
					import kotlinx.coroutines.flow.MutableStateFlow
 | 
				
			||||||
 | 
					import lwacharactersheet.composeapp.generated.resources.Res
 | 
				
			||||||
 | 
					import lwacharactersheet.composeapp.generated.resources.character__inventory__filter_item_inventory__label
 | 
				
			||||||
 | 
					import org.jetbrains.compose.resources.getString
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class InventoryDialogFactory {
 | 
					class InventoryDialogFactory {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fun filterItems(
 | 
				
			||||||
 | 
					        items: Collection<Item>,
 | 
				
			||||||
 | 
					        filter: String,
 | 
				
			||||||
 | 
					        setting: Settings,
 | 
				
			||||||
 | 
					    ): Collection<Item> {
 | 
				
			||||||
 | 
					        return if (setting.isGameMaster == true) {
 | 
				
			||||||
 | 
					            items.filter {
 | 
				
			||||||
 | 
					                it.metadata.label.unAccent().contains(other = filter, ignoreCase = true)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            items.filter {
 | 
				
			||||||
 | 
					                it.tags.contains(ADDABLE_TAG_ID)
 | 
				
			||||||
 | 
					            }.filter {
 | 
				
			||||||
 | 
					                it.metadata.label.unAccent().contains(other = filter, ignoreCase = true)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    suspend fun convertToDialogUio(
 | 
				
			||||||
 | 
					        items: Collection<Item>,
 | 
				
			||||||
 | 
					        filterFlow: Pair<MutableStateFlow<String>, MutableStateFlow<Boolean>>,
 | 
				
			||||||
 | 
					        characterSheetId: String?,
 | 
				
			||||||
 | 
					    ): InventoryDialogUio? {
 | 
				
			||||||
 | 
					        if (characterSheetId == null) return null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return InventoryDialogUio(
 | 
				
			||||||
 | 
					            characterSheetId = characterSheetId,
 | 
				
			||||||
 | 
					            filter = filterFlow.createTextField(
 | 
				
			||||||
 | 
					                label = getString(Res.string.character__inventory__filter_item_inventory__label),
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            items = items.map { item ->
 | 
				
			||||||
 | 
					                InventoryDialogUio.Item(
 | 
				
			||||||
 | 
					                    itemId = item.id,
 | 
				
			||||||
 | 
					                    label = item.metadata.label,
 | 
				
			||||||
 | 
					                    tooltips = takeIf { item.metadata.description.isNotEmpty() }?.let {
 | 
				
			||||||
 | 
					                        InventoryDialogUio.Item.ToolTip(
 | 
				
			||||||
 | 
					                            label = item.metadata.label,
 | 
				
			||||||
 | 
					                            description = item.metadata.description,
 | 
				
			||||||
 | 
					                            image = item.metadata.image,
 | 
				
			||||||
 | 
					                        )
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fun createTextFieldFlow(
 | 
				
			||||||
 | 
					        value: String = "",
 | 
				
			||||||
 | 
					        error: Boolean = false,
 | 
				
			||||||
 | 
					    ): Pair<MutableStateFlow<String>, MutableStateFlow<Boolean>> {
 | 
				
			||||||
 | 
					        return MutableStateFlow(value) to MutableStateFlow(error)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private fun Pair<MutableStateFlow<String>, MutableStateFlow<Boolean>>.createTextField(
 | 
				
			||||||
 | 
					        enable: Boolean = true,
 | 
				
			||||||
 | 
					        label: String,
 | 
				
			||||||
 | 
					    ) = LwaTextFieldUio(
 | 
				
			||||||
 | 
					        enable = enable,
 | 
				
			||||||
 | 
					        isError = second,
 | 
				
			||||||
 | 
					        valueFlow = first,
 | 
				
			||||||
 | 
					        label = label,
 | 
				
			||||||
 | 
					        placeHolder = null,
 | 
				
			||||||
 | 
					        onValueChange = { first.value = it },
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    companion object {
 | 
				
			||||||
 | 
					        private const val ADDABLE_TAG_ID = "META:ADDABLE"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,52 @@
 | 
				
			||||||
package com.pixelized.desktop.lwa.ui.composable.character.inventory
 | 
					package com.pixelized.desktop.lwa.ui.composable.character.inventory
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import androidx.lifecycle.ViewModel
 | 
					import androidx.lifecycle.ViewModel
 | 
				
			||||||
 | 
					import androidx.lifecycle.viewModelScope
 | 
				
			||||||
 | 
					import com.pixelized.desktop.lwa.repository.item.ItemRepository
 | 
				
			||||||
 | 
					import com.pixelized.desktop.lwa.repository.settings.SettingsRepository
 | 
				
			||||||
 | 
					import kotlinx.coroutines.flow.MutableStateFlow
 | 
				
			||||||
 | 
					import kotlinx.coroutines.flow.SharingStarted
 | 
				
			||||||
 | 
					import kotlinx.coroutines.flow.combine
 | 
				
			||||||
 | 
					import kotlinx.coroutines.flow.map
 | 
				
			||||||
 | 
					import kotlinx.coroutines.flow.stateIn
 | 
				
			||||||
 | 
					import kotlinx.coroutines.flow.update
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class InventoryDialogViewModel: ViewModel() {
 | 
					class InventoryDialogViewModel(
 | 
				
			||||||
 | 
					    itemRepository: ItemRepository,
 | 
				
			||||||
 | 
					    settingRepository: SettingsRepository,
 | 
				
			||||||
 | 
					    factory: InventoryDialogFactory,
 | 
				
			||||||
 | 
					) : ViewModel() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private val selectedCharacterSheetId = MutableStateFlow<String?>(null)
 | 
				
			||||||
 | 
					    private val filterFlow = factory.createTextFieldFlow()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    val inventoryDialog = combine(
 | 
				
			||||||
 | 
					        itemRepository.itemFlow().map { it.values },
 | 
				
			||||||
 | 
					        settingRepository.settingsFlow(),
 | 
				
			||||||
 | 
					        filterFlow.first,
 | 
				
			||||||
 | 
					        selectedCharacterSheetId,
 | 
				
			||||||
 | 
					    ) { items, settings, filter, characterSheetId ->
 | 
				
			||||||
 | 
					        factory.convertToDialogUio(
 | 
				
			||||||
 | 
					            items = factory.filterItems(
 | 
				
			||||||
 | 
					                items = items,
 | 
				
			||||||
 | 
					                filter = filter,
 | 
				
			||||||
 | 
					                setting = settings,
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					            filterFlow = filterFlow,
 | 
				
			||||||
 | 
					            characterSheetId = characterSheetId,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    }.stateIn(
 | 
				
			||||||
 | 
					        scope = viewModelScope,
 | 
				
			||||||
 | 
					        started = SharingStarted.Lazily,
 | 
				
			||||||
 | 
					        initialValue = null
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fun showInventoryDialog(characterSheetId: String?) {
 | 
				
			||||||
 | 
					        filterFlow.first.update { "" }
 | 
				
			||||||
 | 
					        selectedCharacterSheetId.update { characterSheetId }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fun hideInventoryDialog() {
 | 
				
			||||||
 | 
					        selectedCharacterSheetId.update { null }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -6,9 +6,15 @@ 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.Column
 | 
				
			||||||
import androidx.compose.foundation.layout.PaddingValues
 | 
					import androidx.compose.foundation.layout.PaddingValues
 | 
				
			||||||
 | 
					import androidx.compose.foundation.layout.Row
 | 
				
			||||||
 | 
					import androidx.compose.foundation.layout.calculateEndPadding
 | 
				
			||||||
import androidx.compose.foundation.layout.fillMaxSize
 | 
					import androidx.compose.foundation.layout.fillMaxSize
 | 
				
			||||||
 | 
					import androidx.compose.foundation.layout.fillMaxWidth
 | 
				
			||||||
 | 
					import androidx.compose.foundation.layout.offset
 | 
				
			||||||
import androidx.compose.foundation.layout.padding
 | 
					import androidx.compose.foundation.layout.padding
 | 
				
			||||||
import androidx.compose.foundation.layout.size
 | 
					import androidx.compose.foundation.layout.size
 | 
				
			||||||
 | 
					import androidx.compose.material.Icon
 | 
				
			||||||
 | 
					import androidx.compose.material.IconButton
 | 
				
			||||||
import androidx.compose.material.MaterialTheme
 | 
					import androidx.compose.material.MaterialTheme
 | 
				
			||||||
import androidx.compose.material.Surface
 | 
					import androidx.compose.material.Surface
 | 
				
			||||||
import androidx.compose.material.Text
 | 
					import androidx.compose.material.Text
 | 
				
			||||||
| 
						 | 
					@ -20,16 +26,23 @@ import androidx.compose.ui.Alignment
 | 
				
			||||||
import androidx.compose.ui.Modifier
 | 
					import androidx.compose.ui.Modifier
 | 
				
			||||||
import androidx.compose.ui.graphics.FilterQuality
 | 
					import androidx.compose.ui.graphics.FilterQuality
 | 
				
			||||||
import androidx.compose.ui.layout.ContentScale
 | 
					import androidx.compose.ui.layout.ContentScale
 | 
				
			||||||
 | 
					import androidx.compose.ui.platform.LocalLayoutDirection
 | 
				
			||||||
import androidx.compose.ui.text.font.FontWeight
 | 
					import androidx.compose.ui.text.font.FontWeight
 | 
				
			||||||
 | 
					import androidx.compose.ui.text.style.TextOverflow
 | 
				
			||||||
import androidx.compose.ui.unit.dp
 | 
					import androidx.compose.ui.unit.dp
 | 
				
			||||||
import androidx.compose.ui.window.Dialog
 | 
					import androidx.compose.ui.window.Dialog
 | 
				
			||||||
import com.pixelized.desktop.lwa.ui.composable.decoratedBox.DecoratedBox
 | 
					import com.pixelized.desktop.lwa.ui.composable.decoratedBox.DecoratedBox
 | 
				
			||||||
import com.pixelized.desktop.lwa.ui.composable.image.DesaturatedAsyncImage
 | 
					import com.pixelized.desktop.lwa.ui.composable.image.DesaturatedAsyncImage
 | 
				
			||||||
 | 
					import com.pixelized.desktop.lwa.ui.theme.lwa
 | 
				
			||||||
import com.pixelized.desktop.lwa.utils.extention.onPreviewEscape
 | 
					import com.pixelized.desktop.lwa.utils.extention.onPreviewEscape
 | 
				
			||||||
import com.pixelized.desktop.lwa.utils.rememberSaturationFilter
 | 
					import com.pixelized.desktop.lwa.utils.rememberSaturationFilter
 | 
				
			||||||
 | 
					import lwacharactersheet.composeapp.generated.resources.Res
 | 
				
			||||||
 | 
					import lwacharactersheet.composeapp.generated.resources.ic_close_24dp
 | 
				
			||||||
 | 
					import org.jetbrains.compose.resources.painterResource
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Stable
 | 
					@Stable
 | 
				
			||||||
data class ItemDetailDialogUio(
 | 
					data class ItemDetailDialogUio(
 | 
				
			||||||
 | 
					    val inventoryId: String?,
 | 
				
			||||||
    val itemId: String,
 | 
					    val itemId: String,
 | 
				
			||||||
    val label: String,
 | 
					    val label: String,
 | 
				
			||||||
    val description: String,
 | 
					    val description: String,
 | 
				
			||||||
| 
						 | 
					@ -76,6 +89,10 @@ private fun ItemDetailDialogContent(
 | 
				
			||||||
    dialog: ItemDetailDialogUio,
 | 
					    dialog: ItemDetailDialogUio,
 | 
				
			||||||
    onDismissRequest: () -> Unit,
 | 
					    onDismissRequest: () -> Unit,
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
 | 
					    val layoutDirection = LocalLayoutDirection.current
 | 
				
			||||||
 | 
					    val end = remember(layoutDirection, paddings) { paddings.calculateEndPadding(layoutDirection) }
 | 
				
			||||||
 | 
					    val top = remember(paddings) { paddings.calculateTopPadding() }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Box(
 | 
					    Box(
 | 
				
			||||||
        modifier = Modifier
 | 
					        modifier = Modifier
 | 
				
			||||||
            .clickable(
 | 
					            .clickable(
 | 
				
			||||||
| 
						 | 
					@ -96,6 +113,7 @@ private fun ItemDetailDialogContent(
 | 
				
			||||||
                Box(
 | 
					                Box(
 | 
				
			||||||
                    modifier = Modifier.padding(paddingValues = paddings)
 | 
					                    modifier = Modifier.padding(paddingValues = paddings)
 | 
				
			||||||
                ) {
 | 
					                ) {
 | 
				
			||||||
 | 
					                    takeIf { dialog.image?.isNotEmpty() == true }?.let {
 | 
				
			||||||
                        DesaturatedAsyncImage(
 | 
					                        DesaturatedAsyncImage(
 | 
				
			||||||
                            modifier = Modifier
 | 
					                            modifier = Modifier
 | 
				
			||||||
                                .size(64.dp * 2)
 | 
					                                .size(64.dp * 2)
 | 
				
			||||||
| 
						 | 
					@ -107,15 +125,35 @@ private fun ItemDetailDialogContent(
 | 
				
			||||||
                            filterQuality = FilterQuality.High,
 | 
					                            filterQuality = FilterQuality.High,
 | 
				
			||||||
                            contentDescription = null,
 | 
					                            contentDescription = null,
 | 
				
			||||||
                        )
 | 
					                        )
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                    Column(
 | 
					                    Column(
 | 
				
			||||||
                        modifier = modifier,
 | 
					                        modifier = modifier,
 | 
				
			||||||
                        verticalArrangement = Arrangement.spacedBy(space = 8.dp)
 | 
					                        verticalArrangement = Arrangement.spacedBy(space = 8.dp)
 | 
				
			||||||
 | 
					                    ) {
 | 
				
			||||||
 | 
					                        Row(
 | 
				
			||||||
 | 
					                            modifier = Modifier.fillMaxWidth(),
 | 
				
			||||||
 | 
					                            verticalAlignment = Alignment.CenterVertically,
 | 
				
			||||||
 | 
					                            horizontalArrangement = Arrangement.SpaceBetween,
 | 
				
			||||||
                        ) {
 | 
					                        ) {
 | 
				
			||||||
                            Text(
 | 
					                            Text(
 | 
				
			||||||
 | 
					                                modifier = Modifier.weight(weight = 1f),
 | 
				
			||||||
                                style = MaterialTheme.typography.h5,
 | 
					                                style = MaterialTheme.typography.h5,
 | 
				
			||||||
                                fontWeight = FontWeight.Bold,
 | 
					                                fontWeight = FontWeight.Bold,
 | 
				
			||||||
 | 
					                                overflow = TextOverflow.Ellipsis,
 | 
				
			||||||
 | 
					                                maxLines = 1,
 | 
				
			||||||
                                text = dialog.label,
 | 
					                                text = dialog.label,
 | 
				
			||||||
                            )
 | 
					                            )
 | 
				
			||||||
 | 
					                            IconButton(
 | 
				
			||||||
 | 
					                                modifier = Modifier.offset(x = end, y = -top),
 | 
				
			||||||
 | 
					                                onClick = onDismissRequest,
 | 
				
			||||||
 | 
					                            ) {
 | 
				
			||||||
 | 
					                                Icon(
 | 
				
			||||||
 | 
					                                    painter = painterResource(Res.drawable.ic_close_24dp),
 | 
				
			||||||
 | 
					                                    tint = MaterialTheme.lwa.colorScheme.base.primary,
 | 
				
			||||||
 | 
					                                    contentDescription = null,
 | 
				
			||||||
 | 
					                                )
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
                        Text(
 | 
					                        Text(
 | 
				
			||||||
                            style = MaterialTheme.typography.body1,
 | 
					                            style = MaterialTheme.typography.body1,
 | 
				
			||||||
                            text = dialog.description,
 | 
					                            text = dialog.description,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,19 +1,26 @@
 | 
				
			||||||
package com.pixelized.desktop.lwa.ui.composable.character.item
 | 
					package com.pixelized.desktop.lwa.ui.composable.character.item
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.pixelized.shared.lwa.model.item.Item
 | 
					import com.pixelized.shared.lwa.model.item.Item
 | 
				
			||||||
 | 
					import lwacharactersheet.composeapp.generated.resources.Res
 | 
				
			||||||
 | 
					import lwacharactersheet.composeapp.generated.resources.character__inventory__description_empty__label
 | 
				
			||||||
 | 
					import org.jetbrains.compose.resources.getString
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ItemDetailDialogFactory {
 | 
					class ItemDetailDialogFactory {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fun convertToDialogUio(
 | 
					    suspend fun convertToDialogUio(
 | 
				
			||||||
        items: Map<String, Item>,
 | 
					        items: Map<String, Item>,
 | 
				
			||||||
 | 
					        inventoryId: String?,
 | 
				
			||||||
        itemId: String?,
 | 
					        itemId: String?,
 | 
				
			||||||
    ): ItemDetailDialogUio? {
 | 
					    ): ItemDetailDialogUio? {
 | 
				
			||||||
        val item = itemId.let(items::get) ?: return null
 | 
					        val item = itemId.let(items::get) ?: return null
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return ItemDetailDialogUio(
 | 
					        return ItemDetailDialogUio(
 | 
				
			||||||
 | 
					            inventoryId = inventoryId,
 | 
				
			||||||
            itemId = item.id,
 | 
					            itemId = item.id,
 | 
				
			||||||
            label = item.metadata.label,
 | 
					            label = item.metadata.label,
 | 
				
			||||||
            description = item.metadata.description,
 | 
					            description = item.metadata.description.ifBlank {
 | 
				
			||||||
 | 
					                getString(Res.string.character__inventory__description_empty__label)
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
            image = item.metadata.image,
 | 
					            image = item.metadata.image,
 | 
				
			||||||
            option = ItemDetailDialogUio.OptionUio(
 | 
					            option = ItemDetailDialogUio.OptionUio(
 | 
				
			||||||
                equipable = item.options.equipable,
 | 
					                equipable = item.options.equipable,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,21 +14,37 @@ class ItemDetailDialogViewModel(
 | 
				
			||||||
    factory: ItemDetailDialogFactory,
 | 
					    factory: ItemDetailDialogFactory,
 | 
				
			||||||
) : ViewModel() {
 | 
					) : ViewModel() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private val selectedItemId = MutableStateFlow<String?>(null)
 | 
					    private val selectedItemId = MutableStateFlow<InventoryItemId?>(null)
 | 
				
			||||||
    val itemDialog = combine(
 | 
					    val itemDialog = combine(
 | 
				
			||||||
        itemRepository.itemFlow, selectedItemId,
 | 
					        itemRepository.itemFlow(), selectedItemId,
 | 
				
			||||||
        transform = { items, itemId -> factory.convertToDialogUio(items, itemId) }
 | 
					        transform = { items, ids ->
 | 
				
			||||||
 | 
					            factory.convertToDialogUio(
 | 
				
			||||||
 | 
					                items = items,
 | 
				
			||||||
 | 
					                inventoryId = ids?.inventoryId,
 | 
				
			||||||
 | 
					                itemId = ids?.itemId,
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    ).stateIn(
 | 
					    ).stateIn(
 | 
				
			||||||
        scope = viewModelScope,
 | 
					        scope = viewModelScope,
 | 
				
			||||||
        started = SharingStarted.Lazily,
 | 
					        started = SharingStarted.Lazily,
 | 
				
			||||||
        initialValue = null
 | 
					        initialValue = null
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fun showItemDialog(itemId: String?) {
 | 
					    fun showItemDialog(inventoryId: String?, itemId: String?) {
 | 
				
			||||||
        selectedItemId.update { itemId }
 | 
					        selectedItemId.update {
 | 
				
			||||||
 | 
					            InventoryItemId(
 | 
				
			||||||
 | 
					                inventoryId = inventoryId,
 | 
				
			||||||
 | 
					                itemId = itemId,
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fun hideItemDialog() {
 | 
					    fun hideItemDialog() {
 | 
				
			||||||
        selectedItemId.update { null }
 | 
					        selectedItemId.update { null }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private data class InventoryItemId(
 | 
				
			||||||
 | 
					        val inventoryId: String?,
 | 
				
			||||||
 | 
					        val itemId: String?,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,43 @@
 | 
				
			||||||
 | 
					package com.pixelized.desktop.lwa.ui.composable.tooltip
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import androidx.compose.foundation.ExperimentalFoundationApi
 | 
				
			||||||
 | 
					import androidx.compose.foundation.TooltipArea
 | 
				
			||||||
 | 
					import androidx.compose.foundation.TooltipPlacement
 | 
				
			||||||
 | 
					import androidx.compose.foundation.layout.Box
 | 
				
			||||||
 | 
					import androidx.compose.foundation.layout.width
 | 
				
			||||||
 | 
					import androidx.compose.runtime.Composable
 | 
				
			||||||
 | 
					import androidx.compose.ui.Modifier
 | 
				
			||||||
 | 
					import androidx.compose.ui.unit.DpOffset
 | 
				
			||||||
 | 
					import androidx.compose.ui.unit.dp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Composable
 | 
				
			||||||
 | 
					@OptIn(ExperimentalFoundationApi::class)
 | 
				
			||||||
 | 
					fun <T> TooltipLayout2(
 | 
				
			||||||
 | 
					    modifier: Modifier = Modifier,
 | 
				
			||||||
 | 
					    delayMillis: Int = 1000,
 | 
				
			||||||
 | 
					    tooltipPlacement: TooltipPlacement = TooltipPlacement.CursorPoint(DpOffset(0.dp, 16.dp)),
 | 
				
			||||||
 | 
					    tips: T? = null,
 | 
				
			||||||
 | 
					    tooltip: (@Composable (tips: T) -> Unit)? = null,
 | 
				
			||||||
 | 
					    content: @Composable () -> Unit,
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					    if (tips != null && tooltip != null) {
 | 
				
			||||||
 | 
					        TooltipArea(
 | 
				
			||||||
 | 
					            modifier = modifier,
 | 
				
			||||||
 | 
					            tooltip = {
 | 
				
			||||||
 | 
					                Box(
 | 
				
			||||||
 | 
					                    modifier = Modifier.width(width = 448.dp),
 | 
				
			||||||
 | 
					                    content = { tooltip(tips) },
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            content = content,
 | 
				
			||||||
 | 
					            delayMillis = delayMillis,
 | 
				
			||||||
 | 
					            tooltipPlacement = tooltipPlacement,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        Box(
 | 
				
			||||||
 | 
					            modifier = modifier,
 | 
				
			||||||
 | 
					            content = { content() },
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -30,6 +30,8 @@ import androidx.compose.ui.Modifier
 | 
				
			||||||
import androidx.compose.ui.unit.Dp
 | 
					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.inventory.InventoryDialog
 | 
				
			||||||
 | 
					import com.pixelized.desktop.lwa.ui.composable.character.inventory.InventoryDialogViewModel
 | 
				
			||||||
import com.pixelized.desktop.lwa.ui.composable.character.item.ItemDetailDialog
 | 
					import com.pixelized.desktop.lwa.ui.composable.character.item.ItemDetailDialog
 | 
				
			||||||
import com.pixelized.desktop.lwa.ui.composable.character.item.ItemDetailDialogViewModel
 | 
					import com.pixelized.desktop.lwa.ui.composable.character.item.ItemDetailDialogViewModel
 | 
				
			||||||
import com.pixelized.desktop.lwa.ui.composable.character.purse.PurseDialog
 | 
					import com.pixelized.desktop.lwa.ui.composable.character.purse.PurseDialog
 | 
				
			||||||
| 
						 | 
					@ -81,6 +83,7 @@ fun CharacterDetailInventory(
 | 
				
			||||||
    paddings: PaddingValues = CharacterDetailInventoryDefault.padding,
 | 
					    paddings: PaddingValues = CharacterDetailInventoryDefault.padding,
 | 
				
			||||||
    spacing: Dp = CharacterDetailInventoryDefault.spacing,
 | 
					    spacing: Dp = CharacterDetailInventoryDefault.spacing,
 | 
				
			||||||
    purseViewModel: PurseDialogViewModel = koinViewModel(),
 | 
					    purseViewModel: PurseDialogViewModel = koinViewModel(),
 | 
				
			||||||
 | 
					    inventoryDialogViewModel: InventoryDialogViewModel = koinViewModel(),
 | 
				
			||||||
    itemDetailDialogViewModel: ItemDetailDialogViewModel = koinViewModel(),
 | 
					    itemDetailDialogViewModel: ItemDetailDialogViewModel = koinViewModel(),
 | 
				
			||||||
    inventory: State<CharacterDetailInventoryUio?>,
 | 
					    inventory: State<CharacterDetailInventoryUio?>,
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
| 
						 | 
					@ -97,11 +100,22 @@ fun CharacterDetailInventory(
 | 
				
			||||||
            inventory = unWrap,
 | 
					            inventory = unWrap,
 | 
				
			||||||
            onPurse = {
 | 
					            onPurse = {
 | 
				
			||||||
                blur.show()
 | 
					                blur.show()
 | 
				
			||||||
                purseViewModel.showPurseDialog(characterSheetId = it)
 | 
					                purseViewModel.showPurseDialog(
 | 
				
			||||||
 | 
					                    characterSheetId = it,
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            onItem = {
 | 
					            onItem = {
 | 
				
			||||||
                blur.show()
 | 
					                blur.show()
 | 
				
			||||||
                itemDetailDialogViewModel.showItemDialog(itemId = it.itemId)
 | 
					                itemDetailDialogViewModel.showItemDialog(
 | 
				
			||||||
 | 
					                    inventoryId = it.inventoryId,
 | 
				
			||||||
 | 
					                    itemId = it.itemId,
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            onAddItem = {
 | 
				
			||||||
 | 
					                blur.show()
 | 
				
			||||||
 | 
					                inventoryDialogViewModel.showInventoryDialog(
 | 
				
			||||||
 | 
					                    characterSheetId = it,
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -125,6 +139,21 @@ fun CharacterDetailInventory(
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    InventoryDialog(
 | 
				
			||||||
 | 
					        dialog = inventoryDialogViewModel.inventoryDialog.collectAsState(),
 | 
				
			||||||
 | 
					        onDismissRequest = {
 | 
				
			||||||
 | 
					            blur.hide()
 | 
				
			||||||
 | 
					            inventoryDialogViewModel.hideInventoryDialog()
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        onItem = { itemId ->
 | 
				
			||||||
 | 
					            blur.show()
 | 
				
			||||||
 | 
					            itemDetailDialogViewModel.showItemDialog(
 | 
				
			||||||
 | 
					                inventoryId = null,
 | 
				
			||||||
 | 
					                itemId = itemId,
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ItemDetailDialog(
 | 
					    ItemDetailDialog(
 | 
				
			||||||
        dialog = itemDetailDialogViewModel.itemDialog.collectAsState(),
 | 
					        dialog = itemDetailDialogViewModel.itemDialog.collectAsState(),
 | 
				
			||||||
        onDismissRequest = {
 | 
					        onDismissRequest = {
 | 
				
			||||||
| 
						 | 
					@ -146,6 +175,7 @@ private fun CharacterDetailInventoryContent(
 | 
				
			||||||
    inventory: CharacterDetailInventoryUio,
 | 
					    inventory: CharacterDetailInventoryUio,
 | 
				
			||||||
    onPurse: (String) -> Unit,
 | 
					    onPurse: (String) -> Unit,
 | 
				
			||||||
    onItem: (InventoryItemUio) -> Unit,
 | 
					    onItem: (InventoryItemUio) -> Unit,
 | 
				
			||||||
 | 
					    onAddItem: (String) -> Unit,
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
    Box(
 | 
					    Box(
 | 
				
			||||||
        modifier = modifier,
 | 
					        modifier = modifier,
 | 
				
			||||||
| 
						 | 
					@ -221,7 +251,7 @@ private fun CharacterDetailInventoryContent(
 | 
				
			||||||
                colors = LwaButtonColors(),
 | 
					                colors = LwaButtonColors(),
 | 
				
			||||||
                elevation = ButtonDefaults.elevation(4.dp),
 | 
					                elevation = ButtonDefaults.elevation(4.dp),
 | 
				
			||||||
                shape = CircleShape,
 | 
					                shape = CircleShape,
 | 
				
			||||||
                onClick = { },
 | 
					                onClick = { onAddItem(inventory.characterSheetId) },
 | 
				
			||||||
            ) {
 | 
					            ) {
 | 
				
			||||||
                Text(
 | 
					                Text(
 | 
				
			||||||
                    modifier = Modifier.padding(end = 4.dp),
 | 
					                    modifier = Modifier.padding(end = 4.dp),
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -42,7 +42,7 @@ class CharacterDetailInventoryFactory(
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        return combine(
 | 
					        return combine(
 | 
				
			||||||
            inventoryRepository.inventoryFlow(characterSheetId = characterSheetId),
 | 
					            inventoryRepository.inventoryFlow(characterSheetId = characterSheetId),
 | 
				
			||||||
            itemRepository.itemFlow,
 | 
					            itemRepository.itemFlow(),
 | 
				
			||||||
            filterFlow.map { it.unAccent() },
 | 
					            filterFlow.map { it.unAccent() },
 | 
				
			||||||
        ) { inventory, items, filter ->
 | 
					        ) { inventory, items, filter ->
 | 
				
			||||||
            convertToCharacterInventoryUio(
 | 
					            convertToCharacterInventoryUio(
 | 
				
			||||||
| 
						 | 
					@ -85,6 +85,13 @@ class CharacterDetailInventoryFactory(
 | 
				
			||||||
                        label = item.metadata.label,
 | 
					                        label = item.metadata.label,
 | 
				
			||||||
                        count = it.count,
 | 
					                        count = it.count,
 | 
				
			||||||
                        equipped = it.equipped,
 | 
					                        equipped = it.equipped,
 | 
				
			||||||
 | 
					                        tooltips = takeIf { item.metadata.description.isNotEmpty() }?.let {
 | 
				
			||||||
 | 
					                            InventoryItemUio.Tooltips(
 | 
				
			||||||
 | 
					                                label = item.metadata.label,
 | 
				
			||||||
 | 
					                                description = item.metadata.description,
 | 
				
			||||||
 | 
					                                image = item.metadata.image,
 | 
				
			||||||
 | 
					                            )
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
                    )
 | 
					                    )
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                ?.sortedWith(compareBy(Collator.getInstance()) { it.label })
 | 
					                ?.sortedWith(compareBy(Collator.getInstance()) { it.label })
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,20 +1,22 @@
 | 
				
			||||||
package com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.inventory.item
 | 
					package com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.inventory.item
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import androidx.compose.animation.AnimatedContent
 | 
					import androidx.compose.animation.AnimatedContent
 | 
				
			||||||
import androidx.compose.animation.AnimatedVisibility
 | 
					 | 
				
			||||||
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.animation.togetherWith
 | 
				
			||||||
 | 
					import androidx.compose.foundation.ExperimentalFoundationApi
 | 
				
			||||||
import androidx.compose.foundation.background
 | 
					import androidx.compose.foundation.background
 | 
				
			||||||
import androidx.compose.foundation.clickable
 | 
					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.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.offset
 | 
				
			||||||
import androidx.compose.foundation.layout.padding
 | 
					import androidx.compose.foundation.layout.padding
 | 
				
			||||||
 | 
					import androidx.compose.foundation.layout.size
 | 
				
			||||||
import androidx.compose.material.MaterialTheme
 | 
					import androidx.compose.material.MaterialTheme
 | 
				
			||||||
 | 
					import androidx.compose.material.Surface
 | 
				
			||||||
import androidx.compose.material.Text
 | 
					import androidx.compose.material.Text
 | 
				
			||||||
import androidx.compose.material.minimumInteractiveComponentSize
 | 
					import androidx.compose.material.minimumInteractiveComponentSize
 | 
				
			||||||
import androidx.compose.runtime.Composable
 | 
					import androidx.compose.runtime.Composable
 | 
				
			||||||
| 
						 | 
					@ -23,21 +25,35 @@ import androidx.compose.ui.Alignment
 | 
				
			||||||
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.graphics.Color
 | 
				
			||||||
 | 
					import androidx.compose.ui.graphics.FilterQuality
 | 
				
			||||||
 | 
					import androidx.compose.ui.layout.ContentScale
 | 
				
			||||||
import androidx.compose.ui.text.font.FontWeight
 | 
					import androidx.compose.ui.text.font.FontWeight
 | 
				
			||||||
import androidx.compose.ui.text.style.TextOverflow
 | 
					import androidx.compose.ui.text.style.TextOverflow
 | 
				
			||||||
import androidx.compose.ui.unit.Dp
 | 
					import androidx.compose.ui.unit.Dp
 | 
				
			||||||
import androidx.compose.ui.unit.dp
 | 
					import androidx.compose.ui.unit.dp
 | 
				
			||||||
 | 
					import com.pixelized.desktop.lwa.ui.composable.decoratedBox.DecoratedBox
 | 
				
			||||||
 | 
					import com.pixelized.desktop.lwa.ui.composable.image.DesaturatedAsyncImage
 | 
				
			||||||
 | 
					import com.pixelized.desktop.lwa.ui.composable.tooltip.TooltipLayout2
 | 
				
			||||||
import com.pixelized.desktop.lwa.ui.theme.lwa
 | 
					import com.pixelized.desktop.lwa.ui.theme.lwa
 | 
				
			||||||
import com.pixelized.desktop.lwa.utils.extention.ribbon
 | 
					import com.pixelized.desktop.lwa.utils.extention.ribbon
 | 
				
			||||||
 | 
					import com.pixelized.desktop.lwa.utils.rememberSaturationFilter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Stable
 | 
					@Stable
 | 
				
			||||||
data class InventoryItemUio(
 | 
					data class InventoryItemUio(
 | 
				
			||||||
    val inventoryId: String,
 | 
					    val inventoryId: String,
 | 
				
			||||||
    val itemId: String,
 | 
					    val itemId: String,
 | 
				
			||||||
    val label: String,
 | 
					    val label: String,
 | 
				
			||||||
    val count: Int,
 | 
					    val count: Float,
 | 
				
			||||||
    val equipped: Boolean,
 | 
					    val equipped: Boolean,
 | 
				
			||||||
)
 | 
					    val tooltips: Tooltips?,
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					    @Stable
 | 
				
			||||||
 | 
					    data class Tooltips(
 | 
				
			||||||
 | 
					        val label: String,
 | 
				
			||||||
 | 
					        val description: String,
 | 
				
			||||||
 | 
					        val image: String?,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Stable
 | 
					@Stable
 | 
				
			||||||
object GMCharacterPreviewDefault {
 | 
					object GMCharacterPreviewDefault {
 | 
				
			||||||
| 
						 | 
					@ -48,6 +64,7 @@ object GMCharacterPreviewDefault {
 | 
				
			||||||
    val spacing: Dp = 4.dp
 | 
					    val spacing: Dp = 4.dp
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@OptIn(ExperimentalFoundationApi::class)
 | 
				
			||||||
@Composable
 | 
					@Composable
 | 
				
			||||||
fun InventoryItem(
 | 
					fun InventoryItem(
 | 
				
			||||||
    modifier: Modifier = Modifier,
 | 
					    modifier: Modifier = Modifier,
 | 
				
			||||||
| 
						 | 
					@ -56,6 +73,49 @@ fun InventoryItem(
 | 
				
			||||||
    item: InventoryItemUio,
 | 
					    item: InventoryItemUio,
 | 
				
			||||||
    onClick: () -> Unit,
 | 
					    onClick: () -> Unit,
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
 | 
					    TooltipLayout2(
 | 
				
			||||||
 | 
					        delayMillis = 500,
 | 
				
			||||||
 | 
					        tips = item.tooltips,
 | 
				
			||||||
 | 
					        tooltip = { tooltips ->
 | 
				
			||||||
 | 
					            DecoratedBox {
 | 
				
			||||||
 | 
					                Surface {
 | 
				
			||||||
 | 
					                    Box(
 | 
				
			||||||
 | 
					                        modifier = Modifier.padding(all = 16.dp)
 | 
				
			||||||
 | 
					                    ) {
 | 
				
			||||||
 | 
					                        takeIf { tooltips.image?.isNotEmpty() == true }?.let {
 | 
				
			||||||
 | 
					                            DesaturatedAsyncImage(
 | 
				
			||||||
 | 
					                                modifier = Modifier
 | 
				
			||||||
 | 
					                                    .size(96.dp)
 | 
				
			||||||
 | 
					                                    .align(alignment = Alignment.TopEnd)
 | 
				
			||||||
 | 
					                                    .offset(x = 8.dp, y = (-8).dp),
 | 
				
			||||||
 | 
					                                colorFilter = rememberSaturationFilter(),
 | 
				
			||||||
 | 
					                                model = tooltips.image,
 | 
				
			||||||
 | 
					                                contentScale = ContentScale.Crop,
 | 
				
			||||||
 | 
					                                alignment = Alignment.TopCenter,
 | 
				
			||||||
 | 
					                                filterQuality = FilterQuality.High,
 | 
				
			||||||
 | 
					                                contentDescription = null,
 | 
				
			||||||
 | 
					                            )
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        Column(
 | 
				
			||||||
 | 
					                            modifier = modifier,
 | 
				
			||||||
 | 
					                            verticalArrangement = Arrangement.spacedBy(space = 8.dp)
 | 
				
			||||||
 | 
					                        ) {
 | 
				
			||||||
 | 
					                            Text(
 | 
				
			||||||
 | 
					                                style = MaterialTheme.typography.body2,
 | 
				
			||||||
 | 
					                                fontWeight = FontWeight.Bold,
 | 
				
			||||||
 | 
					                                text = tooltips.label,
 | 
				
			||||||
 | 
					                            )
 | 
				
			||||||
 | 
					                            Text(
 | 
				
			||||||
 | 
					                                style = MaterialTheme.typography.caption,
 | 
				
			||||||
 | 
					                                fontWeight = FontWeight.Light,
 | 
				
			||||||
 | 
					                                text = tooltips.description,
 | 
				
			||||||
 | 
					                            )
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        content = {
 | 
				
			||||||
            Row(
 | 
					            Row(
 | 
				
			||||||
                modifier = Modifier
 | 
					                modifier = Modifier
 | 
				
			||||||
                    .clip(shape = MaterialTheme.lwa.shapes.item)
 | 
					                    .clip(shape = MaterialTheme.lwa.shapes.item)
 | 
				
			||||||
| 
						 | 
					@ -81,23 +141,15 @@ fun InventoryItem(
 | 
				
			||||||
                    maxLines = 1,
 | 
					                    maxLines = 1,
 | 
				
			||||||
                    text = item.label,
 | 
					                    text = item.label,
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
        AnimatedVisibility(
 | 
					 | 
				
			||||||
            visible = item.count > 1,
 | 
					 | 
				
			||||||
            enter = fadeIn(),
 | 
					 | 
				
			||||||
            exit = fadeOut(),
 | 
					 | 
				
			||||||
        ) {
 | 
					 | 
				
			||||||
                AnimatedContent(
 | 
					                AnimatedContent(
 | 
				
			||||||
                targetState = item.count,
 | 
					 | 
				
			||||||
                transitionSpec = {
 | 
					 | 
				
			||||||
                    val prod = if (initialState < targetState) 1 else -1
 | 
					 | 
				
			||||||
                    val enter = fadeIn() + slideInVertically { -8 * prod }
 | 
					 | 
				
			||||||
                    val exit = fadeOut() + slideOutVertically { 8 * prod }
 | 
					 | 
				
			||||||
                    enter togetherWith exit using SizeTransform(clip = false)
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            ) {
 | 
					 | 
				
			||||||
                Text(
 | 
					 | 
				
			||||||
                    modifier = Modifier.alignByBaseline(),
 | 
					                    modifier = Modifier.alignByBaseline(),
 | 
				
			||||||
                    style = MaterialTheme.lwa.typography.base.body1,
 | 
					                    targetState = item.count,
 | 
				
			||||||
 | 
					                    transitionSpec = { fadeIn() togetherWith fadeOut() },
 | 
				
			||||||
 | 
					                ) {
 | 
				
			||||||
 | 
					                    when (it) {
 | 
				
			||||||
 | 
					                        0f, 1f -> Unit
 | 
				
			||||||
 | 
					                        else -> Text(
 | 
				
			||||||
 | 
					                            style = MaterialTheme.lwa.typography.base.caption,
 | 
				
			||||||
                            overflow = TextOverflow.Ellipsis,
 | 
					                            overflow = TextOverflow.Ellipsis,
 | 
				
			||||||
                            maxLines = 1,
 | 
					                            maxLines = 1,
 | 
				
			||||||
                            text = "x${it}",
 | 
					                            text = "x${it}",
 | 
				
			||||||
| 
						 | 
					@ -105,4 +157,6 @@ fun InventoryItem(
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -60,7 +60,7 @@ class GMItemViewModel(
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    val items: StateFlow<List<GMItemUio>> = combine(
 | 
					    val items: StateFlow<List<GMItemUio>> = combine(
 | 
				
			||||||
        itemRepository.itemFlow,
 | 
					        itemRepository.itemFlow(),
 | 
				
			||||||
        tagRepository.itemsTagFlow(),
 | 
					        tagRepository.itemsTagFlow(),
 | 
				
			||||||
        filter.valueFlow.map { it.unAccent() },
 | 
					        filter.valueFlow.map { it.unAccent() },
 | 
				
			||||||
        selectedTagId,
 | 
					        selectedTagId,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,8 +26,8 @@ class CharacterSheetJsonFactory(
 | 
				
			||||||
        val json = CharacterSheetJsonV1(
 | 
					        val json = CharacterSheetJsonV1(
 | 
				
			||||||
            id = sheet.id,
 | 
					            id = sheet.id,
 | 
				
			||||||
            name = sheet.name,
 | 
					            name = sheet.name,
 | 
				
			||||||
            portrait = sheet.portrait,
 | 
					            portrait = sheet.portrait?.takeIf { it.isNotBlank() },
 | 
				
			||||||
            thumbnail = sheet.thumbnail,
 | 
					            thumbnail = sheet.thumbnail?.takeIf { it.isNotBlank() },
 | 
				
			||||||
            level = sheet.level,
 | 
					            level = sheet.level,
 | 
				
			||||||
            shouldLevelUp = sheet.shouldLevelUp,
 | 
					            shouldLevelUp = sheet.shouldLevelUp,
 | 
				
			||||||
            strength = sheet.strength,
 | 
					            strength = sheet.strength,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,7 +14,7 @@ data class Inventory(
 | 
				
			||||||
    data class Item(
 | 
					    data class Item(
 | 
				
			||||||
        val inventoryId: String,
 | 
					        val inventoryId: String,
 | 
				
			||||||
        val itemId: String,
 | 
					        val itemId: String,
 | 
				
			||||||
        val count: Int,
 | 
					        val count: Float,
 | 
				
			||||||
        val equipped: Boolean,
 | 
					        val equipped: Boolean,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -20,7 +20,7 @@ data class InventoryJsonV1(
 | 
				
			||||||
    data class ItemJson(
 | 
					    data class ItemJson(
 | 
				
			||||||
        val inventoryId: String,
 | 
					        val inventoryId: String,
 | 
				
			||||||
        val itemId: String,
 | 
					        val itemId: String,
 | 
				
			||||||
        val count: Int,
 | 
					        val count: Float,
 | 
				
			||||||
        val equipped: Boolean?,
 | 
					        val equipped: Boolean?,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -30,8 +30,8 @@ class ItemJsonFactoryV1 {
 | 
				
			||||||
            metadata = ItemJsonV1.ItemMetadataJsonV1(
 | 
					            metadata = ItemJsonV1.ItemMetadataJsonV1(
 | 
				
			||||||
                name = item.metadata.label,
 | 
					                name = item.metadata.label,
 | 
				
			||||||
                description = item.metadata.description,
 | 
					                description = item.metadata.description,
 | 
				
			||||||
                image = item.metadata.image,
 | 
					                image = item.metadata.image?.takeIf { it.isNotBlank() },
 | 
				
			||||||
                thumbnail = item.metadata.thumbnail,
 | 
					                thumbnail = item.metadata.thumbnail?.takeIf { it.isNotBlank() },
 | 
				
			||||||
            ),
 | 
					            ),
 | 
				
			||||||
            options = ItemJsonV1.ItemOptionJsonV1(
 | 
					            options = ItemJsonV1.ItemOptionJsonV1(
 | 
				
			||||||
                stackable = item.options.stackable,
 | 
					                stackable = item.options.stackable,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue