Add client inventory sheet management.
This commit is contained in:
		
							parent
							
								
									8982bab22d
								
							
						
					
					
						commit
						05a376aea8
					
				
					 10 changed files with 325 additions and 118 deletions
				
			
		| 
						 | 
				
			
			@ -1,29 +1,26 @@
 | 
			
		|||
package com.pixelized.desktop.lwa.ui.screen.campaign.player.detail.inventory
 | 
			
		||||
 | 
			
		||||
import androidx.compose.animation.AnimatedContent
 | 
			
		||||
import androidx.compose.animation.AnimatedContentTransitionScope
 | 
			
		||||
import androidx.compose.animation.ContentTransform
 | 
			
		||||
import androidx.compose.animation.SizeTransform
 | 
			
		||||
import androidx.compose.animation.AnimatedVisibility
 | 
			
		||||
import androidx.compose.animation.fadeIn
 | 
			
		||||
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.Box
 | 
			
		||||
import androidx.compose.foundation.layout.Column
 | 
			
		||||
import androidx.compose.foundation.layout.PaddingValues
 | 
			
		||||
import androidx.compose.foundation.layout.Row
 | 
			
		||||
import androidx.compose.foundation.layout.fillMaxWidth
 | 
			
		||||
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.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.Text
 | 
			
		||||
import androidx.compose.material.icons.Icons
 | 
			
		||||
import androidx.compose.material.icons.filled.Add
 | 
			
		||||
import androidx.compose.runtime.Composable
 | 
			
		||||
import androidx.compose.runtime.Stable
 | 
			
		||||
import androidx.compose.runtime.State
 | 
			
		||||
| 
						 | 
				
			
			@ -31,45 +28,57 @@ import androidx.compose.runtime.collectAsState
 | 
			
		|||
import androidx.compose.runtime.rememberCoroutineScope
 | 
			
		||||
import androidx.compose.ui.Alignment
 | 
			
		||||
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 com.pixelized.desktop.lwa.LocalBlurController
 | 
			
		||||
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.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.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.utils.extention.plus
 | 
			
		||||
import kotlinx.coroutines.launch
 | 
			
		||||
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 lwacharactersheet.composeapp.generated.resources.character__inventory__add_to_inventory__action
 | 
			
		||||
import lwacharactersheet.composeapp.generated.resources.ic_cancel_24dp
 | 
			
		||||
import org.jetbrains.compose.resources.painterResource
 | 
			
		||||
import org.jetbrains.compose.resources.stringResource
 | 
			
		||||
import org.koin.compose.viewmodel.koinViewModel
 | 
			
		||||
 | 
			
		||||
@Stable
 | 
			
		||||
data class CharacterDetailInventoryUio(
 | 
			
		||||
    val characterSheetId: String,
 | 
			
		||||
    val filter: LwaTextFieldUio,
 | 
			
		||||
    val purse: PurseUio,
 | 
			
		||||
    val items: List<InventoryItemUio>,
 | 
			
		||||
) {
 | 
			
		||||
    @Stable
 | 
			
		||||
    data class PurseUio(
 | 
			
		||||
        val gold: Int,
 | 
			
		||||
        val silver: Int,
 | 
			
		||||
        val copper: Int,
 | 
			
		||||
    )
 | 
			
		||||
}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@Stable
 | 
			
		||||
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
 | 
			
		||||
fun CharacterDetailInventory(
 | 
			
		||||
    modifier: Modifier = Modifier,
 | 
			
		||||
    paddings: PaddingValues = CharacterDetailInventoryDefault.padding,
 | 
			
		||||
    spacing: Dp = CharacterDetailInventoryDefault.spacing,
 | 
			
		||||
    purseViewModel: PurseDialogViewModel = koinViewModel(),
 | 
			
		||||
    inventory: State<CharacterDetailInventoryUio?>,
 | 
			
		||||
) {
 | 
			
		||||
| 
						 | 
				
			
			@ -82,6 +91,7 @@ fun CharacterDetailInventory(
 | 
			
		|||
        else -> CharacterDetailInventoryContent(
 | 
			
		||||
            modifier = modifier,
 | 
			
		||||
            paddings = paddings,
 | 
			
		||||
            spacing = spacing,
 | 
			
		||||
            inventory = unWrap,
 | 
			
		||||
            onPurse = {
 | 
			
		||||
                blur.show()
 | 
			
		||||
| 
						 | 
				
			
			@ -118,119 +128,95 @@ fun CharacterDetailInventory(
 | 
			
		|||
private fun CharacterDetailInventoryContent(
 | 
			
		||||
    modifier: Modifier = Modifier,
 | 
			
		||||
    paddings: PaddingValues,
 | 
			
		||||
    spacing: Dp,
 | 
			
		||||
    inventory: CharacterDetailInventoryUio,
 | 
			
		||||
    onPurse: (String) -> Unit,
 | 
			
		||||
) {
 | 
			
		||||
    Column(
 | 
			
		||||
    Box(
 | 
			
		||||
        modifier = modifier,
 | 
			
		||||
    ) {
 | 
			
		||||
        LazyColumn(
 | 
			
		||||
            modifier = Modifier.weight(1f),
 | 
			
		||||
            contentPadding = paddings,
 | 
			
		||||
            modifier = Modifier.matchParentSize(),
 | 
			
		||||
            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 = inventory.items,
 | 
			
		||||
                key = { it.inventoryId },
 | 
			
		||||
            ) {
 | 
			
		||||
                InventoryItem(
 | 
			
		||||
                    modifier = Modifier.fillMaxWidth(),
 | 
			
		||||
                    modifier = Modifier
 | 
			
		||||
                        .animateItem()
 | 
			
		||||
                        .fillMaxWidth(),
 | 
			
		||||
                    item = it,
 | 
			
		||||
                    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(
 | 
			
		||||
            modifier = Modifier
 | 
			
		||||
                .clickable { onPurse() }
 | 
			
		||||
                .align(alignment = Alignment.BottomEnd)
 | 
			
		||||
                .padding(paddingValues = paddings),
 | 
			
		||||
            horizontalArrangement = Arrangement.spacedBy(space = 8.dp),
 | 
			
		||||
            horizontalArrangement = Arrangement.SpaceBetween,
 | 
			
		||||
        ) {
 | 
			
		||||
            Row(verticalAlignment = Alignment.Bottom) {
 | 
			
		||||
                Image(
 | 
			
		||||
                    painter = painterResource(Res.drawable.ic_gold_32px),
 | 
			
		||||
                    modifier = Modifier.padding(bottom = 2.dp).size(size = 16.dp),
 | 
			
		||||
            Button(
 | 
			
		||||
                colors = LwaButtonColors(),
 | 
			
		||||
                elevation = ButtonDefaults.elevation(4.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,
 | 
			
		||||
                )
 | 
			
		||||
                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.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.PurseUio
 | 
			
		||||
import com.pixelized.desktop.lwa.utils.extention.unAccent
 | 
			
		||||
import com.pixelized.shared.lwa.model.inventory.Inventory
 | 
			
		||||
import com.pixelized.shared.lwa.model.item.Item
 | 
			
		||||
import kotlinx.coroutines.CoroutineScope
 | 
			
		||||
import kotlinx.coroutines.flow.MutableStateFlow
 | 
			
		||||
import kotlinx.coroutines.flow.SharingStarted
 | 
			
		||||
import kotlinx.coroutines.flow.StateFlow
 | 
			
		||||
import kotlinx.coroutines.flow.combine
 | 
			
		||||
import kotlinx.coroutines.flow.map
 | 
			
		||||
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(
 | 
			
		||||
    private val inventoryRepository: InventoryRepository,
 | 
			
		||||
    private val itemRepository: ItemRepository,
 | 
			
		||||
) {
 | 
			
		||||
 | 
			
		||||
    fun convertToCharacterInventoryUioFlow(
 | 
			
		||||
    suspend fun convertToCharacterInventoryUioFlow(
 | 
			
		||||
        characterSheetId: String,
 | 
			
		||||
        scope: CoroutineScope,
 | 
			
		||||
        started: SharingStarted = SharingStarted.Eagerly,
 | 
			
		||||
        initialValue: () -> 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(
 | 
			
		||||
            inventoryRepository.inventoryFlow(characterSheetId = characterSheetId),
 | 
			
		||||
            itemRepository.itemFlow,
 | 
			
		||||
        ) { inventory, items ->
 | 
			
		||||
            filterFlow.map { it.unAccent() },
 | 
			
		||||
        ) { inventory, items, filter ->
 | 
			
		||||
            convertToCharacterInventoryUio(
 | 
			
		||||
                characterSheetId = characterSheetId,
 | 
			
		||||
                filter = filterField,
 | 
			
		||||
                purse = inventory?.purse,
 | 
			
		||||
                inventory = inventory?.items,
 | 
			
		||||
                items = items,
 | 
			
		||||
                items = items.filterValues { it.metadata.name.unAccent().contains(filter, true) },
 | 
			
		||||
            )
 | 
			
		||||
        }.stateIn(
 | 
			
		||||
            scope = scope,
 | 
			
		||||
| 
						 | 
				
			
			@ -41,6 +61,7 @@ class CharacterDetailInventoryFactory(
 | 
			
		|||
 | 
			
		||||
    private suspend fun convertToCharacterInventoryUio(
 | 
			
		||||
        characterSheetId: String?,
 | 
			
		||||
        filter: LwaTextFieldUio,
 | 
			
		||||
        purse: Inventory.Purse?,
 | 
			
		||||
        inventory: List<Inventory.Item>?,
 | 
			
		||||
        items: Map<String, Item>,
 | 
			
		||||
| 
						 | 
				
			
			@ -49,19 +70,22 @@ class CharacterDetailInventoryFactory(
 | 
			
		|||
 | 
			
		||||
        return CharacterDetailInventoryUio(
 | 
			
		||||
            characterSheetId = characterSheetId,
 | 
			
		||||
            purse = CharacterDetailInventoryUio.PurseUio(
 | 
			
		||||
            purse = PurseUio(
 | 
			
		||||
                gold = purse?.gold ?: 0,
 | 
			
		||||
                silver = purse?.silver ?: 0,
 | 
			
		||||
                copper = purse?.copper ?: 0,
 | 
			
		||||
            ),
 | 
			
		||||
            filter = filter,
 | 
			
		||||
            items = inventory
 | 
			
		||||
                ?.mapNotNull {
 | 
			
		||||
                    val label = items[it.itemId]?.metadata?.name ?: return@mapNotNull null
 | 
			
		||||
                    InventoryItemUio(
 | 
			
		||||
                        inventoryId = it.inventoryId,
 | 
			
		||||
                        label = label,
 | 
			
		||||
                        equipped = it.equipped,
 | 
			
		||||
                    )
 | 
			
		||||
                }
 | 
			
		||||
                ?.sortedWith(compareBy(Collator.getInstance()) { it.label })
 | 
			
		||||
                ?: emptyList()
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,13 +12,16 @@ import androidx.compose.runtime.Composable
 | 
			
		|||
import androidx.compose.runtime.Stable
 | 
			
		||||
import androidx.compose.ui.Modifier
 | 
			
		||||
import androidx.compose.ui.draw.clip
 | 
			
		||||
import androidx.compose.ui.graphics.Color
 | 
			
		||||
import androidx.compose.ui.unit.dp
 | 
			
		||||
import com.pixelized.desktop.lwa.ui.theme.lwa
 | 
			
		||||
import com.pixelized.desktop.lwa.utils.extention.ribbon
 | 
			
		||||
 | 
			
		||||
@Stable
 | 
			
		||||
data class InventoryItemUio(
 | 
			
		||||
    val inventoryId: String,
 | 
			
		||||
    val label: String,
 | 
			
		||||
    val equipped: Boolean,
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@Stable
 | 
			
		||||
| 
						 | 
				
			
			@ -36,9 +39,15 @@ fun InventoryItem(
 | 
			
		|||
) {
 | 
			
		||||
    Row(
 | 
			
		||||
        modifier = Modifier
 | 
			
		||||
            .clip(shape = MaterialTheme.lwa.shapes.gameMaster)
 | 
			
		||||
            .clip(shape = MaterialTheme.lwa.shapes.item)
 | 
			
		||||
            .clickable(onClick = onClick)
 | 
			
		||||
            .background(color = MaterialTheme.lwa.colorScheme.elevated.base2dp)
 | 
			
		||||
            .ribbon(
 | 
			
		||||
                color = when (item.equipped) {
 | 
			
		||||
                    true -> MaterialTheme.lwa.colorScheme.base.primary
 | 
			
		||||
                    else -> Color.Transparent
 | 
			
		||||
                }
 | 
			
		||||
            )
 | 
			
		||||
            .minimumInteractiveComponentSize()
 | 
			
		||||
            .padding(paddingValues = padding)
 | 
			
		||||
            .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(),
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue