Naive implementation to link inventory to firebase.
This commit is contained in:
		
							parent
							
								
									531e4bea98
								
							
						
					
					
						commit
						464aea6fd6
					
				
					 26 changed files with 766 additions and 690 deletions
				
			
		| 
						 | 
					@ -11,7 +11,7 @@ import com.pixelized.rplexicon.data.repository.character.AlterationRepository
 | 
				
			||||||
import com.pixelized.rplexicon.data.repository.character.CharacterSheetRepository
 | 
					import com.pixelized.rplexicon.data.repository.character.CharacterSheetRepository
 | 
				
			||||||
import com.pixelized.rplexicon.data.repository.character.DescriptionRepository
 | 
					import com.pixelized.rplexicon.data.repository.character.DescriptionRepository
 | 
				
			||||||
import com.pixelized.rplexicon.data.repository.character.EquipmentRepository
 | 
					import com.pixelized.rplexicon.data.repository.character.EquipmentRepository
 | 
				
			||||||
import com.pixelized.rplexicon.data.repository.character.InventoryRepository
 | 
					import com.pixelized.rplexicon.data.repository.character.ItemsRepository
 | 
				
			||||||
import com.pixelized.rplexicon.data.repository.character.ObjectActionRepository
 | 
					import com.pixelized.rplexicon.data.repository.character.ObjectActionRepository
 | 
				
			||||||
import com.pixelized.rplexicon.data.repository.character.SkillRepository
 | 
					import com.pixelized.rplexicon.data.repository.character.SkillRepository
 | 
				
			||||||
import com.pixelized.rplexicon.data.repository.character.SpellRepository
 | 
					import com.pixelized.rplexicon.data.repository.character.SpellRepository
 | 
				
			||||||
| 
						 | 
					@ -45,7 +45,7 @@ class LauncherViewModel @Inject constructor(
 | 
				
			||||||
    spellRepository: SpellRepository,
 | 
					    spellRepository: SpellRepository,
 | 
				
			||||||
    skillRepository: SkillRepository,
 | 
					    skillRepository: SkillRepository,
 | 
				
			||||||
    descriptionRepository: DescriptionRepository,
 | 
					    descriptionRepository: DescriptionRepository,
 | 
				
			||||||
    inventoryRepository: InventoryRepository,
 | 
					    itemsRepository: ItemsRepository,
 | 
				
			||||||
    equipmentRepository: EquipmentRepository,
 | 
					    equipmentRepository: EquipmentRepository,
 | 
				
			||||||
    removeConRepository: RemoteConfigRepository // Unused but injected to initialize it.
 | 
					    removeConRepository: RemoteConfigRepository // Unused but injected to initialize it.
 | 
				
			||||||
) : ViewModel() {
 | 
					) : ViewModel() {
 | 
				
			||||||
| 
						 | 
					@ -108,7 +108,7 @@ class LauncherViewModel @Inject constructor(
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            val inventory = async {
 | 
					            val inventory = async {
 | 
				
			||||||
                try {
 | 
					                try {
 | 
				
			||||||
                    inventoryRepository.fetchInventory()
 | 
					                    itemsRepository.fetchItems()
 | 
				
			||||||
                } catch (exception: Exception) {
 | 
					                } catch (exception: Exception) {
 | 
				
			||||||
                    Log.e(TAG, exception.message, exception)
 | 
					                    Log.e(TAG, exception.message, exception)
 | 
				
			||||||
                    _error.emit(FetchErrorUio.Structure(type = Type.INVENTORY))
 | 
					                    _error.emit(FetchErrorUio.Structure(type = Type.INVENTORY))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,53 +0,0 @@
 | 
				
			||||||
package com.pixelized.rplexicon.data.model
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
data class Inventory(
 | 
					 | 
				
			||||||
    val items: List<Item>,
 | 
					 | 
				
			||||||
) {
 | 
					 | 
				
			||||||
    class Builder(
 | 
					 | 
				
			||||||
        val items: MutableList<Item.Builder> = mutableListOf(),
 | 
					 | 
				
			||||||
    ) {
 | 
					 | 
				
			||||||
        fun build(): Inventory = Inventory(
 | 
					 | 
				
			||||||
            items = items.map { it.build() }
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        fun find(container: String): Item.Builder? {
 | 
					 | 
				
			||||||
            return items.localFind { it.find(name = container) }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    data class Item(
 | 
					 | 
				
			||||||
        val name: String,
 | 
					 | 
				
			||||||
        val amount: String?,
 | 
					 | 
				
			||||||
        val items: List<Item>,
 | 
					 | 
				
			||||||
    ) {
 | 
					 | 
				
			||||||
        class Builder(
 | 
					 | 
				
			||||||
            var name: String,
 | 
					 | 
				
			||||||
            var amount: String? = null,
 | 
					 | 
				
			||||||
            val items: MutableList<Builder> = mutableListOf(),
 | 
					 | 
				
			||||||
        ) {
 | 
					 | 
				
			||||||
            fun build(): Item = Item(
 | 
					 | 
				
			||||||
                name = name,
 | 
					 | 
				
			||||||
                amount = amount,
 | 
					 | 
				
			||||||
                items = items.map { it.build() }
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            fun find(name: String): Builder? {
 | 
					 | 
				
			||||||
                return when (this.name) {
 | 
					 | 
				
			||||||
                    name -> this
 | 
					 | 
				
			||||||
                    else -> items.localFind { it.find(name = name) }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
private inline fun <T> Iterable<T>.localFind(predicate: (T) -> T?): T? {
 | 
					 | 
				
			||||||
    var single: T? = null
 | 
					 | 
				
			||||||
    for (element in this) {
 | 
					 | 
				
			||||||
        single = predicate(element)
 | 
					 | 
				
			||||||
        if (single != null) {
 | 
					 | 
				
			||||||
            break
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return single
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,12 @@
 | 
				
			||||||
 | 
					package com.pixelized.rplexicon.data.model.item
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.pixelized.rplexicon.data.model.roll.Throw
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					data class Item(
 | 
				
			||||||
 | 
					    val id: String,
 | 
				
			||||||
 | 
					    val prefix: String?,
 | 
				
			||||||
 | 
					    val name: String,
 | 
				
			||||||
 | 
					    val isContainer: Boolean,
 | 
				
			||||||
 | 
					    val effect: Throw?,
 | 
				
			||||||
 | 
					    val icon: Any?,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,17 @@
 | 
				
			||||||
 | 
					package com.pixelized.rplexicon.data.network
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import androidx.annotation.Keep
 | 
				
			||||||
 | 
					import com.google.firebase.database.IgnoreExtraProperties
 | 
				
			||||||
 | 
					import com.google.firebase.database.PropertyName
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Keep
 | 
				
			||||||
 | 
					@IgnoreExtraProperties
 | 
				
			||||||
 | 
					class CharacterInventoryFire(
 | 
				
			||||||
 | 
					    @get:PropertyName(INVENTORIES)
 | 
				
			||||||
 | 
					    @set:PropertyName(INVENTORIES)
 | 
				
			||||||
 | 
					    var inventories: Map<String, List<ItemDto>> = emptyMap(),
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					    companion object {
 | 
				
			||||||
 | 
					        const val INVENTORIES = "Inventories"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -25,7 +25,7 @@ data class CharacterSheetFire(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @get:PropertyName(ALTERATIONS)
 | 
					    @get:PropertyName(ALTERATIONS)
 | 
				
			||||||
    @set:PropertyName(ALTERATIONS)
 | 
					    @set:PropertyName(ALTERATIONS)
 | 
				
			||||||
    var alterations : Map<String, Boolean> = emptyMap()
 | 
					    var alterations: Map<String, Boolean> = emptyMap(),
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
    @Keep
 | 
					    @Keep
 | 
				
			||||||
    @IgnoreExtraProperties
 | 
					    @IgnoreExtraProperties
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,6 +12,6 @@ data class CharacterSheetFireMap(
 | 
				
			||||||
    var characters: Map<String, CharacterSheetFire> = emptyMap(),
 | 
					    var characters: Map<String, CharacterSheetFire> = emptyMap(),
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
    companion object {
 | 
					    companion object {
 | 
				
			||||||
        private const val CHARACTERS = "Characters"
 | 
					        const val CHARACTERS = "Characters"
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,27 @@
 | 
				
			||||||
 | 
					package com.pixelized.rplexicon.data.network
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import androidx.annotation.Keep
 | 
				
			||||||
 | 
					import com.google.firebase.database.IgnoreExtraProperties
 | 
				
			||||||
 | 
					import com.google.firebase.database.PropertyName
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@Keep
 | 
				
			||||||
 | 
					@IgnoreExtraProperties
 | 
				
			||||||
 | 
					data class ItemDto(
 | 
				
			||||||
 | 
					    @get:PropertyName(ID)
 | 
				
			||||||
 | 
					    @set:PropertyName(ID)
 | 
				
			||||||
 | 
					    var id: String? = null,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @get:PropertyName(AMOUNT)
 | 
				
			||||||
 | 
					    @set:PropertyName(AMOUNT)
 | 
				
			||||||
 | 
					    var amount: Int? = null,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @get:PropertyName(CHILDREN)
 | 
				
			||||||
 | 
					    @set:PropertyName(CHILDREN)
 | 
				
			||||||
 | 
					    var children: List<ItemDto> = emptyList(),
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					    companion object {
 | 
				
			||||||
 | 
					        private const val ID = "id"
 | 
				
			||||||
 | 
					        private const val AMOUNT = "amount"
 | 
				
			||||||
 | 
					        private const val CHILDREN = "children"
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,55 +0,0 @@
 | 
				
			||||||
package com.pixelized.rplexicon.data.parser.inventory
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.google.api.services.sheets.v4.model.ValueRange
 | 
					 | 
				
			||||||
import com.pixelized.rplexicon.data.model.Inventory
 | 
					 | 
				
			||||||
import com.pixelized.rplexicon.data.parser.column
 | 
					 | 
				
			||||||
import com.pixelized.rplexicon.data.parser.parserScope
 | 
					 | 
				
			||||||
import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
 | 
					 | 
				
			||||||
import javax.inject.Inject
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class InventoryParser @Inject constructor() {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Throws(IncompatibleSheetStructure::class)
 | 
					 | 
				
			||||||
    fun parse(sheet: ValueRange): Map<String, Inventory> = parserScope {
 | 
					 | 
				
			||||||
        val inventories = hashMapOf<String, Inventory.Builder>()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        sheet.forEachRowIndexed { index, row ->
 | 
					 | 
				
			||||||
            when {
 | 
					 | 
				
			||||||
                index == 0 -> updateStructure(row = row, columns = COLUMNS)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                row.isNotEmpty() -> {
 | 
					 | 
				
			||||||
                    val character = row[0]?.toItem()
 | 
					 | 
				
			||||||
                    val container = row.parse(column = CONTAINER)
 | 
					 | 
				
			||||||
                    val name = row.parse(column = NAME)
 | 
					 | 
				
			||||||
                    val amount = row.parse(column = AMOUNT)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    if (character != null && name != null) {
 | 
					 | 
				
			||||||
                        // retrieve the character inventory
 | 
					 | 
				
			||||||
                        val inventory = inventories.getOrPut(character) { Inventory.Builder() }
 | 
					 | 
				
			||||||
                        // make a item builder
 | 
					 | 
				
			||||||
                        val item = Inventory.Item.Builder(
 | 
					 | 
				
			||||||
                            name = name,
 | 
					 | 
				
			||||||
                            amount = amount,
 | 
					 | 
				
			||||||
                        )
 | 
					 | 
				
			||||||
                        // add the item to its container or by default to the inventory.
 | 
					 | 
				
			||||||
                        val isAdded = container?.let {
 | 
					 | 
				
			||||||
                            inventory.find(container = it)?.items?.add(item)
 | 
					 | 
				
			||||||
                        } ?: false
 | 
					 | 
				
			||||||
                        if (isAdded.not()) {
 | 
					 | 
				
			||||||
                            inventory.items.add(item)
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return@parserScope inventories.mapValues { entry -> entry.value.build() }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    companion object {
 | 
					 | 
				
			||||||
        private val CONTAINER = column("Contenant")
 | 
					 | 
				
			||||||
        private val NAME = column("Name")
 | 
					 | 
				
			||||||
        private val AMOUNT = column("Quantité")
 | 
					 | 
				
			||||||
        private val COLUMNS = listOf(CONTAINER, NAME, AMOUNT)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,56 @@
 | 
				
			||||||
 | 
					package com.pixelized.rplexicon.data.parser.inventory
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.api.services.sheets.v4.model.ValueRange
 | 
				
			||||||
 | 
					import com.pixelized.rplexicon.data.model.item.Item
 | 
				
			||||||
 | 
					import com.pixelized.rplexicon.data.parser.column
 | 
				
			||||||
 | 
					import com.pixelized.rplexicon.data.parser.parserScope
 | 
				
			||||||
 | 
					import com.pixelized.rplexicon.data.parser.roll.ThrowParser
 | 
				
			||||||
 | 
					import com.pixelized.rplexicon.utilitary.exceptions.IncompatibleSheetStructure
 | 
				
			||||||
 | 
					import com.pixelized.rplexicon.utilitary.extentions.string.BaldurGageImageCache
 | 
				
			||||||
 | 
					import javax.inject.Inject
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ItemLexiconParser @Inject constructor(
 | 
				
			||||||
 | 
					    private val throwParser: ThrowParser,
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Throws(IncompatibleSheetStructure::class)
 | 
				
			||||||
 | 
					    fun parse(sheet: ValueRange): Map<String, Item> = parserScope {
 | 
				
			||||||
 | 
					        val inventories = hashMapOf<String, Item>()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        sheet.forEachRowIndexed { index, row ->
 | 
				
			||||||
 | 
					            when {
 | 
				
			||||||
 | 
					                index == 0 -> updateStructure(row = row, columns = COLUMNS)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                row.isNotEmpty() -> {
 | 
				
			||||||
 | 
					                    val container = row.parseBool(column = CONTAINER) ?: false
 | 
				
			||||||
 | 
					                    val id = row.parse(column = ID)
 | 
				
			||||||
 | 
					                    val name = row.parse(column = NAME)
 | 
				
			||||||
 | 
					                    if (id != null && name != null) {
 | 
				
			||||||
 | 
					                        val item = Item(
 | 
				
			||||||
 | 
					                            id = id,
 | 
				
			||||||
 | 
					                            prefix = row.parse(column = PREFIX),
 | 
				
			||||||
 | 
					                            name = name,
 | 
				
			||||||
 | 
					                            isContainer = container,
 | 
				
			||||||
 | 
					                            effect = throwParser.parse(value = row.parse(column = EFFECT)),
 | 
				
			||||||
 | 
					                            icon = BaldurGageImageCache.cache(url = row.parseUri(column = ICON)),
 | 
				
			||||||
 | 
					                        )
 | 
				
			||||||
 | 
					                        inventories[item.id] = item
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return@parserScope inventories
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    companion object {
 | 
				
			||||||
 | 
					        private val ID = column("Id")
 | 
				
			||||||
 | 
					        private val PREFIX = column("Préfix")
 | 
				
			||||||
 | 
					        private val NAME = column("Nom")
 | 
				
			||||||
 | 
					        private val CONTAINER = column("Contenant")
 | 
				
			||||||
 | 
					        private val EFFECT = column("Effet")
 | 
				
			||||||
 | 
					        private val ICON = column("Icone")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private val COLUMNS = listOf(ID, PREFIX, NAME, CONTAINER, EFFECT, ICON)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -19,9 +19,9 @@ object CharacterBinder {
 | 
				
			||||||
    const val MAGIC = "Magies"
 | 
					    const val MAGIC = "Magies"
 | 
				
			||||||
    const val SKILL = "Capacités"
 | 
					    const val SKILL = "Capacités"
 | 
				
			||||||
    const val MAGIC_LEXICON = "Lexique magique"
 | 
					    const val MAGIC_LEXICON = "Lexique magique"
 | 
				
			||||||
 | 
					    const val ITEMS_LEXICON = "Lexique des objets"
 | 
				
			||||||
    const val ALTERATION = "Altérations"
 | 
					    const val ALTERATION = "Altérations"
 | 
				
			||||||
    const val DESCRIPTION = "Descriptions"
 | 
					    const val DESCRIPTION = "Descriptions"
 | 
				
			||||||
    const val INVENTORY = "Inventaires"
 | 
					 | 
				
			||||||
    const val EQUIPMENT = "Équipements"
 | 
					    const val EQUIPMENT = "Équipements"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
package com.pixelized.rplexicon.data.repository.character
 | 
					package com.pixelized.rplexicon.data.repository.character
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.pixelized.rplexicon.data.model.Inventory
 | 
					import com.pixelized.rplexicon.data.model.item.Item
 | 
				
			||||||
import com.pixelized.rplexicon.data.parser.inventory.InventoryParser
 | 
					import com.pixelized.rplexicon.data.parser.inventory.ItemLexiconParser
 | 
				
			||||||
import com.pixelized.rplexicon.data.repository.CharacterBinder
 | 
					import com.pixelized.rplexicon.data.repository.CharacterBinder
 | 
				
			||||||
import com.pixelized.rplexicon.data.repository.GoogleSheetServiceRepository
 | 
					import com.pixelized.rplexicon.data.repository.GoogleSheetServiceRepository
 | 
				
			||||||
import com.pixelized.rplexicon.utilitary.Update
 | 
					import com.pixelized.rplexicon.utilitary.Update
 | 
				
			||||||
| 
						 | 
					@ -12,23 +12,21 @@ import javax.inject.Inject
 | 
				
			||||||
import javax.inject.Singleton
 | 
					import javax.inject.Singleton
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Singleton
 | 
					@Singleton
 | 
				
			||||||
class InventoryRepository @Inject constructor(
 | 
					class ItemsRepository @Inject constructor(
 | 
				
			||||||
    private val googleRepository: GoogleSheetServiceRepository,
 | 
					    private val googleRepository: GoogleSheetServiceRepository,
 | 
				
			||||||
    private val inventoryParser: InventoryParser,
 | 
					    private val itemLexiconParser: ItemLexiconParser,
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
    private val _data = MutableStateFlow<Map<String, Inventory>>(emptyMap())
 | 
					    private val _data = MutableStateFlow<Map<String, Item>>(emptyMap())
 | 
				
			||||||
    val data: StateFlow<Map<String, Inventory>> get() = _data
 | 
					    val data: StateFlow<Map<String, Item>> get() = _data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var lastSuccessFullUpdate: Update = Update.INITIAL
 | 
					    var lastSuccessFullUpdate: Update = Update.INITIAL
 | 
				
			||||||
        private set
 | 
					        private set
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fun find(name: String?): Inventory? = _data.value[name]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Throws(IncompatibleSheetStructure::class, Exception::class)
 | 
					    @Throws(IncompatibleSheetStructure::class, Exception::class)
 | 
				
			||||||
    suspend fun fetchInventory() {
 | 
					    suspend fun fetchItems() {
 | 
				
			||||||
        googleRepository.fetch { sheet ->
 | 
					        googleRepository.fetch { sheet ->
 | 
				
			||||||
            val request = sheet.get(CharacterBinder.ID, CharacterBinder.INVENTORY)
 | 
					            val request = sheet.get(CharacterBinder.ID, CharacterBinder.ITEMS_LEXICON)
 | 
				
			||||||
            val data = inventoryParser.parse(sheet = request.execute())
 | 
					            val data = itemLexiconParser.parse(sheet = request.execute())
 | 
				
			||||||
            _data.tryEmit(data)
 | 
					            _data.tryEmit(data)
 | 
				
			||||||
            lastSuccessFullUpdate = Update.currentTime()
 | 
					            lastSuccessFullUpdate = Update.currentTime()
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -9,11 +9,12 @@ import com.google.firebase.database.ktx.database
 | 
				
			||||||
import com.google.firebase.ktx.Firebase
 | 
					import com.google.firebase.ktx.Firebase
 | 
				
			||||||
import com.pixelized.rplexicon.R
 | 
					import com.pixelized.rplexicon.R
 | 
				
			||||||
import com.pixelized.rplexicon.data.model.alteration.AlterationStatus
 | 
					import com.pixelized.rplexicon.data.model.alteration.AlterationStatus
 | 
				
			||||||
 | 
					import com.pixelized.rplexicon.data.network.CharacterInventoryFire
 | 
				
			||||||
import com.pixelized.rplexicon.data.network.CharacterSheetFire
 | 
					import com.pixelized.rplexicon.data.network.CharacterSheetFire
 | 
				
			||||||
import com.pixelized.rplexicon.data.network.CharacterSheetFireMap
 | 
					import com.pixelized.rplexicon.data.network.CharacterSheetFireMap
 | 
				
			||||||
 | 
					import com.pixelized.rplexicon.data.network.ItemDto
 | 
				
			||||||
import com.pixelized.rplexicon.data.network.NetworkThrow
 | 
					import com.pixelized.rplexicon.data.network.NetworkThrow
 | 
				
			||||||
import com.pixelized.rplexicon.data.network.NetworkThrowMap
 | 
					import com.pixelized.rplexicon.data.network.NetworkThrowMap
 | 
				
			||||||
import com.pixelized.rplexicon.data.network.NetworkThrowMap.Companion.CHARACTERS_THROWS
 | 
					 | 
				
			||||||
import kotlinx.coroutines.CoroutineScope
 | 
					import kotlinx.coroutines.CoroutineScope
 | 
				
			||||||
import kotlinx.coroutines.Dispatchers
 | 
					import kotlinx.coroutines.Dispatchers
 | 
				
			||||||
import kotlinx.coroutines.Job
 | 
					import kotlinx.coroutines.Job
 | 
				
			||||||
| 
						 | 
					@ -74,6 +75,38 @@ class RealtimeDatabaseRepository @Inject constructor(
 | 
				
			||||||
        initialValue = emptyMap()
 | 
					        initialValue = emptyMap()
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private val inventoryFire: StateFlow<Map<String, List<ItemDto>>> = callbackFlow {
 | 
				
			||||||
 | 
					        // reference to the node
 | 
				
			||||||
 | 
					        val reference = database.getReference("/")
 | 
				
			||||||
 | 
					        // build a register the callback
 | 
				
			||||||
 | 
					        val listener = reference.addValueEventListener(object : ValueEventListener {
 | 
				
			||||||
 | 
					            override fun onDataChange(dataSnapshot: DataSnapshot) {
 | 
				
			||||||
 | 
					                val value = try {
 | 
				
			||||||
 | 
					                    dataSnapshot.getValue(CharacterInventoryFire::class.java)
 | 
				
			||||||
 | 
					                } catch (exception: Exception) {
 | 
				
			||||||
 | 
					                    Log.e(TAG, "Failed to parse value.", exception)
 | 
				
			||||||
 | 
					                    _error.tryEmit(exception)
 | 
				
			||||||
 | 
					                    null
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if (value != null) {
 | 
				
			||||||
 | 
					                    trySend(value.inventories)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            override fun onCancelled(error: DatabaseError) {
 | 
				
			||||||
 | 
					                Log.e(TAG, "Failed to read value.", error.toException())
 | 
				
			||||||
 | 
					                cancel()
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        awaitClose {
 | 
				
			||||||
 | 
					            reference.removeEventListener(listener)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }.stateIn(
 | 
				
			||||||
 | 
					        scope = CoroutineScope(Dispatchers.Default + Job()),
 | 
				
			||||||
 | 
					        started = SharingStarted.Lazily,
 | 
				
			||||||
 | 
					        initialValue = emptyMap()
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private val networkThrows = callbackFlow {
 | 
					    private val networkThrows = callbackFlow {
 | 
				
			||||||
        // reference to the node
 | 
					        // reference to the node
 | 
				
			||||||
        val reference = database.getReference("/")
 | 
					        val reference = database.getReference("/")
 | 
				
			||||||
| 
						 | 
					@ -133,6 +166,9 @@ class RealtimeDatabaseRepository @Inject constructor(
 | 
				
			||||||
    fun getCharacter(character: String): Flow<CharacterSheetFire> =
 | 
					    fun getCharacter(character: String): Flow<CharacterSheetFire> =
 | 
				
			||||||
        characterFireSheet.mapNotNull { it[character] }
 | 
					        characterFireSheet.mapNotNull { it[character] }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fun getInventory(character: String): Flow<List<ItemDto>> =
 | 
				
			||||||
 | 
					        inventoryFire.map { it[character] ?: emptyList() }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fun getThrows(): Flow<NetworkThrowMap> =
 | 
					    fun getThrows(): Flow<NetworkThrowMap> =
 | 
				
			||||||
        networkThrows
 | 
					        networkThrows
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -193,6 +229,13 @@ class RealtimeDatabaseRepository @Inject constructor(
 | 
				
			||||||
        reference.setValue(value.value)
 | 
					        reference.setValue(value.value)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fun setInventory(character: String?, inventory: List<ItemDto>) {
 | 
				
			||||||
 | 
					        character?.let {
 | 
				
			||||||
 | 
					            val reference = database.getReference("$PATH_INVENTORY/")
 | 
				
			||||||
 | 
					            reference.updateChildren(mapOf(it to inventory))
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fun sendThrow(character: String?, throws: NetworkThrow) {
 | 
					    fun sendThrow(character: String?, throws: NetworkThrow) {
 | 
				
			||||||
        character?.let {
 | 
					        character?.let {
 | 
				
			||||||
            val reference = database.getReference("$PATH_THROWS/")
 | 
					            val reference = database.getReference("$PATH_THROWS/")
 | 
				
			||||||
| 
						 | 
					@ -202,7 +245,8 @@ class RealtimeDatabaseRepository @Inject constructor(
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    companion object {
 | 
					    companion object {
 | 
				
			||||||
        private const val TAG = "FirebaseRepository"
 | 
					        private const val TAG = "FirebaseRepository"
 | 
				
			||||||
        private const val PATH_CHARACTERS = "Characters"
 | 
					        private const val PATH_CHARACTERS = CharacterSheetFireMap.CHARACTERS
 | 
				
			||||||
        private const val PATH_THROWS = CHARACTERS_THROWS
 | 
					        private const val PATH_THROWS = NetworkThrowMap.CHARACTERS_THROWS
 | 
				
			||||||
 | 
					        private const val PATH_INVENTORY = CharacterInventoryFire.INVENTORIES
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -71,6 +71,8 @@ import com.pixelized.rplexicon.ui.screens.character.CharacterTabUio.Proficiency
 | 
				
			||||||
import com.pixelized.rplexicon.ui.screens.character.composable.character.CharacterSheetHeader
 | 
					import com.pixelized.rplexicon.ui.screens.character.composable.character.CharacterSheetHeader
 | 
				
			||||||
import com.pixelized.rplexicon.ui.screens.character.composable.character.CharacterSheetHeaderUio
 | 
					import com.pixelized.rplexicon.ui.screens.character.composable.character.CharacterSheetHeaderUio
 | 
				
			||||||
import com.pixelized.rplexicon.ui.screens.character.composable.character.ResourcePointUio
 | 
					import com.pixelized.rplexicon.ui.screens.character.composable.character.ResourcePointUio
 | 
				
			||||||
 | 
					import com.pixelized.rplexicon.ui.screens.character.composable.chooser.SpellLevelChooser
 | 
				
			||||||
 | 
					import com.pixelized.rplexicon.ui.screens.character.composable.chooser.SpellLevelChooserPreview
 | 
				
			||||||
import com.pixelized.rplexicon.ui.screens.character.composable.preview.rememberCharacterHeaderStatePreview
 | 
					import com.pixelized.rplexicon.ui.screens.character.composable.preview.rememberCharacterHeaderStatePreview
 | 
				
			||||||
import com.pixelized.rplexicon.ui.screens.character.pages.actions.ActionPage
 | 
					import com.pixelized.rplexicon.ui.screens.character.pages.actions.ActionPage
 | 
				
			||||||
import com.pixelized.rplexicon.ui.screens.character.pages.actions.ActionPagePreview
 | 
					import com.pixelized.rplexicon.ui.screens.character.pages.actions.ActionPagePreview
 | 
				
			||||||
| 
						 | 
					@ -82,8 +84,6 @@ import com.pixelized.rplexicon.ui.screens.character.pages.actions.SpellsViewMode
 | 
				
			||||||
import com.pixelized.rplexicon.ui.screens.character.pages.alteration.AlterationPage
 | 
					import com.pixelized.rplexicon.ui.screens.character.pages.alteration.AlterationPage
 | 
				
			||||||
import com.pixelized.rplexicon.ui.screens.character.pages.alteration.AlterationPagePreview
 | 
					import com.pixelized.rplexicon.ui.screens.character.pages.alteration.AlterationPagePreview
 | 
				
			||||||
import com.pixelized.rplexicon.ui.screens.character.pages.alteration.AlterationViewModel
 | 
					import com.pixelized.rplexicon.ui.screens.character.pages.alteration.AlterationViewModel
 | 
				
			||||||
import com.pixelized.rplexicon.ui.screens.character.composable.chooser.SpellLevelChooser
 | 
					 | 
				
			||||||
import com.pixelized.rplexicon.ui.screens.character.composable.chooser.SpellLevelChooserPreview
 | 
					 | 
				
			||||||
import com.pixelized.rplexicon.ui.screens.character.pages.inventory.InventoryPage
 | 
					import com.pixelized.rplexicon.ui.screens.character.pages.inventory.InventoryPage
 | 
				
			||||||
import com.pixelized.rplexicon.ui.screens.character.pages.inventory.InventoryPagePreview
 | 
					import com.pixelized.rplexicon.ui.screens.character.pages.inventory.InventoryPagePreview
 | 
				
			||||||
import com.pixelized.rplexicon.ui.screens.character.pages.inventory.InventoryViewModel
 | 
					import com.pixelized.rplexicon.ui.screens.character.pages.inventory.InventoryViewModel
 | 
				
			||||||
| 
						 | 
					@ -208,7 +208,12 @@ fun CharacterSheetScreen(
 | 
				
			||||||
                    onLevel = { spell, level ->
 | 
					                    onLevel = { spell, level ->
 | 
				
			||||||
                        scope.launch {
 | 
					                        scope.launch {
 | 
				
			||||||
                            sheetState.hide()
 | 
					                            sheetState.hide()
 | 
				
			||||||
                            overlay.prepareRoll(diceThrow = spellsViewModel.onCastSpell(spell, level))
 | 
					                            overlay.prepareRoll(
 | 
				
			||||||
 | 
					                                diceThrow = spellsViewModel.onCastSpell(
 | 
				
			||||||
 | 
					                                    spell,
 | 
				
			||||||
 | 
					                                    level
 | 
				
			||||||
 | 
					                                )
 | 
				
			||||||
 | 
					                            )
 | 
				
			||||||
                            overlay.showOverlay()
 | 
					                            overlay.showOverlay()
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
| 
						 | 
					@ -433,12 +438,7 @@ private fun rememberHeaderTabsState(
 | 
				
			||||||
                        else -> emptyList()
 | 
					                        else -> emptyList()
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
                addAll(
 | 
					                add(Inventory)
 | 
				
			||||||
                    when {
 | 
					 | 
				
			||||||
                        inventoryViewModel.inventory.value.isNotEmpty() -> listOf(Inventory)
 | 
					 | 
				
			||||||
                        else -> emptyList()
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -12,7 +12,7 @@ import com.pixelized.rplexicon.data.repository.character.AlterationRepository
 | 
				
			||||||
import com.pixelized.rplexicon.data.repository.character.CharacterSheetRepository
 | 
					import com.pixelized.rplexicon.data.repository.character.CharacterSheetRepository
 | 
				
			||||||
import com.pixelized.rplexicon.data.repository.character.DescriptionRepository
 | 
					import com.pixelized.rplexicon.data.repository.character.DescriptionRepository
 | 
				
			||||||
import com.pixelized.rplexicon.data.repository.character.EquipmentRepository
 | 
					import com.pixelized.rplexicon.data.repository.character.EquipmentRepository
 | 
				
			||||||
import com.pixelized.rplexicon.data.repository.character.InventoryRepository
 | 
					import com.pixelized.rplexicon.data.repository.character.ItemsRepository
 | 
				
			||||||
import com.pixelized.rplexicon.data.repository.character.ObjectActionRepository
 | 
					import com.pixelized.rplexicon.data.repository.character.ObjectActionRepository
 | 
				
			||||||
import com.pixelized.rplexicon.data.repository.character.SkillRepository
 | 
					import com.pixelized.rplexicon.data.repository.character.SkillRepository
 | 
				
			||||||
import com.pixelized.rplexicon.data.repository.character.SpellRepository
 | 
					import com.pixelized.rplexicon.data.repository.character.SpellRepository
 | 
				
			||||||
| 
						 | 
					@ -33,7 +33,7 @@ class CharacterSheetViewModel @Inject constructor(
 | 
				
			||||||
    private val characterRepository: CharacterSheetRepository,
 | 
					    private val characterRepository: CharacterSheetRepository,
 | 
				
			||||||
    private val descriptionRepository: DescriptionRepository,
 | 
					    private val descriptionRepository: DescriptionRepository,
 | 
				
			||||||
    private val alterationRepository: AlterationRepository,
 | 
					    private val alterationRepository: AlterationRepository,
 | 
				
			||||||
    private val inventoryRepository: InventoryRepository,
 | 
					    private val itemsRepository: ItemsRepository,
 | 
				
			||||||
    private val equipmentRepository: EquipmentRepository,
 | 
					    private val equipmentRepository: EquipmentRepository,
 | 
				
			||||||
    private val actionRepository: ActionRepository,
 | 
					    private val actionRepository: ActionRepository,
 | 
				
			||||||
    private val objectRepository: ObjectActionRepository,
 | 
					    private val objectRepository: ObjectActionRepository,
 | 
				
			||||||
| 
						 | 
					@ -86,9 +86,9 @@ class CharacterSheetViewModel @Inject constructor(
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        val inventory = async {
 | 
					        val inventory = async {
 | 
				
			||||||
            if (force || inventoryRepository.lastSuccessFullUpdate.shouldUpdate()) {
 | 
					            if (force || itemsRepository.lastSuccessFullUpdate.shouldUpdate()) {
 | 
				
			||||||
                try {
 | 
					                try {
 | 
				
			||||||
                    inventoryRepository.fetchInventory()
 | 
					                    itemsRepository.fetchItems()
 | 
				
			||||||
                } catch (exception: Exception) {
 | 
					                } catch (exception: Exception) {
 | 
				
			||||||
                    Log.e(TAG, exception.message, exception)
 | 
					                    Log.e(TAG, exception.message, exception)
 | 
				
			||||||
                    _error.emit(FetchErrorUio.Structure(type = Type.INVENTORY))
 | 
					                    _error.emit(FetchErrorUio.Structure(type = Type.INVENTORY))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,127 +0,0 @@
 | 
				
			||||||
package com.pixelized.rplexicon.ui.screens.character.composable.actions
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import android.content.res.Configuration.UI_MODE_NIGHT_NO
 | 
					 | 
				
			||||||
import android.content.res.Configuration.UI_MODE_NIGHT_YES
 | 
					 | 
				
			||||||
import androidx.compose.foundation.background
 | 
					 | 
				
			||||||
import androidx.compose.foundation.layout.Box
 | 
					 | 
				
			||||||
import androidx.compose.foundation.layout.Column
 | 
					 | 
				
			||||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
 | 
					 | 
				
			||||||
import androidx.compose.foundation.layout.FlowRow
 | 
					 | 
				
			||||||
import androidx.compose.foundation.layout.IntrinsicSize
 | 
					 | 
				
			||||||
import androidx.compose.foundation.layout.PaddingValues
 | 
					 | 
				
			||||||
import androidx.compose.foundation.layout.Row
 | 
					 | 
				
			||||||
import androidx.compose.foundation.layout.fillMaxHeight
 | 
					 | 
				
			||||||
import androidx.compose.foundation.layout.height
 | 
					 | 
				
			||||||
import androidx.compose.foundation.layout.padding
 | 
					 | 
				
			||||||
import androidx.compose.foundation.layout.width
 | 
					 | 
				
			||||||
import androidx.compose.material3.Divider
 | 
					 | 
				
			||||||
import androidx.compose.material3.DividerDefaults
 | 
					 | 
				
			||||||
import androidx.compose.material3.MaterialTheme
 | 
					 | 
				
			||||||
import androidx.compose.material3.Surface
 | 
					 | 
				
			||||||
import androidx.compose.material3.Text
 | 
					 | 
				
			||||||
import androidx.compose.runtime.Composable
 | 
					 | 
				
			||||||
import androidx.compose.runtime.Stable
 | 
					 | 
				
			||||||
import androidx.compose.runtime.remember
 | 
					 | 
				
			||||||
import androidx.compose.ui.Modifier
 | 
					 | 
				
			||||||
import androidx.compose.ui.text.font.FontWeight
 | 
					 | 
				
			||||||
import androidx.compose.ui.tooling.preview.Preview
 | 
					 | 
				
			||||||
import androidx.compose.ui.unit.dp
 | 
					 | 
				
			||||||
import com.pixelized.rplexicon.ui.screens.character.composable.preview.rememberInventoryListState
 | 
					 | 
				
			||||||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
 | 
					 | 
				
			||||||
import com.pixelized.rplexicon.utilitary.rememberTextSize
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@Stable
 | 
					 | 
				
			||||||
data class InventoryItemUio(
 | 
					 | 
				
			||||||
    val name: String,
 | 
					 | 
				
			||||||
    val amount: String? = null,
 | 
					 | 
				
			||||||
    val items: List<InventoryItemUio> = emptyList(),
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@OptIn(ExperimentalLayoutApi::class)
 | 
					 | 
				
			||||||
@Composable
 | 
					 | 
				
			||||||
fun InventoryItem(
 | 
					 | 
				
			||||||
    modifier: Modifier = Modifier,
 | 
					 | 
				
			||||||
    padding: PaddingValues = PaddingValues(horizontal = 16.dp, vertical = 2.dp),
 | 
					 | 
				
			||||||
    item: InventoryItemUio,
 | 
					 | 
				
			||||||
) {
 | 
					 | 
				
			||||||
    Column(
 | 
					 | 
				
			||||||
        modifier = Modifier
 | 
					 | 
				
			||||||
            .padding(paddingValues = padding)
 | 
					 | 
				
			||||||
            .then(other = modifier),
 | 
					 | 
				
			||||||
    ) {
 | 
					 | 
				
			||||||
        FlowRow {
 | 
					 | 
				
			||||||
            Text(
 | 
					 | 
				
			||||||
                modifier = Modifier.alignByBaseline(),
 | 
					 | 
				
			||||||
                fontWeight = FontWeight.Bold,
 | 
					 | 
				
			||||||
                style = MaterialTheme.typography.bodyMedium,
 | 
					 | 
				
			||||||
                text = item.name,
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
            item.amount?.let {
 | 
					 | 
				
			||||||
                Text(
 | 
					 | 
				
			||||||
                    modifier = Modifier.alignByBaseline(),
 | 
					 | 
				
			||||||
                    fontWeight = FontWeight.Medium,
 | 
					 | 
				
			||||||
                    style = MaterialTheme.typography.labelLarge,
 | 
					 | 
				
			||||||
                    text = " : ",
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
                Text(
 | 
					 | 
				
			||||||
                    modifier = Modifier.alignByBaseline(),
 | 
					 | 
				
			||||||
                    fontWeight = FontWeight.Light,
 | 
					 | 
				
			||||||
                    style = MaterialTheme.typography.bodyMedium,
 | 
					 | 
				
			||||||
                    text = it
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        val lastIndex = remember(item.items.size) { item.items.lastIndex }
 | 
					 | 
				
			||||||
        item.items.forEachIndexed { index, item ->
 | 
					 | 
				
			||||||
            Row(
 | 
					 | 
				
			||||||
                modifier = Modifier.height(intrinsicSize = IntrinsicSize.Min),
 | 
					 | 
				
			||||||
            ) {
 | 
					 | 
				
			||||||
                val size = rememberTextSize(style = MaterialTheme.typography.bodyMedium)
 | 
					 | 
				
			||||||
                if (index == lastIndex) {
 | 
					 | 
				
			||||||
                    Box(
 | 
					 | 
				
			||||||
                        modifier = Modifier
 | 
					 | 
				
			||||||
                            .height(height = 3.dp + size.height / 2)
 | 
					 | 
				
			||||||
                            .width(1.dp)
 | 
					 | 
				
			||||||
                            .background(color = DividerDefaults.color)
 | 
					 | 
				
			||||||
                    )
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    Box(
 | 
					 | 
				
			||||||
                        modifier = Modifier
 | 
					 | 
				
			||||||
                            .fillMaxHeight()
 | 
					 | 
				
			||||||
                            .width(1.dp)
 | 
					 | 
				
			||||||
                            .background(color = DividerDefaults.color)
 | 
					 | 
				
			||||||
                    )
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                Box(
 | 
					 | 
				
			||||||
                    modifier = Modifier
 | 
					 | 
				
			||||||
                        .padding(top = 2.dp + size.height / 2)
 | 
					 | 
				
			||||||
                        .height(1.dp)
 | 
					 | 
				
			||||||
                        .width(8.dp)
 | 
					 | 
				
			||||||
                        .background(color = DividerDefaults.color)
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
                InventoryItem(
 | 
					 | 
				
			||||||
                    padding = PaddingValues(start = 7.dp, top = 2.dp, bottom = 2.dp),
 | 
					 | 
				
			||||||
                    item = item,
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@Composable
 | 
					 | 
				
			||||||
@Preview(uiMode = UI_MODE_NIGHT_NO)
 | 
					 | 
				
			||||||
@Preview(uiMode = UI_MODE_NIGHT_YES)
 | 
					 | 
				
			||||||
private fun InventoryItemPreview() {
 | 
					 | 
				
			||||||
    LexiconTheme {
 | 
					 | 
				
			||||||
        Surface {
 | 
					 | 
				
			||||||
            Column {
 | 
					 | 
				
			||||||
                val items = rememberInventoryListState()
 | 
					 | 
				
			||||||
                items.value.forEach {
 | 
					 | 
				
			||||||
                    InventoryItem(
 | 
					 | 
				
			||||||
                        item = it,
 | 
					 | 
				
			||||||
                    )
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -5,37 +5,114 @@ import androidx.compose.runtime.Stable
 | 
				
			||||||
import androidx.compose.runtime.State
 | 
					import androidx.compose.runtime.State
 | 
				
			||||||
import androidx.compose.runtime.mutableStateOf
 | 
					import androidx.compose.runtime.mutableStateOf
 | 
				
			||||||
import androidx.compose.runtime.remember
 | 
					import androidx.compose.runtime.remember
 | 
				
			||||||
import com.pixelized.rplexicon.ui.screens.character.composable.actions.InventoryItemUio
 | 
					import com.pixelized.rplexicon.R
 | 
				
			||||||
 | 
					import com.pixelized.rplexicon.ui.screens.character.pages.inventory.InventoryItemUio
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Composable
 | 
					@Composable
 | 
				
			||||||
@Stable
 | 
					@Stable
 | 
				
			||||||
fun rememberInventoryListState(): State<List<InventoryItemUio>> {
 | 
					fun rememberInventoryListState(): State<List<InventoryItemUio>> {
 | 
				
			||||||
    return remember {
 | 
					    return remember {
 | 
				
			||||||
 | 
					        var id = 0
 | 
				
			||||||
        mutableStateOf(
 | 
					        mutableStateOf(
 | 
				
			||||||
            listOf(
 | 
					            listOf(
 | 
				
			||||||
                InventoryItemUio(
 | 
					                InventoryItemUio(
 | 
				
			||||||
                    name = "Bourse",
 | 
					                    id = "${id++}",
 | 
				
			||||||
                    items = listOf(
 | 
					                    name = "Pouch",
 | 
				
			||||||
                        InventoryItemUio(name = "Or", amount = "21"),
 | 
					                    amount = 1,
 | 
				
			||||||
                    ),
 | 
					                    container = true,
 | 
				
			||||||
 | 
					                    icon = R.drawable.icbg_pouch_a_unfaded,
 | 
				
			||||||
                ),
 | 
					                ),
 | 
				
			||||||
                InventoryItemUio(
 | 
					                InventoryItemUio(
 | 
				
			||||||
                    name = "Sac à dos",
 | 
					                    id = "${id++}",
 | 
				
			||||||
                    items = listOf(
 | 
					                    name = "Backpack",
 | 
				
			||||||
                        InventoryItemUio(name = "Sac de couchage"),
 | 
					                    amount = 1,
 | 
				
			||||||
                        InventoryItemUio(name = "Kit de cuisine"),
 | 
					                    container = true,
 | 
				
			||||||
                        InventoryItemUio(name = "Boite d'allume-feu"),
 | 
					                    icon = R.drawable.icbg_backpack_a_unfaded,
 | 
				
			||||||
                        InventoryItemUio(name = "Torches", amount = "10"),
 | 
					                ),
 | 
				
			||||||
                        InventoryItemUio(name = "Rations journalières", amount = "10"),
 | 
					                InventoryItemUio(
 | 
				
			||||||
                        InventoryItemUio(name = "Outre d'eau"),
 | 
					                    id = "${id++}",
 | 
				
			||||||
                        InventoryItemUio(name = "Cordes", amount = "15 mètres"),
 | 
					                    name = "Scroll of blessing",
 | 
				
			||||||
                        InventoryItemUio(name = "Piège de chasse"),
 | 
					                    amount = 1,
 | 
				
			||||||
                        InventoryItemUio(name = "Bâton de marche"),
 | 
					                    container = false,
 | 
				
			||||||
                    ),
 | 
					                    icon = R.drawable.icbg_scroll_of_bless_unfaded,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                InventoryItemUio(
 | 
				
			||||||
 | 
					                    id = "${id++}",
 | 
				
			||||||
 | 
					                    name = "Scroll of spirit weapon",
 | 
				
			||||||
 | 
					                    amount = 1,
 | 
				
			||||||
 | 
					                    container = false,
 | 
				
			||||||
 | 
					                    icon = R.drawable.icbg_book_signedtradebisa_unfaded,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                InventoryItemUio(
 | 
				
			||||||
 | 
					                    id = "${id++}",
 | 
				
			||||||
 | 
					                    name = "Potion of healing",
 | 
				
			||||||
 | 
					                    amount = 2,
 | 
				
			||||||
 | 
					                    container = false,
 | 
				
			||||||
 | 
					                    icon = R.drawable.icbg_potion_of_superior_healing_unfaded,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                InventoryItemUio(
 | 
				
			||||||
 | 
					                    id = "${id++}",
 | 
				
			||||||
 | 
					                    name = "Potion of supérior healing",
 | 
				
			||||||
 | 
					                    amount = 2,
 | 
				
			||||||
 | 
					                    container = false,
 | 
				
			||||||
 | 
					                    icon = R.drawable.icbg_pot_potion_of_healing_unfaded,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                InventoryItemUio(
 | 
				
			||||||
 | 
					                    id = "${id++}",
 | 
				
			||||||
 | 
					                    name = "Potion of holy water",
 | 
				
			||||||
 | 
					                    amount = 2,
 | 
				
			||||||
 | 
					                    container = false,
 | 
				
			||||||
 | 
					                    icon = R.drawable.icbg_grn_holy_water_unfaded,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                InventoryItemUio(
 | 
				
			||||||
 | 
					                    id = "${id++}",
 | 
				
			||||||
 | 
					                    name = "Leather armor",
 | 
				
			||||||
 | 
					                    amount = 1,
 | 
				
			||||||
 | 
					                    container = false,
 | 
				
			||||||
 | 
					                    icon = R.drawable.icbg_leather_armour_rogue_unfaded,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                InventoryItemUio(
 | 
				
			||||||
 | 
					                    id = "${id++}",
 | 
				
			||||||
 | 
					                    name = "Silver battleaxe",
 | 
				
			||||||
 | 
					                    amount = 1,
 | 
				
			||||||
 | 
					                    container = false,
 | 
				
			||||||
 | 
					                    icon = R.drawable.icbg_battleaxe_plus_one_unfaded,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                InventoryItemUio(
 | 
				
			||||||
 | 
					                    id = "${id++}",
 | 
				
			||||||
 | 
					                    name = "Hand crossbow",
 | 
				
			||||||
 | 
					                    amount = 1,
 | 
				
			||||||
 | 
					                    container = false,
 | 
				
			||||||
 | 
					                    icon = R.drawable.icbg_hand_crossbow_unfaded,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                InventoryItemUio(
 | 
				
			||||||
 | 
					                    id = "${id++}",
 | 
				
			||||||
 | 
					                    name = "Goodberry",
 | 
				
			||||||
 | 
					                    amount = 1,
 | 
				
			||||||
 | 
					                    container = false,
 | 
				
			||||||
 | 
					                    icon = R.drawable.icbg_food_goodberry_unfaded,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                InventoryItemUio(
 | 
				
			||||||
 | 
					                    id = "${id++}",
 | 
				
			||||||
 | 
					                    name = "Goodberry",
 | 
				
			||||||
 | 
					                    amount = 1,
 | 
				
			||||||
 | 
					                    container = false,
 | 
				
			||||||
 | 
					                    icon = R.drawable.icbg_worg_fang_unfaded,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                InventoryItemUio(
 | 
				
			||||||
 | 
					                    id = "${id++}",
 | 
				
			||||||
 | 
					                    name = "Lantern of revealing",
 | 
				
			||||||
 | 
					                    amount = 1,
 | 
				
			||||||
 | 
					                    container = false,
 | 
				
			||||||
 | 
					                    icon = R.drawable.icbg_lantern_of_revealing,
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                InventoryItemUio(
 | 
				
			||||||
 | 
					                    id = "${id++}",
 | 
				
			||||||
 | 
					                    name = "Dust of disappearance",
 | 
				
			||||||
 | 
					                    amount = 1,
 | 
				
			||||||
 | 
					                    container = false,
 | 
				
			||||||
 | 
					                    icon = R.drawable.icbg_haste_spore_grenade_unfaded,
 | 
				
			||||||
                ),
 | 
					                ),
 | 
				
			||||||
                InventoryItemUio(name = "Dague"),
 | 
					 | 
				
			||||||
                InventoryItemUio(name = "Javelot", amount = "4"),
 | 
					 | 
				
			||||||
                InventoryItemUio(name = "Cape de protection"),
 | 
					 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,16 +1,29 @@
 | 
				
			||||||
package com.pixelized.rplexicon.ui.screens.character.factory
 | 
					package com.pixelized.rplexicon.ui.screens.character.factory
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.pixelized.rplexicon.data.model.Inventory
 | 
					import com.pixelized.rplexicon.data.model.item.Item
 | 
				
			||||||
import com.pixelized.rplexicon.ui.screens.character.composable.actions.InventoryItemUio
 | 
					import com.pixelized.rplexicon.data.network.ItemDto
 | 
				
			||||||
 | 
					import com.pixelized.rplexicon.ui.screens.character.pages.inventory.InventoryItemUio
 | 
				
			||||||
import javax.inject.Inject
 | 
					import javax.inject.Inject
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ItemUioFactory @Inject constructor() {
 | 
					class ItemUioFactory @Inject constructor() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fun toUio(item: Inventory.Item): InventoryItemUio {
 | 
					    fun toUio(
 | 
				
			||||||
        return InventoryItemUio(
 | 
					        items: Map<String, Item>,
 | 
				
			||||||
            name = item.name,
 | 
					        fires: List<ItemDto>,
 | 
				
			||||||
            amount = item.amount,
 | 
					    ): List<InventoryItemUio> {
 | 
				
			||||||
            items = item.items.map { toUio(it) }
 | 
					        return fires.mapNotNull { fire ->
 | 
				
			||||||
        )
 | 
					            items[fire.id]?.let { item ->
 | 
				
			||||||
 | 
					                fire.amount?.let { amount ->
 | 
				
			||||||
 | 
					                    InventoryItemUio(
 | 
				
			||||||
 | 
					                        id = item.id,
 | 
				
			||||||
 | 
					                        name = item.name,
 | 
				
			||||||
 | 
					                        amount = amount,
 | 
				
			||||||
 | 
					                        container = item.isContainer,
 | 
				
			||||||
 | 
					                        icon = item.icon,
 | 
				
			||||||
 | 
					                        items = null // fire.children?.let { toUio(items = items, fires = it) },
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -34,12 +34,12 @@ import com.pixelized.rplexicon.ui.theme.LexiconTheme
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Stable
 | 
					@Stable
 | 
				
			||||||
data class InventoryItemUio(
 | 
					data class InventoryItemUio(
 | 
				
			||||||
    val id: Int,
 | 
					    val id: String,
 | 
				
			||||||
    val name: String,
 | 
					    val name: String,
 | 
				
			||||||
    val amount: Int,
 | 
					    val amount: Int = 1,
 | 
				
			||||||
    val container: Boolean,
 | 
					    val container: Boolean,
 | 
				
			||||||
    val icon: Any?,
 | 
					    val icon: Any? = R.drawable.icbg_generic_darkness_icon,
 | 
				
			||||||
    val items: List<InventoryItemUio> = emptyList(),
 | 
					    val items: List<InventoryItemUio>? = null,
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Composable
 | 
					@Composable
 | 
				
			||||||
| 
						 | 
					@ -57,6 +57,39 @@ fun InventoryItem(
 | 
				
			||||||
            model = item.icon,
 | 
					            model = item.icon,
 | 
				
			||||||
            contentScale = ContentScale.Fit,
 | 
					            contentScale = ContentScale.Fit,
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					        AnimatedContent(
 | 
				
			||||||
 | 
					            modifier = Modifier
 | 
				
			||||||
 | 
					                .align(alignment = Alignment.TopEnd)
 | 
				
			||||||
 | 
					                .offset(x = 0.dp, y = (-4).dp)
 | 
				
			||||||
 | 
					                .padding(horizontal = 2.dp),
 | 
				
			||||||
 | 
					            targetState = item.items?.size,
 | 
				
			||||||
 | 
					            transitionSpec = {
 | 
				
			||||||
 | 
					                // Compare the incoming number with the previous number.
 | 
				
			||||||
 | 
					                if ((targetState ?: 0) > (initialState ?: 0)) {
 | 
				
			||||||
 | 
					                    // If the target number is larger, it slides up and fades in
 | 
				
			||||||
 | 
					                    // while the initial (smaller) number slides up and fades out.
 | 
				
			||||||
 | 
					                    slideInVertically { height -> height / 2 } + fadeIn() togetherWith slideOutVertically { height -> -height / 2 } + fadeOut()
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    // If the target number is smaller, it slides down and fades in
 | 
				
			||||||
 | 
					                    // while the initial number slides down and fades out.
 | 
				
			||||||
 | 
					                    slideInVertically { height -> -height / 2 } + fadeIn() togetherWith slideOutVertically { height -> height / 2 } + fadeOut()
 | 
				
			||||||
 | 
					                }.using(
 | 
				
			||||||
 | 
					                    // Disable clipping since the faded slide-in/out should
 | 
				
			||||||
 | 
					                    // be displayed out of bounds.
 | 
				
			||||||
 | 
					                    SizeTransform(clip = false)
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            label = "Container count size",
 | 
				
			||||||
 | 
					        ) { amount ->
 | 
				
			||||||
 | 
					            Text(
 | 
				
			||||||
 | 
					                maxLines = 1,
 | 
				
			||||||
 | 
					                overflow = TextOverflow.Ellipsis,
 | 
				
			||||||
 | 
					                style = MaterialTheme.typography.bodyLarge,
 | 
				
			||||||
 | 
					                fontWeight = FontWeight.Bold,
 | 
				
			||||||
 | 
					                textAlign = TextAlign.End,
 | 
				
			||||||
 | 
					                text = amount.takeIf { it != null }?.let { "$it" } ?: "",
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        AnimatedContent(
 | 
					        AnimatedContent(
 | 
				
			||||||
            modifier = Modifier
 | 
					            modifier = Modifier
 | 
				
			||||||
                .align(alignment = Alignment.BottomEnd)
 | 
					                .align(alignment = Alignment.BottomEnd)
 | 
				
			||||||
| 
						 | 
					@ -68,13 +101,11 @@ fun InventoryItem(
 | 
				
			||||||
                if (targetState > initialState) {
 | 
					                if (targetState > initialState) {
 | 
				
			||||||
                    // If the target number is larger, it slides up and fades in
 | 
					                    // If the target number is larger, it slides up and fades in
 | 
				
			||||||
                    // while the initial (smaller) number slides up and fades out.
 | 
					                    // while the initial (smaller) number slides up and fades out.
 | 
				
			||||||
                    slideInVertically { height -> height / 2 } + fadeIn() togetherWith
 | 
					                    slideInVertically { height -> height / 2 } + fadeIn() togetherWith slideOutVertically { height -> -height / 2 } + fadeOut()
 | 
				
			||||||
                            slideOutVertically { height -> -height / 2 } + fadeOut()
 | 
					 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    // If the target number is smaller, it slides down and fades in
 | 
					                    // If the target number is smaller, it slides down and fades in
 | 
				
			||||||
                    // while the initial number slides down and fades out.
 | 
					                    // while the initial number slides down and fades out.
 | 
				
			||||||
                    slideInVertically { height -> -height / 2 } + fadeIn() togetherWith
 | 
					                    slideInVertically { height -> -height / 2 } + fadeIn() togetherWith slideOutVertically { height -> height / 2 } + fadeOut()
 | 
				
			||||||
                            slideOutVertically { height -> height / 2 } + fadeOut()
 | 
					 | 
				
			||||||
                }.using(
 | 
					                }.using(
 | 
				
			||||||
                    // Disable clipping since the faded slide-in/out should
 | 
					                    // Disable clipping since the faded slide-in/out should
 | 
				
			||||||
                    // be displayed out of bounds.
 | 
					                    // be displayed out of bounds.
 | 
				
			||||||
| 
						 | 
					@ -90,42 +121,7 @@ fun InventoryItem(
 | 
				
			||||||
                style = MaterialTheme.typography.bodyLarge,
 | 
					                style = MaterialTheme.typography.bodyLarge,
 | 
				
			||||||
                fontWeight = FontWeight.Bold,
 | 
					                fontWeight = FontWeight.Bold,
 | 
				
			||||||
                textAlign = TextAlign.End,
 | 
					                textAlign = TextAlign.End,
 | 
				
			||||||
                text = amount.takeIf { it > 0 }?.let { "$it" } ?: " ", //  
 | 
					                text = amount.takeIf { it > 1 }?.let { "$it" } ?: " ", //  
 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        AnimatedContent(
 | 
					 | 
				
			||||||
            modifier = Modifier
 | 
					 | 
				
			||||||
                .align(alignment = Alignment.TopEnd)
 | 
					 | 
				
			||||||
                .offset(x = 0.dp, y = (-4).dp)
 | 
					 | 
				
			||||||
                .padding(horizontal = 2.dp),
 | 
					 | 
				
			||||||
            targetState = item.items.size,
 | 
					 | 
				
			||||||
            transitionSpec = {
 | 
					 | 
				
			||||||
                // Compare the incoming number with the previous number.
 | 
					 | 
				
			||||||
                if (targetState > initialState) {
 | 
					 | 
				
			||||||
                    // If the target number is larger, it slides up and fades in
 | 
					 | 
				
			||||||
                    // while the initial (smaller) number slides up and fades out.
 | 
					 | 
				
			||||||
                    slideInVertically { height -> height / 2 } + fadeIn() togetherWith
 | 
					 | 
				
			||||||
                            slideOutVertically { height -> -height / 2 } + fadeOut()
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    // If the target number is smaller, it slides down and fades in
 | 
					 | 
				
			||||||
                    // while the initial number slides down and fades out.
 | 
					 | 
				
			||||||
                    slideInVertically { height -> -height / 2 } + fadeIn() togetherWith
 | 
					 | 
				
			||||||
                            slideOutVertically { height -> height / 2 } + fadeOut()
 | 
					 | 
				
			||||||
                }.using(
 | 
					 | 
				
			||||||
                    // Disable clipping since the faded slide-in/out should
 | 
					 | 
				
			||||||
                    // be displayed out of bounds.
 | 
					 | 
				
			||||||
                    SizeTransform(clip = false)
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
            },
 | 
					 | 
				
			||||||
            label = "Container count size",
 | 
					 | 
				
			||||||
        ) { amount ->
 | 
					 | 
				
			||||||
            Text(
 | 
					 | 
				
			||||||
                maxLines = 1,
 | 
					 | 
				
			||||||
                overflow = TextOverflow.Ellipsis,
 | 
					 | 
				
			||||||
                style = MaterialTheme.typography.bodyLarge,
 | 
					 | 
				
			||||||
                fontWeight = FontWeight.Bold,
 | 
					 | 
				
			||||||
                textAlign = TextAlign.End,
 | 
					 | 
				
			||||||
                text = amount.takeIf { it > 0 }?.let { "$it" } ?: " ", //  
 | 
					 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -150,23 +146,21 @@ private fun InventoryItemPreview(
 | 
				
			||||||
private class ClassInventoryItemProvider : PreviewParameterProvider<InventoryItemUio> {
 | 
					private class ClassInventoryItemProvider : PreviewParameterProvider<InventoryItemUio> {
 | 
				
			||||||
    override val values: Sequence<InventoryItemUio> = sequenceOf(
 | 
					    override val values: Sequence<InventoryItemUio> = sequenceOf(
 | 
				
			||||||
        InventoryItemUio(
 | 
					        InventoryItemUio(
 | 
				
			||||||
            id = 0,
 | 
					            id = "0",
 | 
				
			||||||
            name = "Pouch",
 | 
					            name = "Pouch",
 | 
				
			||||||
            amount = 1,
 | 
					            amount = 1,
 | 
				
			||||||
            container = true,
 | 
					            container = true,
 | 
				
			||||||
            icon = R.drawable.icbg_pouch_a_unfaded,
 | 
					            icon = R.drawable.icbg_pouch_a_unfaded,
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
            ),
 | 
					 | 
				
			||||||
        InventoryItemUio(
 | 
					        InventoryItemUio(
 | 
				
			||||||
            id = 1,
 | 
					            id = "1",
 | 
				
			||||||
            name = "Scroll of blessing",
 | 
					            name = "Scroll of blessing",
 | 
				
			||||||
            amount = 1,
 | 
					            amount = 1,
 | 
				
			||||||
            container = false,
 | 
					            container = false,
 | 
				
			||||||
            icon = R.drawable.icbg_scroll_of_bless_unfaded,
 | 
					            icon = R.drawable.icbg_scroll_of_bless_unfaded,
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
            ),
 | 
					 | 
				
			||||||
        InventoryItemUio(
 | 
					        InventoryItemUio(
 | 
				
			||||||
            id = 2,
 | 
					            id = "2",
 | 
				
			||||||
            name = "Potion of blessing",
 | 
					            name = "Potion of blessing",
 | 
				
			||||||
            amount = 2,
 | 
					            amount = 2,
 | 
				
			||||||
            container = false,
 | 
					            container = false,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,13 +2,22 @@ package com.pixelized.rplexicon.ui.screens.character.pages.inventory
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import android.content.res.Configuration.UI_MODE_NIGHT_NO
 | 
					import android.content.res.Configuration.UI_MODE_NIGHT_NO
 | 
				
			||||||
import android.content.res.Configuration.UI_MODE_NIGHT_YES
 | 
					import android.content.res.Configuration.UI_MODE_NIGHT_YES
 | 
				
			||||||
import androidx.compose.foundation.ExperimentalFoundationApi
 | 
					import androidx.compose.animation.animateColor
 | 
				
			||||||
 | 
					import androidx.compose.animation.core.updateTransition
 | 
				
			||||||
 | 
					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.fillMaxSize
 | 
					import androidx.compose.foundation.layout.fillMaxSize
 | 
				
			||||||
import androidx.compose.foundation.layout.padding
 | 
					import androidx.compose.foundation.layout.padding
 | 
				
			||||||
import androidx.compose.foundation.lazy.LazyColumn
 | 
					import androidx.compose.foundation.lazy.grid.GridCells
 | 
				
			||||||
import androidx.compose.foundation.lazy.items
 | 
					import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
 | 
				
			||||||
 | 
					import androidx.compose.foundation.lazy.grid.itemsIndexed
 | 
				
			||||||
 | 
					import androidx.compose.foundation.lazy.grid.rememberLazyGridState
 | 
				
			||||||
 | 
					import androidx.compose.foundation.shape.RoundedCornerShape
 | 
				
			||||||
 | 
					import androidx.compose.material3.MaterialTheme
 | 
				
			||||||
import androidx.compose.material3.Surface
 | 
					import androidx.compose.material3.Surface
 | 
				
			||||||
 | 
					import androidx.compose.material3.surfaceColorAtElevation
 | 
				
			||||||
import androidx.compose.runtime.Composable
 | 
					import androidx.compose.runtime.Composable
 | 
				
			||||||
import androidx.compose.runtime.LaunchedEffect
 | 
					import androidx.compose.runtime.LaunchedEffect
 | 
				
			||||||
import androidx.compose.runtime.State
 | 
					import androidx.compose.runtime.State
 | 
				
			||||||
| 
						 | 
					@ -16,19 +25,23 @@ import androidx.compose.runtime.mutableStateOf
 | 
				
			||||||
import androidx.compose.runtime.remember
 | 
					import androidx.compose.runtime.remember
 | 
				
			||||||
import androidx.compose.runtime.rememberCoroutineScope
 | 
					import androidx.compose.runtime.rememberCoroutineScope
 | 
				
			||||||
import androidx.compose.ui.Modifier
 | 
					import androidx.compose.ui.Modifier
 | 
				
			||||||
 | 
					import androidx.compose.ui.graphics.compositeOver
 | 
				
			||||||
 | 
					import androidx.compose.ui.graphics.graphicsLayer
 | 
				
			||||||
 | 
					import androidx.compose.ui.platform.LocalView
 | 
				
			||||||
import androidx.compose.ui.tooling.preview.Preview
 | 
					import androidx.compose.ui.tooling.preview.Preview
 | 
				
			||||||
import androidx.compose.ui.unit.dp
 | 
					import androidx.compose.ui.unit.dp
 | 
				
			||||||
import com.pixelized.rplexicon.LocalSnack
 | 
					import com.pixelized.rplexicon.LocalSnack
 | 
				
			||||||
import com.pixelized.rplexicon.R
 | 
					 | 
				
			||||||
import com.pixelized.rplexicon.ui.screens.character.composable.actions.EquipmentItem
 | 
					import com.pixelized.rplexicon.ui.screens.character.composable.actions.EquipmentItem
 | 
				
			||||||
import com.pixelized.rplexicon.ui.screens.character.composable.actions.EquipmentItemUio
 | 
					import com.pixelized.rplexicon.ui.screens.character.composable.actions.EquipmentItemUio
 | 
				
			||||||
import com.pixelized.rplexicon.ui.screens.character.composable.actions.GenericHeader
 | 
					import com.pixelized.rplexicon.ui.screens.character.composable.dialogs.SkillDetailDialog
 | 
				
			||||||
import com.pixelized.rplexicon.ui.screens.character.composable.actions.InventoryItem
 | 
					 | 
				
			||||||
import com.pixelized.rplexicon.ui.screens.character.composable.actions.InventoryItemUio
 | 
					 | 
				
			||||||
import com.pixelized.rplexicon.ui.screens.character.composable.preview.rememberEquipmentState
 | 
					import com.pixelized.rplexicon.ui.screens.character.composable.preview.rememberEquipmentState
 | 
				
			||||||
import com.pixelized.rplexicon.ui.screens.character.composable.preview.rememberInventoryListState
 | 
					import com.pixelized.rplexicon.ui.screens.character.composable.preview.rememberInventoryListState
 | 
				
			||||||
import com.pixelized.rplexicon.ui.screens.character.composable.dialogs.SkillDetailDialog
 | 
					 | 
				
			||||||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
 | 
					import com.pixelized.rplexicon.ui.theme.LexiconTheme
 | 
				
			||||||
 | 
					import com.pixelized.rplexicon.utilitary.DraggableItem
 | 
				
			||||||
 | 
					import com.pixelized.rplexicon.utilitary.dragContainer
 | 
				
			||||||
 | 
					import com.pixelized.rplexicon.utilitary.extentions.lexicon
 | 
				
			||||||
 | 
					import com.pixelized.rplexicon.utilitary.extentions.modifier.doubleBorder
 | 
				
			||||||
 | 
					import com.pixelized.rplexicon.utilitary.rememberGridDragDropState
 | 
				
			||||||
import kotlinx.coroutines.Job
 | 
					import kotlinx.coroutines.Job
 | 
				
			||||||
import kotlinx.coroutines.launch
 | 
					import kotlinx.coroutines.launch
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -36,6 +49,7 @@ import kotlinx.coroutines.launch
 | 
				
			||||||
fun InventoryPage(
 | 
					fun InventoryPage(
 | 
				
			||||||
    viewModel: InventoryViewModel,
 | 
					    viewModel: InventoryViewModel,
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
 | 
					    val view = LocalView.current
 | 
				
			||||||
    val snack = LocalSnack.current
 | 
					    val snack = LocalSnack.current
 | 
				
			||||||
    val snackJob = remember { mutableStateOf<Job?>(null) }
 | 
					    val snackJob = remember { mutableStateOf<Job?>(null) }
 | 
				
			||||||
    val scope = rememberCoroutineScope()
 | 
					    val scope = rememberCoroutineScope()
 | 
				
			||||||
| 
						 | 
					@ -48,6 +62,10 @@ fun InventoryPage(
 | 
				
			||||||
            snackJob.value?.cancel()
 | 
					            snackJob.value?.cancel()
 | 
				
			||||||
            viewModel.showSkillDetailDialog(item = it)
 | 
					            viewModel.showSkillDetailDialog(item = it)
 | 
				
			||||||
        },
 | 
					        },
 | 
				
			||||||
 | 
					        onInventoryItemMove = viewModel::onMove,
 | 
				
			||||||
 | 
					        onInventoryIsMoveEnable = viewModel::isMoveEnable,
 | 
				
			||||||
 | 
					        onInventoryItemDrop = viewModel::onDrop,
 | 
				
			||||||
 | 
					        onInventoryItemDropOver = viewModel::onDropOver,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    SkillDetailDialog(
 | 
					    SkillDetailDialog(
 | 
				
			||||||
| 
						 | 
					@ -65,37 +83,102 @@ fun InventoryPage(
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@OptIn(ExperimentalFoundationApi::class)
 | 
					 | 
				
			||||||
@Composable
 | 
					@Composable
 | 
				
			||||||
private fun InventoryPageContent(
 | 
					private fun InventoryPageContent(
 | 
				
			||||||
    modifier: Modifier = Modifier,
 | 
					    modifier: Modifier = Modifier,
 | 
				
			||||||
    equipments: State<EquipmentItemUio?>,
 | 
					    equipments: State<EquipmentItemUio?>,
 | 
				
			||||||
    inventory: State<List<InventoryItemUio>>,
 | 
					    inventory: State<List<InventoryItemUio>>,
 | 
				
			||||||
    onEquipment: (String) -> Unit,
 | 
					    onEquipment: (String) -> Unit,
 | 
				
			||||||
 | 
					    onInventoryItemMove: (Int, Int) -> Unit,
 | 
				
			||||||
 | 
					    onInventoryIsMoveEnable: (Int, Int) -> Boolean,
 | 
				
			||||||
 | 
					    onInventoryItemDrop: (Int) -> Unit,
 | 
				
			||||||
 | 
					    onInventoryItemDropOver: (Int, Int) -> Unit,
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
    LazyColumn(
 | 
					    val contentPadding = remember { PaddingValues(16.dp) }
 | 
				
			||||||
 | 
					    val gridState = rememberLazyGridState()
 | 
				
			||||||
 | 
					    val dragDropState = rememberGridDragDropState(
 | 
				
			||||||
 | 
					        contentPadding = contentPadding,
 | 
				
			||||||
 | 
					        gridState = gridState,
 | 
				
			||||||
 | 
					        onMove = onInventoryItemMove,
 | 
				
			||||||
 | 
					        isMoveEnable = onInventoryIsMoveEnable,
 | 
				
			||||||
 | 
					        onDragRelease = onInventoryItemDrop,
 | 
				
			||||||
 | 
					        onDropReleaseOver = onInventoryItemDropOver,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    Column(
 | 
				
			||||||
        modifier = modifier,
 | 
					        modifier = modifier,
 | 
				
			||||||
        contentPadding = PaddingValues(top = 8.dp, bottom = 16.dp),
 | 
					 | 
				
			||||||
    ) {
 | 
					    ) {
 | 
				
			||||||
        equipments.value?.let {
 | 
					        equipments.value?.let {
 | 
				
			||||||
            item {
 | 
					            EquipmentItem(
 | 
				
			||||||
                EquipmentItem(
 | 
					                modifier = Modifier.padding(bottom = 16.dp),
 | 
				
			||||||
                    modifier = Modifier.padding(bottom = 16.dp),
 | 
					                equipments = it,
 | 
				
			||||||
                    equipments = it,
 | 
					                onClick = onEquipment,
 | 
				
			||||||
                    onClick = onEquipment,
 | 
					            )
 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (inventory.value.isNotEmpty()) {
 | 
					        LazyVerticalGrid(
 | 
				
			||||||
            stickyHeader {
 | 
					            columns = GridCells.Fixed(5),
 | 
				
			||||||
                GenericHeader(
 | 
					            modifier = Modifier
 | 
				
			||||||
                    label = R.string.character_sheet_title_inventory
 | 
					                .graphicsLayer { this.clip = false }
 | 
				
			||||||
                )
 | 
					                .dragContainer(dragDropState)
 | 
				
			||||||
            }
 | 
					                .fillMaxSize(),
 | 
				
			||||||
            items(items = inventory.value) {
 | 
					            state = gridState,
 | 
				
			||||||
                InventoryItem(
 | 
					            contentPadding = contentPadding,
 | 
				
			||||||
                    item = it,
 | 
					            verticalArrangement = Arrangement.spacedBy(8.dp),
 | 
				
			||||||
                )
 | 
					            horizontalArrangement = Arrangement.spacedBy(8.dp),
 | 
				
			||||||
 | 
					        ) {
 | 
				
			||||||
 | 
					            itemsIndexed(
 | 
				
			||||||
 | 
					                items = inventory.value,
 | 
				
			||||||
 | 
					                key = { _, item -> item.id },
 | 
				
			||||||
 | 
					            ) { index, item ->
 | 
				
			||||||
 | 
					                DraggableItem(
 | 
				
			||||||
 | 
					                    dragDropState = dragDropState,
 | 
				
			||||||
 | 
					                    index = index
 | 
				
			||||||
 | 
					                ) { isDragging, isOvering ->
 | 
				
			||||||
 | 
					                    val colorScheme = MaterialTheme.lexicon.colorScheme
 | 
				
			||||||
 | 
					                    val transition = updateTransition(
 | 
				
			||||||
 | 
					                        targetState = isDragging || isOvering,
 | 
				
			||||||
 | 
					                        label = "Dragging transition",
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                    val backgroundColor = transition.animateColor(
 | 
				
			||||||
 | 
					                        label = "Draggable item background color",
 | 
				
			||||||
 | 
					                    ) {
 | 
				
			||||||
 | 
					                        val surface = colorScheme.base.surfaceColorAtElevation(2.dp)
 | 
				
			||||||
 | 
					                        when (it) {
 | 
				
			||||||
 | 
					                            true -> colorScheme.base.primary.copy(alpha = 0.15f)
 | 
				
			||||||
 | 
					                                .compositeOver(surface)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            else -> surface
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    val outlineColor = transition.animateColor(
 | 
				
			||||||
 | 
					                        label = "Draggable item outline color",
 | 
				
			||||||
 | 
					                    ) {
 | 
				
			||||||
 | 
					                        when (it) {
 | 
				
			||||||
 | 
					                            true -> colorScheme.base.primary
 | 
				
			||||||
 | 
					                            else -> colorScheme.characterSheet.outlineBorder
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    val innerColor = transition.animateColor(
 | 
				
			||||||
 | 
					                        label = "Draggable item inline color",
 | 
				
			||||||
 | 
					                    ) {
 | 
				
			||||||
 | 
					                        when (it) {
 | 
				
			||||||
 | 
					                            true -> colorScheme.base.primary
 | 
				
			||||||
 | 
					                            else -> colorScheme.characterSheet.innerBorder
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    Box(
 | 
				
			||||||
 | 
					                        modifier = Modifier.doubleBorder(
 | 
				
			||||||
 | 
					                            backgroundColor = backgroundColor.value,
 | 
				
			||||||
 | 
					                            outline = remember { RoundedCornerShape(8.dp) },
 | 
				
			||||||
 | 
					                            outlineColor = outlineColor.value,
 | 
				
			||||||
 | 
					                            inner = remember { RoundedCornerShape(6.dp) },
 | 
				
			||||||
 | 
					                            innerColor = innerColor.value,
 | 
				
			||||||
 | 
					                        )
 | 
				
			||||||
 | 
					                    ) {
 | 
				
			||||||
 | 
					                        InventoryItem(
 | 
				
			||||||
 | 
					                            item = item,
 | 
				
			||||||
 | 
					                        )
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					@ -112,6 +195,10 @@ fun InventoryPagePreview() {
 | 
				
			||||||
                equipments = rememberEquipmentState(),
 | 
					                equipments = rememberEquipmentState(),
 | 
				
			||||||
                inventory = rememberInventoryListState(),
 | 
					                inventory = rememberInventoryListState(),
 | 
				
			||||||
                onEquipment = { },
 | 
					                onEquipment = { },
 | 
				
			||||||
 | 
					                onInventoryItemMove = { _, _ -> },
 | 
				
			||||||
 | 
					                onInventoryIsMoveEnable = { _, _ -> true },
 | 
				
			||||||
 | 
					                onInventoryItemDrop = { _ -> },
 | 
				
			||||||
 | 
					                onInventoryItemDropOver = { _, _ -> }
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,18 +1,23 @@
 | 
				
			||||||
package com.pixelized.rplexicon.ui.screens.character.pages.inventory
 | 
					package com.pixelized.rplexicon.ui.screens.character.pages.inventory
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import android.app.Application
 | 
					import android.app.Application
 | 
				
			||||||
 | 
					import androidx.compose.runtime.Composable
 | 
				
			||||||
 | 
					import androidx.compose.runtime.Stable
 | 
				
			||||||
import androidx.compose.runtime.State
 | 
					import androidx.compose.runtime.State
 | 
				
			||||||
 | 
					import androidx.compose.runtime.collectAsState
 | 
				
			||||||
import androidx.compose.runtime.mutableStateOf
 | 
					import androidx.compose.runtime.mutableStateOf
 | 
				
			||||||
import androidx.lifecycle.AndroidViewModel
 | 
					import androidx.lifecycle.AndroidViewModel
 | 
				
			||||||
import androidx.lifecycle.SavedStateHandle
 | 
					import androidx.lifecycle.SavedStateHandle
 | 
				
			||||||
import androidx.lifecycle.viewModelScope
 | 
					import androidx.lifecycle.viewModelScope
 | 
				
			||||||
import com.pixelized.rplexicon.R
 | 
					import com.pixelized.rplexicon.R
 | 
				
			||||||
 | 
					import com.pixelized.rplexicon.data.model.item.Item
 | 
				
			||||||
 | 
					import com.pixelized.rplexicon.data.network.ItemDto
 | 
				
			||||||
import com.pixelized.rplexicon.data.repository.character.DescriptionRepository
 | 
					import com.pixelized.rplexicon.data.repository.character.DescriptionRepository
 | 
				
			||||||
import com.pixelized.rplexicon.data.repository.character.EquipmentRepository
 | 
					import com.pixelized.rplexicon.data.repository.character.EquipmentRepository
 | 
				
			||||||
import com.pixelized.rplexicon.data.repository.character.InventoryRepository
 | 
					import com.pixelized.rplexicon.data.repository.character.ItemsRepository
 | 
				
			||||||
 | 
					import com.pixelized.rplexicon.data.repository.firebase.RealtimeDatabaseRepository
 | 
				
			||||||
import com.pixelized.rplexicon.ui.navigation.screens.characterSheetArgument
 | 
					import com.pixelized.rplexicon.ui.navigation.screens.characterSheetArgument
 | 
				
			||||||
import com.pixelized.rplexicon.ui.screens.character.composable.actions.EquipmentItemUio
 | 
					import com.pixelized.rplexicon.ui.screens.character.composable.actions.EquipmentItemUio
 | 
				
			||||||
import com.pixelized.rplexicon.ui.screens.character.composable.actions.InventoryItemUio
 | 
					 | 
				
			||||||
import com.pixelized.rplexicon.ui.screens.character.composable.dialogs.SkillDialogDetailUio
 | 
					import com.pixelized.rplexicon.ui.screens.character.composable.dialogs.SkillDialogDetailUio
 | 
				
			||||||
import com.pixelized.rplexicon.ui.screens.character.factory.ItemUioFactory
 | 
					import com.pixelized.rplexicon.ui.screens.character.factory.ItemUioFactory
 | 
				
			||||||
import com.pixelized.rplexicon.utilitary.extentions.context
 | 
					import com.pixelized.rplexicon.utilitary.extentions.context
 | 
				
			||||||
| 
						 | 
					@ -21,17 +26,23 @@ import com.pixelized.rplexicon.utilitary.extentions.uri
 | 
				
			||||||
import dagger.hilt.android.lifecycle.HiltViewModel
 | 
					import dagger.hilt.android.lifecycle.HiltViewModel
 | 
				
			||||||
import kotlinx.coroutines.Dispatchers
 | 
					import kotlinx.coroutines.Dispatchers
 | 
				
			||||||
import kotlinx.coroutines.flow.MutableSharedFlow
 | 
					import kotlinx.coroutines.flow.MutableSharedFlow
 | 
				
			||||||
 | 
					import kotlinx.coroutines.flow.MutableStateFlow
 | 
				
			||||||
import kotlinx.coroutines.flow.SharedFlow
 | 
					import kotlinx.coroutines.flow.SharedFlow
 | 
				
			||||||
 | 
					import kotlinx.coroutines.flow.SharingStarted
 | 
				
			||||||
 | 
					import kotlinx.coroutines.flow.combine
 | 
				
			||||||
 | 
					import kotlinx.coroutines.flow.map
 | 
				
			||||||
 | 
					import kotlinx.coroutines.flow.stateIn
 | 
				
			||||||
import kotlinx.coroutines.launch
 | 
					import kotlinx.coroutines.launch
 | 
				
			||||||
import kotlinx.coroutines.withContext
 | 
					import kotlinx.coroutines.withContext
 | 
				
			||||||
import javax.inject.Inject
 | 
					import javax.inject.Inject
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@HiltViewModel
 | 
					@HiltViewModel
 | 
				
			||||||
class InventoryViewModel @Inject constructor(
 | 
					class InventoryViewModel @Inject constructor(
 | 
				
			||||||
    private val inventoryRepository: InventoryRepository,
 | 
					 | 
				
			||||||
    private val equipmentRepository: EquipmentRepository,
 | 
					    private val equipmentRepository: EquipmentRepository,
 | 
				
			||||||
    private val descriptionRepository: DescriptionRepository,
 | 
					    private val descriptionRepository: DescriptionRepository,
 | 
				
			||||||
 | 
					    private val fireRepository: RealtimeDatabaseRepository,
 | 
				
			||||||
    private val itemFactory: ItemUioFactory,
 | 
					    private val itemFactory: ItemUioFactory,
 | 
				
			||||||
 | 
					    itemsRepository: ItemsRepository,
 | 
				
			||||||
    savedStateHandle: SavedStateHandle,
 | 
					    savedStateHandle: SavedStateHandle,
 | 
				
			||||||
    application: Application
 | 
					    application: Application
 | 
				
			||||||
) : AndroidViewModel(application) {
 | 
					) : AndroidViewModel(application) {
 | 
				
			||||||
| 
						 | 
					@ -40,8 +51,55 @@ class InventoryViewModel @Inject constructor(
 | 
				
			||||||
    private val _equipments = mutableStateOf<EquipmentItemUio?>(null)
 | 
					    private val _equipments = mutableStateOf<EquipmentItemUio?>(null)
 | 
				
			||||||
    val equipments: State<EquipmentItemUio?> get() = _equipments
 | 
					    val equipments: State<EquipmentItemUio?> get() = _equipments
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private val _inventory = mutableStateOf<List<InventoryItemUio>>(emptyList())
 | 
					    // Target the local indexes form specific ids (change locally to avoid flooding of firebase)
 | 
				
			||||||
    val inventory: State<List<InventoryItemUio>> get() = _inventory
 | 
					    private val itemLocalIndex =
 | 
				
			||||||
 | 
					        MutableStateFlow<Map<String, Int>>(hashMapOf())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // inversion of itemLocalIndex
 | 
				
			||||||
 | 
					    private val itemLocalId =
 | 
				
			||||||
 | 
					        itemLocalIndex.map { items -> items.map { it.value to it.key }.toMap() }.stateIn(
 | 
				
			||||||
 | 
					            scope = viewModelScope,
 | 
				
			||||||
 | 
					            started = SharingStarted.Eagerly,
 | 
				
			||||||
 | 
					            initialValue = emptyMap(),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // inventory for a specific character in firebase.
 | 
				
			||||||
 | 
					    private val fireInventory = fireRepository
 | 
				
			||||||
 | 
					        .getInventory(character = character)
 | 
				
			||||||
 | 
					        .stateIn(
 | 
				
			||||||
 | 
					            scope = viewModelScope,
 | 
				
			||||||
 | 
					            started = SharingStarted.Eagerly,
 | 
				
			||||||
 | 
					            initialValue = emptyList(),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // UI data, merge the firebase inventory full data and with the local index and transform it into UIO.
 | 
				
			||||||
 | 
					    private val _inventory = itemsRepository.data
 | 
				
			||||||
 | 
					        .combine(fireInventory) { items, fire ->
 | 
				
			||||||
 | 
					            Data().also {
 | 
				
			||||||
 | 
					                it.items = items
 | 
				
			||||||
 | 
					                it.fire = fire
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        .combine(itemLocalIndex) { data, indexes ->
 | 
				
			||||||
 | 
					            data.also {
 | 
				
			||||||
 | 
					                it.indexes = indexes
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        .map { data ->
 | 
				
			||||||
 | 
					            val (items, fire, indexes) = data
 | 
				
			||||||
 | 
					            itemFactory.toUio(items = items, fires = fire).sortedBy { indexes[it.id] }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        .stateIn(
 | 
				
			||||||
 | 
					            scope = viewModelScope,
 | 
				
			||||||
 | 
					            started = SharingStarted.Eagerly,
 | 
				
			||||||
 | 
					            initialValue = emptyList(),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    val inventory: State<List<InventoryItemUio>>
 | 
				
			||||||
 | 
					        @Composable
 | 
				
			||||||
 | 
					        @Stable
 | 
				
			||||||
 | 
					        get() {
 | 
				
			||||||
 | 
					            return _inventory.collectAsState(emptyList())
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private val _dialog = mutableStateOf<SkillDialogDetailUio?>(null)
 | 
					    private val _dialog = mutableStateOf<SkillDialogDetailUio?>(null)
 | 
				
			||||||
    val dialog: State<SkillDialogDetailUio?> get() = _dialog
 | 
					    val dialog: State<SkillDialogDetailUio?> get() = _dialog
 | 
				
			||||||
| 
						 | 
					@ -52,11 +110,14 @@ class InventoryViewModel @Inject constructor(
 | 
				
			||||||
    init {
 | 
					    init {
 | 
				
			||||||
        viewModelScope.launch {
 | 
					        viewModelScope.launch {
 | 
				
			||||||
            launch(Dispatchers.IO) {
 | 
					            launch(Dispatchers.IO) {
 | 
				
			||||||
                inventoryRepository.data.collect { inventories ->
 | 
					                fireRepository.getInventory(character).collect { items ->
 | 
				
			||||||
                    val items = inventories[character]?.items?.map { itemFactory.toUio(it) }
 | 
					                    itemLocalIndex.value = items.mapNotNull {
 | 
				
			||||||
                    withContext(Dispatchers.Main) {
 | 
					                        if (it.id != null) {
 | 
				
			||||||
                        _inventory.value = items ?: emptyList()
 | 
					                            it.id as String to items.indexOf(it)
 | 
				
			||||||
                    }
 | 
					                        } else {
 | 
				
			||||||
 | 
					                            null
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }.toMap()
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            launch(Dispatchers.IO) {
 | 
					            launch(Dispatchers.IO) {
 | 
				
			||||||
| 
						 | 
					@ -85,6 +146,40 @@ class InventoryViewModel @Inject constructor(
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            launch(Dispatchers.IO) {
 | 
				
			||||||
 | 
					                fireRepository.setInventory(
 | 
				
			||||||
 | 
					                    character = character,
 | 
				
			||||||
 | 
					                    inventory = listOf(
 | 
				
			||||||
 | 
					                        ItemDto(
 | 
				
			||||||
 | 
					                            id = "7d27561b-f2f4-4899-a2fc-df3501b1b66b",
 | 
				
			||||||
 | 
					                            amount = 1,
 | 
				
			||||||
 | 
					                            children = listOf(
 | 
				
			||||||
 | 
					                                ItemDto(
 | 
				
			||||||
 | 
					                                    id = "bd1400bf-9d1c-480c-873b-0539ac82bfdb",
 | 
				
			||||||
 | 
					                                    amount = 32,
 | 
				
			||||||
 | 
					                                ),
 | 
				
			||||||
 | 
					                                ItemDto(
 | 
				
			||||||
 | 
					                                    id = "85f778ae-11c7-47a9-bddc-f6d8a1fd03dc",
 | 
				
			||||||
 | 
					                                    amount = 5,
 | 
				
			||||||
 | 
					                                ),
 | 
				
			||||||
 | 
					                                ItemDto(
 | 
				
			||||||
 | 
					                                    id = "950d9a2b-fc1a-4989-bb92-930de98f7ea4",
 | 
				
			||||||
 | 
					                                    amount = 15,
 | 
				
			||||||
 | 
					                                ),
 | 
				
			||||||
 | 
					                            ),
 | 
				
			||||||
 | 
					                        ),
 | 
				
			||||||
 | 
					                        ItemDto(
 | 
				
			||||||
 | 
					                            id = "1c69559c-b96f-4600-99b3-85c07cb528e0",
 | 
				
			||||||
 | 
					                            amount = 1,
 | 
				
			||||||
 | 
					                        ),
 | 
				
			||||||
 | 
					                        ItemDto(
 | 
				
			||||||
 | 
					                            id = "1a5931c6-bb45-43ea-ad25-207aac820383",
 | 
				
			||||||
 | 
					                            amount = 1,
 | 
				
			||||||
 | 
					                        ),
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -109,4 +204,75 @@ class InventoryViewModel @Inject constructor(
 | 
				
			||||||
    fun hideSkillDetailDialog() {
 | 
					    fun hideSkillDetailDialog() {
 | 
				
			||||||
        _dialog.value = null
 | 
					        _dialog.value = null
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fun onMove(fromIndex: Int, toIndex: Int) {
 | 
				
			||||||
 | 
					        // item have been moved, need to update the local item index.
 | 
				
			||||||
 | 
					        itemLocalIndex.value = itemLocalIndex.value.toMutableMap().also { map ->
 | 
				
			||||||
 | 
					            val fromId = map.firstNotNullOfOrNull { if (it.value == fromIndex) it.key else null }
 | 
				
			||||||
 | 
					            val toId = map.firstNotNullOfOrNull { if (it.value == toIndex) it.key else null }
 | 
				
			||||||
 | 
					            if (fromId != null && toId != null) {
 | 
				
			||||||
 | 
					                map[fromId] = toIndex
 | 
				
			||||||
 | 
					                map[toId] = fromIndex
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fun isMoveEnable(fromIndex: Int, toIndex: Int): Boolean {
 | 
				
			||||||
 | 
					        val receiver = _inventory.value[toIndex]
 | 
				
			||||||
 | 
					        return !receiver.container
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fun onDrop(index: Int) {
 | 
				
			||||||
 | 
					        val source = _inventory.value[index]
 | 
				
			||||||
 | 
					        val fireIndex = fireInventory.value.indexOfFirst { it.id == source.id }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        val inventory = fireInventory.value.toMutableList().also { fireInventory ->
 | 
				
			||||||
 | 
					            fireInventory.add(index, fireInventory.removeAt(fireIndex))
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fireRepository.setInventory(
 | 
				
			||||||
 | 
					            character = character,
 | 
				
			||||||
 | 
					            inventory = inventory,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fun onDropOver(fromIndex: Int, toIndex: Int) {
 | 
				
			||||||
 | 
					        val source = _inventory.value[fromIndex]
 | 
				
			||||||
 | 
					        val receiver = _inventory.value[toIndex]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (fromIndex != toIndex && receiver.container) {
 | 
				
			||||||
 | 
					            val inventory = fireInventory.value.toMutableList().also { fireInventory ->
 | 
				
			||||||
 | 
					                val sourceIndex = fireInventory.indexOfFirst { it.id == source.id }
 | 
				
			||||||
 | 
					                val receiverIndex = fireInventory.indexOfFirst { it.id == receiver.id }
 | 
				
			||||||
 | 
					                val sourceFireItem = fireInventory.getOrNull(sourceIndex)
 | 
				
			||||||
 | 
					                val receiverFireItem = fireInventory.getOrNull(receiverIndex)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (sourceFireItem != null && receiverFireItem != null) {
 | 
				
			||||||
 | 
					                    fireInventory.remove(sourceFireItem)
 | 
				
			||||||
 | 
					                    fireInventory.remove(receiverFireItem)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    fireInventory.add(
 | 
				
			||||||
 | 
					                        receiverIndex,
 | 
				
			||||||
 | 
					                        receiverFireItem.copy(
 | 
				
			||||||
 | 
					                            children = receiverFireItem.children.toMutableList()
 | 
				
			||||||
 | 
					                                .also { it.add(sourceFireItem) },
 | 
				
			||||||
 | 
					                        )
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            fireRepository.setInventory(
 | 
				
			||||||
 | 
					                character = character,
 | 
				
			||||||
 | 
					                inventory = inventory,
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private class Data {
 | 
				
			||||||
 | 
					        lateinit var items: Map<String, Item>
 | 
				
			||||||
 | 
					        lateinit var fire: List<ItemDto>
 | 
				
			||||||
 | 
					        lateinit var indexes: Map<String, Int>
 | 
				
			||||||
 | 
					        operator fun component1() = items
 | 
				
			||||||
 | 
					        operator fun component2() = fire
 | 
				
			||||||
 | 
					        operator fun component3() = indexes
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -1,254 +0,0 @@
 | 
				
			||||||
package com.pixelized.rplexicon.ui.screens.character.pages.inventory
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import android.content.res.Configuration
 | 
					 | 
				
			||||||
import androidx.compose.animation.animateColor
 | 
					 | 
				
			||||||
import androidx.compose.animation.core.updateTransition
 | 
					 | 
				
			||||||
import androidx.compose.foundation.layout.Arrangement
 | 
					 | 
				
			||||||
import androidx.compose.foundation.layout.Box
 | 
					 | 
				
			||||||
import androidx.compose.foundation.layout.PaddingValues
 | 
					 | 
				
			||||||
import androidx.compose.foundation.layout.fillMaxSize
 | 
					 | 
				
			||||||
import androidx.compose.foundation.lazy.grid.GridCells
 | 
					 | 
				
			||||||
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
 | 
					 | 
				
			||||||
import androidx.compose.foundation.lazy.grid.itemsIndexed
 | 
					 | 
				
			||||||
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
 | 
					 | 
				
			||||||
import androidx.compose.foundation.shape.RoundedCornerShape
 | 
					 | 
				
			||||||
import androidx.compose.material3.MaterialTheme
 | 
					 | 
				
			||||||
import androidx.compose.material3.Surface
 | 
					 | 
				
			||||||
import androidx.compose.material3.surfaceColorAtElevation
 | 
					 | 
				
			||||||
import androidx.compose.runtime.Composable
 | 
					 | 
				
			||||||
import androidx.compose.runtime.mutableIntStateOf
 | 
					 | 
				
			||||||
import androidx.compose.runtime.mutableStateOf
 | 
					 | 
				
			||||||
import androidx.compose.runtime.remember
 | 
					 | 
				
			||||||
import androidx.compose.ui.Modifier
 | 
					 | 
				
			||||||
import androidx.compose.ui.graphics.compositeOver
 | 
					 | 
				
			||||||
import androidx.compose.ui.tooling.preview.Preview
 | 
					 | 
				
			||||||
import androidx.compose.ui.unit.dp
 | 
					 | 
				
			||||||
import com.pixelized.rplexicon.R
 | 
					 | 
				
			||||||
import com.pixelized.rplexicon.ui.theme.LexiconTheme
 | 
					 | 
				
			||||||
import com.pixelized.rplexicon.utilitary.DraggableItem
 | 
					 | 
				
			||||||
import com.pixelized.rplexicon.utilitary.dragContainer
 | 
					 | 
				
			||||||
import com.pixelized.rplexicon.utilitary.extentions.lexicon
 | 
					 | 
				
			||||||
import com.pixelized.rplexicon.utilitary.extentions.modifier.doubleBorder
 | 
					 | 
				
			||||||
import com.pixelized.rplexicon.utilitary.rememberGridDragDropState
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@Composable
 | 
					 | 
				
			||||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
 | 
					 | 
				
			||||||
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
 | 
					 | 
				
			||||||
fun Pouet() {
 | 
					 | 
				
			||||||
    val items = remember {
 | 
					 | 
				
			||||||
        mutableStateOf(
 | 
					 | 
				
			||||||
            listOf(
 | 
					 | 
				
			||||||
                InventoryItemUio(
 | 
					 | 
				
			||||||
                    id = 0,
 | 
					 | 
				
			||||||
                    name = "Pouch",
 | 
					 | 
				
			||||||
                    amount = 1,
 | 
					 | 
				
			||||||
                    container = true,
 | 
					 | 
				
			||||||
                    icon = R.drawable.icbg_pouch_a_unfaded,
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
                InventoryItemUio(
 | 
					 | 
				
			||||||
                    id = 1,
 | 
					 | 
				
			||||||
                    name = "Scroll of blessing",
 | 
					 | 
				
			||||||
                    amount = 1,
 | 
					 | 
				
			||||||
                    container = false,
 | 
					 | 
				
			||||||
                    icon = R.drawable.icbg_scroll_of_bless_unfaded,
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
                InventoryItemUio(
 | 
					 | 
				
			||||||
                    id = 2,
 | 
					 | 
				
			||||||
                    name = "Scroll of spirit weapon",
 | 
					 | 
				
			||||||
                    amount = 1,
 | 
					 | 
				
			||||||
                    container = false,
 | 
					 | 
				
			||||||
                    icon = R.drawable.icbg_book_signedtradebisa_unfaded,
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
                InventoryItemUio(
 | 
					 | 
				
			||||||
                    id = 3,
 | 
					 | 
				
			||||||
                    name = "Potion of healing",
 | 
					 | 
				
			||||||
                    amount = 2,
 | 
					 | 
				
			||||||
                    container = false,
 | 
					 | 
				
			||||||
                    icon = R.drawable.icbg_potion_of_superior_healing_unfaded,
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
                InventoryItemUio(
 | 
					 | 
				
			||||||
                    id = 4,
 | 
					 | 
				
			||||||
                    name = "Potion of supérior healing",
 | 
					 | 
				
			||||||
                    amount = 2,
 | 
					 | 
				
			||||||
                    container = false,
 | 
					 | 
				
			||||||
                    icon = R.drawable.icbg_pot_potion_of_healing_unfaded,
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
                InventoryItemUio(
 | 
					 | 
				
			||||||
                    id = 5,
 | 
					 | 
				
			||||||
                    name = "Potion of holy water",
 | 
					 | 
				
			||||||
                    amount = 2,
 | 
					 | 
				
			||||||
                    container = false,
 | 
					 | 
				
			||||||
                    icon = R.drawable.icbg_grn_holy_water_unfaded,
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
                InventoryItemUio(
 | 
					 | 
				
			||||||
                    id = 6,
 | 
					 | 
				
			||||||
                    name = "Leather armor",
 | 
					 | 
				
			||||||
                    amount = 1,
 | 
					 | 
				
			||||||
                    container = false,
 | 
					 | 
				
			||||||
                    icon = R.drawable.icbg_leather_armour_rogue_unfaded,
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
                InventoryItemUio(
 | 
					 | 
				
			||||||
                    id = 7,
 | 
					 | 
				
			||||||
                    name = "Silver battleaxe",
 | 
					 | 
				
			||||||
                    amount = 0,
 | 
					 | 
				
			||||||
                    container = false,
 | 
					 | 
				
			||||||
                    icon = R.drawable.icbg_battleaxe_plus_one_unfaded,
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
                InventoryItemUio(
 | 
					 | 
				
			||||||
                    id = 8,
 | 
					 | 
				
			||||||
                    name = "Hand crossbow",
 | 
					 | 
				
			||||||
                    amount = 0,
 | 
					 | 
				
			||||||
                    container = false,
 | 
					 | 
				
			||||||
                    icon = R.drawable.icbg_hand_crossbow_unfaded,
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
                InventoryItemUio(
 | 
					 | 
				
			||||||
                    id = 9,
 | 
					 | 
				
			||||||
                    name = "Goodberry",
 | 
					 | 
				
			||||||
                    amount = 0,
 | 
					 | 
				
			||||||
                    container = false,
 | 
					 | 
				
			||||||
                    icon = R.drawable.icbg_food_goodberry_unfaded,
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
                InventoryItemUio(
 | 
					 | 
				
			||||||
                    id = 10,
 | 
					 | 
				
			||||||
                    name = "Goodberry",
 | 
					 | 
				
			||||||
                    amount = 0,
 | 
					 | 
				
			||||||
                    container = false,
 | 
					 | 
				
			||||||
                    icon = R.drawable.icbg_worg_fang_unfaded,
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
                InventoryItemUio(
 | 
					 | 
				
			||||||
                    id = 11,
 | 
					 | 
				
			||||||
                    name = "Lantern of revealing",
 | 
					 | 
				
			||||||
                    amount = 0,
 | 
					 | 
				
			||||||
                    container = false,
 | 
					 | 
				
			||||||
                    icon = R.drawable.icbg_lantern_of_revealing,
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
                InventoryItemUio(
 | 
					 | 
				
			||||||
                    id = 12,
 | 
					 | 
				
			||||||
                    name = "Dust of disappearance",
 | 
					 | 
				
			||||||
                    amount = 0,
 | 
					 | 
				
			||||||
                    container = false,
 | 
					 | 
				
			||||||
                    icon = R.drawable.icbg_haste_spore_grenade_unfaded,
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
                InventoryItemUio(
 | 
					 | 
				
			||||||
                    id = 13,
 | 
					 | 
				
			||||||
                    name = "Pouch",
 | 
					 | 
				
			||||||
                    amount = 1,
 | 
					 | 
				
			||||||
                    container = true,
 | 
					 | 
				
			||||||
                    icon = R.drawable.icbg_pouch_a_unfaded,
 | 
					 | 
				
			||||||
                ),
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    val overIndex = remember { mutableIntStateOf(-1) }
 | 
					 | 
				
			||||||
    val contentPadding = remember { PaddingValues(16.dp) }
 | 
					 | 
				
			||||||
    val gridState = rememberLazyGridState()
 | 
					 | 
				
			||||||
    val dragDropState = rememberGridDragDropState(
 | 
					 | 
				
			||||||
        contentPadding = contentPadding,
 | 
					 | 
				
			||||||
        gridState = gridState,
 | 
					 | 
				
			||||||
        onMove = { fromIndex, toIndex ->
 | 
					 | 
				
			||||||
            items.value = items.value.toMutableList().apply {
 | 
					 | 
				
			||||||
                add(toIndex, removeAt(fromIndex))
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        onOver = { _, toIndex ->
 | 
					 | 
				
			||||||
            val receiver = items.value[toIndex]
 | 
					 | 
				
			||||||
            if (receiver.container) {
 | 
					 | 
				
			||||||
                overIndex.intValue = toIndex
 | 
					 | 
				
			||||||
                false
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                true
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        onDrop = { fromIndex, toIndex ->
 | 
					 | 
				
			||||||
            if (fromIndex != toIndex) {
 | 
					 | 
				
			||||||
                val receiver = items.value[toIndex]
 | 
					 | 
				
			||||||
                if (receiver.container) {
 | 
					 | 
				
			||||||
                    items.value = items.value.toMutableList().apply {
 | 
					 | 
				
			||||||
                        val item = removeAt(fromIndex)
 | 
					 | 
				
			||||||
                        val receiverCopy = receiver.copy(
 | 
					 | 
				
			||||||
                            items = receiver.items.toMutableList().also {
 | 
					 | 
				
			||||||
                                it.add(item)
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                        )
 | 
					 | 
				
			||||||
                        val receiverIndex = indexOf(receiver)
 | 
					 | 
				
			||||||
                        removeAt(receiverIndex)
 | 
					 | 
				
			||||||
                        add(receiverIndex, receiverCopy)
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    LexiconTheme {
 | 
					 | 
				
			||||||
        Surface {
 | 
					 | 
				
			||||||
            LazyVerticalGrid(
 | 
					 | 
				
			||||||
                columns = GridCells.Fixed(5),
 | 
					 | 
				
			||||||
                modifier = Modifier
 | 
					 | 
				
			||||||
                    .dragContainer(dragDropState)
 | 
					 | 
				
			||||||
                    .fillMaxSize(),
 | 
					 | 
				
			||||||
                state = gridState,
 | 
					 | 
				
			||||||
                contentPadding = contentPadding,
 | 
					 | 
				
			||||||
                verticalArrangement = Arrangement.spacedBy(8.dp),
 | 
					 | 
				
			||||||
                horizontalArrangement = Arrangement.spacedBy(8.dp),
 | 
					 | 
				
			||||||
            ) {
 | 
					 | 
				
			||||||
                itemsIndexed(
 | 
					 | 
				
			||||||
                    items = items.value,
 | 
					 | 
				
			||||||
                    key = { _, item -> item.id },
 | 
					 | 
				
			||||||
                ) { index, item ->
 | 
					 | 
				
			||||||
                    DraggableItem(
 | 
					 | 
				
			||||||
                        dragDropState = dragDropState,
 | 
					 | 
				
			||||||
                        index = index
 | 
					 | 
				
			||||||
                    ) { isDragging, isOvering ->
 | 
					 | 
				
			||||||
                        val colorScheme = MaterialTheme.lexicon.colorScheme
 | 
					 | 
				
			||||||
                        val transition = updateTransition(
 | 
					 | 
				
			||||||
                            targetState = isDragging || isOvering ,
 | 
					 | 
				
			||||||
                            label = "Dragging transition",
 | 
					 | 
				
			||||||
                        )
 | 
					 | 
				
			||||||
                        val backgroundColor = transition.animateColor(
 | 
					 | 
				
			||||||
                            label = "Draggable item background color",
 | 
					 | 
				
			||||||
                        ) {
 | 
					 | 
				
			||||||
                            val surface = colorScheme.base.surfaceColorAtElevation(2.dp)
 | 
					 | 
				
			||||||
                            when (it) {
 | 
					 | 
				
			||||||
                                true -> colorScheme.base.primary.copy(alpha = 0.15f)
 | 
					 | 
				
			||||||
                                    .compositeOver(surface)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                                else -> surface
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                        val outlineColor = transition.animateColor(
 | 
					 | 
				
			||||||
                            label = "Draggable item outline color",
 | 
					 | 
				
			||||||
                        ) {
 | 
					 | 
				
			||||||
                            when (it) {
 | 
					 | 
				
			||||||
                                true -> colorScheme.base.primary
 | 
					 | 
				
			||||||
                                else -> colorScheme.characterSheet.outlineBorder
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                        val innerColor = transition.animateColor(
 | 
					 | 
				
			||||||
                            label = "Draggable item inline color",
 | 
					 | 
				
			||||||
                        ) {
 | 
					 | 
				
			||||||
                            when (it) {
 | 
					 | 
				
			||||||
                                true -> colorScheme.base.primary
 | 
					 | 
				
			||||||
                                else -> colorScheme.characterSheet.innerBorder
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                        Box(
 | 
					 | 
				
			||||||
                            modifier = Modifier.doubleBorder(
 | 
					 | 
				
			||||||
                                backgroundColor = backgroundColor.value,
 | 
					 | 
				
			||||||
                                outline = remember { RoundedCornerShape(8.dp) },
 | 
					 | 
				
			||||||
                                outlineColor = outlineColor.value,
 | 
					 | 
				
			||||||
                                inner = remember { RoundedCornerShape(6.dp) },
 | 
					 | 
				
			||||||
                                innerColor = innerColor.value,
 | 
					 | 
				
			||||||
                            )
 | 
					 | 
				
			||||||
                        ) {
 | 
					 | 
				
			||||||
                            InventoryItem(
 | 
					 | 
				
			||||||
                                item = item,
 | 
					 | 
				
			||||||
                            )
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -47,15 +47,17 @@ fun rememberGridDragDropState(
 | 
				
			||||||
    contentPadding: PaddingValues,
 | 
					    contentPadding: PaddingValues,
 | 
				
			||||||
    gridState: LazyGridState,
 | 
					    gridState: LazyGridState,
 | 
				
			||||||
    onMove: (Int, Int) -> Unit,
 | 
					    onMove: (Int, Int) -> Unit,
 | 
				
			||||||
    onOver: (Int, Int) -> Boolean,
 | 
					    isMoveEnable: (Int, Int) -> Boolean,
 | 
				
			||||||
    onDrop: (Int, Int) -> Unit,
 | 
					    onDragRelease: (Int) -> Unit,
 | 
				
			||||||
 | 
					    onDropReleaseOver: (Int, Int) -> Unit,
 | 
				
			||||||
): GridDragDropState {
 | 
					): GridDragDropState {
 | 
				
			||||||
    val density = LocalDensity.current
 | 
					    val density = LocalDensity.current
 | 
				
			||||||
    val layoutDirection = LocalLayoutDirection.current
 | 
					    val layoutDirection = LocalLayoutDirection.current
 | 
				
			||||||
    val scope = rememberCoroutineScope()
 | 
					    val scope = rememberCoroutineScope()
 | 
				
			||||||
    val currentOnMove = rememberUpdatedState(newValue = onMove)
 | 
					    val currentOnMove = rememberUpdatedState(newValue = onMove)
 | 
				
			||||||
    val currentOnOver = rememberUpdatedState(newValue = onOver)
 | 
					    val currentIsMoveEnable = rememberUpdatedState(newValue = isMoveEnable)
 | 
				
			||||||
    val currentOnDrop = rememberUpdatedState(newValue = onDrop)
 | 
					    val currentOnDragRelease = rememberUpdatedState(newValue = onDragRelease)
 | 
				
			||||||
 | 
					    val currentOnDropReleaseOver = rememberUpdatedState(newValue = onDropReleaseOver)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    val state = remember(gridState, scope, currentOnMove) {
 | 
					    val state = remember(gridState, scope, currentOnMove) {
 | 
				
			||||||
        GridDragDropState(
 | 
					        GridDragDropState(
 | 
				
			||||||
| 
						 | 
					@ -68,8 +70,9 @@ fun rememberGridDragDropState(
 | 
				
			||||||
            scope = scope,
 | 
					            scope = scope,
 | 
				
			||||||
            state = gridState,
 | 
					            state = gridState,
 | 
				
			||||||
            onMove = currentOnMove.value,
 | 
					            onMove = currentOnMove.value,
 | 
				
			||||||
            onOver = currentOnOver.value,
 | 
					            isMoveEnable = currentIsMoveEnable.value,
 | 
				
			||||||
            onDrop = currentOnDrop.value,
 | 
					            onDragRelease = currentOnDragRelease.value,
 | 
				
			||||||
 | 
					            onDropReleaseOver = currentOnDropReleaseOver.value,
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -88,34 +91,38 @@ class GridDragDropState internal constructor(
 | 
				
			||||||
    private val scope: CoroutineScope,
 | 
					    private val scope: CoroutineScope,
 | 
				
			||||||
    private val state: LazyGridState,
 | 
					    private val state: LazyGridState,
 | 
				
			||||||
    private val onMove: (Int, Int) -> Unit,
 | 
					    private val onMove: (Int, Int) -> Unit,
 | 
				
			||||||
    private val onOver: (Int, Int) -> Boolean,
 | 
					    private val isMoveEnable: (Int, Int) -> Boolean,
 | 
				
			||||||
    private val onDrop: (Int, Int) -> Unit,
 | 
					    private val onDragRelease: (Int) -> Unit,
 | 
				
			||||||
 | 
					    private val onDropReleaseOver: (Int, Int) -> Unit,
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
    internal val scrollChannel = Channel<Float>()
 | 
					    internal val scrollChannel = Channel<Float>()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var overItemIndex by mutableStateOf<Int?>(null)
 | 
				
			||||||
 | 
					        private set
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private var previousItemIndex by mutableStateOf<Int?>(null)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var draggingItemIndex by mutableStateOf<Int?>(null)
 | 
					    var draggingItemIndex by mutableStateOf<Int?>(null)
 | 
				
			||||||
        private set
 | 
					        private set
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var overItemIndex by mutableStateOf<Int?>(null)
 | 
					    internal var draggingItemLingeringIndex by mutableStateOf<Int?>(null)
 | 
				
			||||||
        private set
 | 
					        private set
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private var draggingItemInitialOffset by mutableStateOf(Offset.Zero)
 | 
					    private var draggingItemInitialOffset by mutableStateOf(Offset.Zero)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private var draggingItemDraggedDelta by mutableStateOf(Offset.Zero)
 | 
					    private var draggingItemDraggedDelta by mutableStateOf(Offset.Zero)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private val draggingItemLayoutInfo: LazyGridItemInfo?
 | 
				
			||||||
 | 
					        get() = state.layoutInfo.visibleItemsInfo
 | 
				
			||||||
 | 
					            .firstOrNull { it.index == draggingItemIndex }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    internal val draggingItemOffset: Offset
 | 
					    internal val draggingItemOffset: Offset
 | 
				
			||||||
        get() = draggingItemLayoutInfo
 | 
					        get() = draggingItemLayoutInfo
 | 
				
			||||||
            ?.let { item -> draggingItemInitialOffset + draggingItemDraggedDelta - item.offset.toOffset() }
 | 
					            ?.let { item -> draggingItemInitialOffset + draggingItemDraggedDelta - item.offset.toOffset() }
 | 
				
			||||||
            ?: Offset.Zero
 | 
					            ?: Offset.Zero
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private val draggingItemLayoutInfo: LazyGridItemInfo?
 | 
					    internal var draggingItemSetIntoPositionAnimation =
 | 
				
			||||||
        get() = state.layoutInfo.visibleItemsInfo
 | 
					        Animatable(initialValue = Offset.Zero, typeConverter = Offset.VectorConverter)
 | 
				
			||||||
            .firstOrNull { it.index == draggingItemIndex }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    internal var previousIndexOfDraggedItem by mutableStateOf<Int?>(null)
 | 
					 | 
				
			||||||
        private set
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    internal var previousItemOffset = Animatable(Offset.Zero, Offset.VectorConverter)
 | 
					 | 
				
			||||||
        private set
 | 
					        private set
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    internal fun onDragStart(offset: Offset) {
 | 
					    internal fun onDragStart(offset: Offset) {
 | 
				
			||||||
| 
						 | 
					@ -127,33 +134,6 @@ class GridDragDropState internal constructor(
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    internal fun onDragInterrupted(offset: Offset) {
 | 
					 | 
				
			||||||
        val localDraggingItemIndex = draggingItemIndex
 | 
					 | 
				
			||||||
        if (localDraggingItemIndex != null) {
 | 
					 | 
				
			||||||
            state.layoutInfo.visibleItemsInfo
 | 
					 | 
				
			||||||
                .firstItemWithOffsetOrNull(offset)
 | 
					 | 
				
			||||||
                ?.let { onDrop(localDraggingItemIndex, it.index) }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            previousIndexOfDraggedItem = draggingItemIndex
 | 
					 | 
				
			||||||
            val startOffset = draggingItemOffset
 | 
					 | 
				
			||||||
            scope.launch {
 | 
					 | 
				
			||||||
                previousItemOffset.snapTo(startOffset)
 | 
					 | 
				
			||||||
                previousItemOffset.animateTo(
 | 
					 | 
				
			||||||
                    Offset.Zero,
 | 
					 | 
				
			||||||
                    spring(
 | 
					 | 
				
			||||||
                        stiffness = Spring.StiffnessMediumLow,
 | 
					 | 
				
			||||||
                        visibilityThreshold = Offset.VisibilityThreshold
 | 
					 | 
				
			||||||
                    )
 | 
					 | 
				
			||||||
                )
 | 
					 | 
				
			||||||
                previousIndexOfDraggedItem = null
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        overItemIndex = null
 | 
					 | 
				
			||||||
        draggingItemDraggedDelta = Offset.Zero
 | 
					 | 
				
			||||||
        draggingItemIndex = null
 | 
					 | 
				
			||||||
        draggingItemInitialOffset = Offset.Zero
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    internal fun onDrag(offset: Offset) {
 | 
					    internal fun onDrag(offset: Offset) {
 | 
				
			||||||
        draggingItemDraggedDelta += offset
 | 
					        draggingItemDraggedDelta += offset
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -170,15 +150,17 @@ class GridDragDropState internal constructor(
 | 
				
			||||||
                    item.offset.x <= middleXOffset && middleXOffset <= item.offsetEnd.x &&
 | 
					                    item.offset.x <= middleXOffset && middleXOffset <= item.offsetEnd.x &&
 | 
				
			||||||
                    item.offset.y <= middleYOffset && middleYOffset <= item.offsetEnd.y
 | 
					                    item.offset.y <= middleYOffset && middleYOffset <= item.offsetEnd.y
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					 | 
				
			||||||
        overItemIndex = targetItem?.index
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (targetItem != null) {
 | 
					        if (targetItem != null) {
 | 
				
			||||||
            if (onOver.invoke(draggingItem.index, targetItem.index)) {
 | 
					            if (isMoveEnable.invoke(draggingItem.index, targetItem.index)) {
 | 
				
			||||||
                onMove.invoke(draggingItem.index, targetItem.index)
 | 
					                onMove.invoke(draggingItem.index, targetItem.index)
 | 
				
			||||||
 | 
					                previousItemIndex = draggingItemIndex
 | 
				
			||||||
                draggingItemIndex = targetItem.index
 | 
					                draggingItemIndex = targetItem.index
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                previousItemIndex = null
 | 
				
			||||||
 | 
					                overItemIndex = targetItem.index
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
 | 
					            overItemIndex = null
 | 
				
			||||||
            val overscroll = when {
 | 
					            val overscroll = when {
 | 
				
			||||||
                draggingItemDraggedDelta.y > 0 ->
 | 
					                draggingItemDraggedDelta.y > 0 ->
 | 
				
			||||||
                    (endOffset.y - state.layoutInfo.viewportEndOffset).coerceAtLeast(0f)
 | 
					                    (endOffset.y - state.layoutInfo.viewportEndOffset).coerceAtLeast(0f)
 | 
				
			||||||
| 
						 | 
					@ -194,6 +176,79 @@ class GridDragDropState internal constructor(
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    internal fun onDragInterrupted(offset: Offset) {
 | 
				
			||||||
 | 
					        val localDraggingItemIndex = draggingItemIndex
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (localDraggingItemIndex != null) {
 | 
				
			||||||
 | 
					            val target = state.layoutInfo.visibleItemsInfo.firstItemWithOffsetOrNull(offset)
 | 
				
			||||||
 | 
					            when {
 | 
				
			||||||
 | 
					                target == null && previousItemIndex != draggingItemIndex -> {
 | 
				
			||||||
 | 
					                    // release have occur on the current place of the item but outside of range
 | 
				
			||||||
 | 
					                    onDragRelease(localDraggingItemIndex)
 | 
				
			||||||
 | 
					                    // play a animation so the item fit in place.
 | 
				
			||||||
 | 
					                    draggingItemLingeringIndex = draggingItemIndex
 | 
				
			||||||
 | 
					                    val startOffset = draggingItemOffset
 | 
				
			||||||
 | 
					                    scope.launch {
 | 
				
			||||||
 | 
					                        draggingItemSetIntoPositionAnimation.snapTo(targetValue = startOffset)
 | 
				
			||||||
 | 
					                        draggingItemSetIntoPositionAnimation.animateTo(
 | 
				
			||||||
 | 
					                            targetValue = Offset.Zero,
 | 
				
			||||||
 | 
					                            animationSpec = spring(
 | 
				
			||||||
 | 
					                                stiffness = Spring.StiffnessMediumLow,
 | 
				
			||||||
 | 
					                                visibilityThreshold = Offset.VisibilityThreshold
 | 
				
			||||||
 | 
					                            )
 | 
				
			||||||
 | 
					                        )
 | 
				
			||||||
 | 
					                        draggingItemLingeringIndex = null
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                target == null -> {
 | 
				
			||||||
 | 
					                    // release have occur outside valid range. play and animation the make the item go back in place.
 | 
				
			||||||
 | 
					                    draggingItemLingeringIndex = draggingItemIndex
 | 
				
			||||||
 | 
					                    val startOffset = draggingItemOffset
 | 
				
			||||||
 | 
					                    scope.launch {
 | 
				
			||||||
 | 
					                        draggingItemSetIntoPositionAnimation.snapTo(targetValue = startOffset)
 | 
				
			||||||
 | 
					                        draggingItemSetIntoPositionAnimation.animateTo(
 | 
				
			||||||
 | 
					                            targetValue = Offset.Zero,
 | 
				
			||||||
 | 
					                            animationSpec = spring(
 | 
				
			||||||
 | 
					                                stiffness = Spring.StiffnessMediumLow,
 | 
				
			||||||
 | 
					                                visibilityThreshold = Offset.VisibilityThreshold
 | 
				
			||||||
 | 
					                            )
 | 
				
			||||||
 | 
					                        )
 | 
				
			||||||
 | 
					                        draggingItemLingeringIndex = null
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                target.index == localDraggingItemIndex -> {
 | 
				
			||||||
 | 
					                    // release have occur on the current place of the item
 | 
				
			||||||
 | 
					                    onDragRelease(localDraggingItemIndex)
 | 
				
			||||||
 | 
					                    // play a animation so the item fit in place.
 | 
				
			||||||
 | 
					                    draggingItemLingeringIndex = draggingItemIndex
 | 
				
			||||||
 | 
					                    val startOffset = draggingItemOffset
 | 
				
			||||||
 | 
					                    scope.launch {
 | 
				
			||||||
 | 
					                        draggingItemSetIntoPositionAnimation.snapTo(targetValue = startOffset)
 | 
				
			||||||
 | 
					                        draggingItemSetIntoPositionAnimation.animateTo(
 | 
				
			||||||
 | 
					                            targetValue = Offset.Zero,
 | 
				
			||||||
 | 
					                            animationSpec = spring(
 | 
				
			||||||
 | 
					                                stiffness = Spring.StiffnessMediumLow,
 | 
				
			||||||
 | 
					                                visibilityThreshold = Offset.VisibilityThreshold
 | 
				
			||||||
 | 
					                            )
 | 
				
			||||||
 | 
					                        )
 | 
				
			||||||
 | 
					                        draggingItemLingeringIndex = null
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                target.index != localDraggingItemIndex -> {
 | 
				
			||||||
 | 
					                    onDropReleaseOver(localDraggingItemIndex, target.index)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        previousItemIndex = null
 | 
				
			||||||
 | 
					        overItemIndex = null
 | 
				
			||||||
 | 
					        draggingItemDraggedDelta = Offset.Zero
 | 
				
			||||||
 | 
					        draggingItemIndex = null
 | 
				
			||||||
 | 
					        draggingItemInitialOffset = Offset.Zero
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private fun List<LazyGridItemInfo>.firstItemWithOffsetOrNull(
 | 
					    private fun List<LazyGridItemInfo>.firstItemWithOffsetOrNull(
 | 
				
			||||||
        offset: Offset,
 | 
					        offset: Offset,
 | 
				
			||||||
    ): LazyGridItemInfo? {
 | 
					    ): LazyGridItemInfo? {
 | 
				
			||||||
| 
						 | 
					@ -265,12 +320,12 @@ fun LazyGridItemScope.DraggableItem(
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        index == dragDropState.previousIndexOfDraggedItem -> {
 | 
					        index == dragDropState.draggingItemLingeringIndex -> {
 | 
				
			||||||
            Modifier
 | 
					            Modifier
 | 
				
			||||||
                .zIndex(1f)
 | 
					                .zIndex(1f)
 | 
				
			||||||
                .graphicsLayer {
 | 
					                .graphicsLayer {
 | 
				
			||||||
                    translationX = dragDropState.previousItemOffset.value.x
 | 
					                    translationX = dragDropState.draggingItemSetIntoPositionAnimation.value.x
 | 
				
			||||||
                    translationY = dragDropState.previousItemOffset.value.y
 | 
					                    translationY = dragDropState.draggingItemSetIntoPositionAnimation.value.y
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,9 +1,26 @@
 | 
				
			||||||
package com.pixelized.rplexicon.utilitary.extentions.string
 | 
					package com.pixelized.rplexicon.utilitary.extentions.string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.net.Uri
 | 
				
			||||||
import androidx.annotation.DrawableRes
 | 
					import androidx.annotation.DrawableRes
 | 
				
			||||||
import com.pixelized.rplexicon.R
 | 
					import com.pixelized.rplexicon.R
 | 
				
			||||||
 | 
					
 | 
				
			||||||
object BaldurGageImageCache {
 | 
					object BaldurGageImageCache {
 | 
				
			||||||
 | 
					    // https://bg3.wiki/wiki/Category:Controller_UI_Icons
 | 
				
			||||||
 | 
					    private val uri = mapOf(
 | 
				
			||||||
 | 
					        // Default.
 | 
				
			||||||
 | 
					        null to R.drawable.icbg_generic_darkness_icon,
 | 
				
			||||||
 | 
					        // Category:Generic Controller Icons
 | 
				
			||||||
 | 
					        "https://bg3.wiki/w/images/5/56/Generic_Darkness_Icon.webp" to R.drawable.icbg_generic_darkness_icon,
 | 
				
			||||||
 | 
					        // Category:Container Controller Icons
 | 
				
			||||||
 | 
					        "https://bg3.wiki/w/images/3/39/Backpack_A_Unfaded.webp" to R.drawable.icbg_backpack_a_unfaded,
 | 
				
			||||||
 | 
					        "https://bg3.wiki/w/images/6/6d/Backpack_B_Unfaded.webp" to R.drawable.icbg_backpack_b_unfaded,
 | 
				
			||||||
 | 
					        // Category:Potion Controller Icons
 | 
				
			||||||
 | 
					        "https://bg3.wiki/w/images/c/ce/POT_Potion_of_Healing_Unfaded.png" to R.drawable.icbg_pot_potion_of_healing_unfaded,
 | 
				
			||||||
 | 
					        "https://bg3.wiki/w/images/7/73/POT_Potion_of_Superior_Healing_Unfaded.png" to R.drawable.icbg_potion_of_superior_healing_unfaded,
 | 
				
			||||||
 | 
					        // Category:Cloak Controller Icons
 | 
				
			||||||
 | 
					        "https://bg3.wiki/w/images/8/85/Cloak_Of_Protection_Unfaded.png" to R.drawable.icbg_cloak_of_protection_unfaded,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private val alterations = hashMapOf(
 | 
					    private val alterations = hashMapOf(
 | 
				
			||||||
        "Arme enflammée" to R.drawable.icbg_flaming_blade,
 | 
					        "Arme enflammée" to R.drawable.icbg_flaming_blade,
 | 
				
			||||||
        "Critique" to R.drawable.icbg_frenzied_strike,
 | 
					        "Critique" to R.drawable.icbg_frenzied_strike,
 | 
				
			||||||
| 
						 | 
					@ -268,4 +285,6 @@ object BaldurGageImageCache {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @DrawableRes
 | 
					    @DrawableRes
 | 
				
			||||||
    fun spellIcon(name: String): Int? = spells[name]
 | 
					    fun spellIcon(name: String): Int? = spells[name]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fun cache(url: Uri?): Any = uri[url.toString()] ?: uri[null]!!
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/drawable/icbg_backpack_a_unfaded.webp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/src/main/res/drawable/icbg_backpack_a_unfaded.webp
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 17 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/drawable/icbg_backpack_b_unfaded.webp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/src/main/res/drawable/icbg_backpack_b_unfaded.webp
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 16 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/drawable/icbg_generic_darkness_icon.webp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/src/main/res/drawable/icbg_generic_darkness_icon.webp
									
										
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 14 KiB  | 
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue