Add haptic feedback (thx Jérémy). better handling of removal animation.

This commit is contained in:
Andres Gomez, Thomas (ITDV RL) 2024-08-21 11:32:11 +02:00
parent b27149edaf
commit 2c5337f3dc
6 changed files with 58 additions and 15 deletions

View file

@ -62,9 +62,10 @@ class InventoryFactory @Inject constructor() {
items: Map<String, Item>,
fires: List<FireItem>,
order: Map<String, Int>,
parent: Map<String, String?>,
): Map<String?, List<Any>> {
return fires
.groupBy { it.parentId }
.groupBy { parent[it.id] }
.mapValues { (_, fires) ->
fires
.asSequence()

View file

@ -31,6 +31,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
@ -70,6 +71,7 @@ fun InventoryPage(
containerWidth: Dp = 64.dp,
) {
val overlay = LocalRollOverlay.current
val feedback = LocalHapticFeedback.current
val scope = rememberCoroutineScope()
val selectedContainer = inventoryViewModel.selectedContainer
@ -78,10 +80,16 @@ fun InventoryPage(
val dragDropState = rememberInventoryDragDropState(
containerWidth = containerWidth,
isItemDraggable = inventoryViewModel::isItemDraggable,
isItemDraggable = {
inventoryViewModel.isItemDraggable(it, feedback)
},
isItemsMovable = inventoryViewModel::isItemsMovable,
onItemMove = inventoryViewModel::onItemMove,
onItemRelease = inventoryViewModel::onItemRelease,
onItemMove = { from, to ->
inventoryViewModel.onItemMove(from, to, feedback)
},
onItemRelease = { from, to ->
inventoryViewModel.onItemRelease(from, to, feedback)
},
onItemLongClick = {
// on container long press we want to display the detail dialog.
if (it.type == Type.CONTAINER) {

View file

@ -5,6 +5,8 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.hapticfeedback.HapticFeedback
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
@ -41,6 +43,7 @@ class InventoryViewModel @Inject constructor(
private val character = savedStateHandle.characterSheetArgument.name
private val orderOverrider = MutableStateFlow<Map<String, Int>>(emptyMap())
private val parentOverrider = MutableStateFlow<Map<String, String?>>(emptyMap())
private val _selectedContainerId = MutableStateFlow<String?>(null)
private val _containers: StateFlow<List<InventoryContainerUio>> = combine(
@ -77,6 +80,7 @@ class InventoryViewModel @Inject constructor(
itemRepository.data,
fireRepository.getInventory(character),
orderOverrider,
parentOverrider,
transform = inventoryFactory::fromModelsToItemUio,
).stateIn(
scope = viewModelScope,
@ -88,6 +92,24 @@ class InventoryViewModel @Inject constructor(
@Stable
get() = _items.collectAsState()
init {
viewModelScope.launch {
launch(Dispatchers.Default) {
// Listen to fire repository to update the orderOverrider as soon as firebase is updated.
fireRepository.getInventory(character = character).collect { fires ->
orderOverrider.value = fires.associate { fire -> fire.id to fire.order }
}
}
launch(Dispatchers.Default) {
// Listen to fire repository to update the orderOverrider as soon as firebase is updated.
fireRepository.getInventory(character = character).collect { fires ->
parentOverrider.value = fires.associate { fire -> fire.id to fire.parentId }
}
}
}
}
fun getItem(element: Element): Any? {
return when (element.type) {
Type.CONTAINER -> _containers.value.getOrNull(element.index)
@ -99,11 +121,17 @@ class InventoryViewModel @Inject constructor(
_selectedContainerId.value = id
}
fun isItemDraggable(element: Element): Boolean {
fun isItemDraggable(element: Element, feedback: HapticFeedback): Boolean {
Log.d("InventoryViewModel", "isItemDraggable(element:${element}")
return when (val item = getItem(element = element)) {
is InventoryItemUio -> true
is InventoryContainerUio -> item.id != null
is InventoryItemUio -> {
feedback.performHapticFeedback(HapticFeedbackType.LongPress)
true
}
is InventoryContainerUio -> {
feedback.performHapticFeedback(HapticFeedbackType.LongPress)
item.id != null
}
else -> false
}
}
@ -135,7 +163,7 @@ class InventoryViewModel @Inject constructor(
}
}
fun onItemMove(from: Element, to: Element) {
fun onItemMove(from: Element, to: Element, feedback: HapticFeedback) {
if (from != to) {
Log.d("InventoryViewModel", "onItemMove(from:${from}, to:${to})")
val fromItem: Any? = getItem(element = from)
@ -146,6 +174,8 @@ class InventoryViewModel @Inject constructor(
val refFromItem = itemRepository.find(id = fromItem.id)
val refToItem = itemRepository.find(id = toItem.id)
if (refFromItem?.type != null && refFromItem.type == refToItem?.type) {
// perform feedback
feedback.performHapticFeedback(HapticFeedbackType.TextHandleMove)
// reorder locally
orderOverrider.value = orderOverrider.value.toMutableMap().also {
it[fromItem.id] = toItem.order
@ -168,6 +198,8 @@ class InventoryViewModel @Inject constructor(
val refFromItem = itemRepository.find(id = fromItem.id)
val refToItem = itemRepository.find(id = toItem.id)
if (refFromItem?.type != null && refFromItem.type == refToItem?.type) {
// perform feedback
feedback.performHapticFeedback(HapticFeedbackType.TextHandleMove)
// reorder locally
orderOverrider.value = orderOverrider.value.toMutableMap().also {
it[fromItem.id] = toItem.order
@ -189,9 +221,7 @@ class InventoryViewModel @Inject constructor(
}
}
fun onItemRelease(from: Element, to: Element) {
// clean the order override map, this is only needed when moving items around.
orderOverrider.value = emptyMap()
fun onItemRelease(from: Element, to: Element, feedback: HapticFeedback) {
// only do something when an item is release on top of a container.
if (from.type == Type.ITEM && to.type == Type.CONTAINER) {
// fetch the itemId & containerId.
@ -215,7 +245,12 @@ class InventoryViewModel @Inject constructor(
val typedOrderContainerFires = fires.filter {
typedItems[it.id] != null && it.parentId == containerId
}
parentOverrider.value = parentOverrider.value.toMutableMap().also { override ->
override[fire.id] = containerId
}
// perform feedback
feedback.performHapticFeedback(HapticFeedbackType.TextHandleMove)
// update the data layer.
viewModelScope.launch(Dispatchers.IO) {
// remove the item from the list
fireRepository.removeItem(
@ -247,9 +282,6 @@ class InventoryViewModel @Inject constructor(
defaultParentId: String? = _selectedContainerId.value,
quantity: Int,
) {
// clean the order override map, this is only needed when moving items around.
orderOverrider.value = emptyMap()
val item = itemRepository.find(id = itemId)
// check the new quantity to decide if we update or remove the item

View file

@ -106,12 +106,14 @@ object ImageCache {
"https://drive.google.com/uc?export=view&id=1Dc0hBGXTNP8dnYqHJlSsK1WgWAsWtCV9" to R.drawable.ic_drive_lantern_of_revealing,
"https://drive.google.com/uc?export=view&id=1FX7HpnxLUXFPqrKGvCYyJNkUSwFacKh9" to R.drawable.ic_silver_coin_pile_unfaded,
"https://drive.google.com/uc?export=view&id=1jJkltHU5HzCuU5ixVly_J5Dr0stEawEV" to R.drawable.ic_drive_deck_of_card,
"https://drive.google.com/uc?export=view&id=1JhUikzOesAyGCtCL9bFW_HRdqM8hj1sf" to R.drawable.ic_drive_bear_trap,
"https://drive.google.com/uc?export=view&id=1Jo0Nk_Mj4j-VKvBe0W6_73uE_tKUgPUF" to R.drawable.ic_drive_kitsune,
"https://drive.google.com/uc?export=view&id=1KPlgQK1C3lfZn3ZHTHd-dqiSINjQpnH9" to R.drawable.ic_drive_ring_of_kazan,
"https://drive.google.com/uc?export=view&id=1KVXSKHI8JgU1Wnhp9rmEZ1f4rsUfoV2a" to R.drawable.ic_gold_coin_pile_unfaded,
"https://drive.google.com/uc?export=view&id=1lLu7o1h1pkXuNRbHJ2dnxQLKyrNRxV3P" to R.drawable.ic_drive_bear_claw_neckless,
"https://drive.google.com/uc?export=view&id=1nazdbGFLyiGNMYvPhVXiZoeyBxDtYMtI" to R.drawable.ic_electrum_coin_pile_unfaded,
"https://drive.google.com/uc?export=view&id=1p5OSYPeeKa-niiTa9GFChdpnu0FqkswG" to R.drawable.ic_drive_harlequin,
"https://drive.google.com/uc?export=view&id=1TDTCkVZb520gdndC4FmAneWDGgf9wmDM" to R.drawable.ic_drive_chalk_stick,
"https://drive.google.com/uc?export=view&id=1PlhfW61aSncyX3Ml1oPDPokHTR7U8O2y" to R.drawable.ic_drive_quiver,
"https://drive.google.com/uc?export=view&id=1WSpydN6AVjQrS6J_F8EFT00McQqCCxCw" to R.drawable.ic_drive_stake,
"https://drive.google.com/uc?export=view&id=1ywDHw0C6exwxNCu-qNgQ0X2Sx9LfqSNH" to R.drawable.ic_drive_bird_skull,

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB