Add a lot of icon + change the cache system.

This commit is contained in:
Andres Gomez, Thomas (ITDV RL) 2024-07-17 20:53:40 +02:00
parent 6ccf7dc56b
commit 0dc237f819
58 changed files with 585 additions and 207 deletions

View file

@ -1,12 +1,17 @@
package com.pixelized.rplexicon.data.model.item
import android.net.Uri
import com.pixelized.rplexicon.data.model.roll.Throw
data class Item(
val id: String,
val prefix: String?,
val name: String,
val type: String?,
val description: String?,
val isContainer: Boolean,
val effect: Throw?,
val icon: Any?,
)
val icon: Uri?,
) {
val fullName: String = prefix?.let { "$it${name}" } ?: name
}

View file

@ -30,9 +30,11 @@ class ItemLexiconParser @Inject constructor(
id = id,
prefix = row.parse(column = PREFIX),
name = name,
type = row.parse(column = TYPE),
description = row.parse(column = CONTEXT),
isContainer = container,
effect = throwParser.parse(value = row.parse(column = EFFECT)),
icon = BaldurGageImageCache.cache(uri = row.parseUri(column = ICON)),
icon = row.parseUri(column = ICON),
)
inventories[item.id] = item
}
@ -47,10 +49,12 @@ class ItemLexiconParser @Inject constructor(
private val ID = column("Id")
private val PREFIX = column("Préfix")
private val NAME = column("Nom")
private val TYPE = column("Type")
private val CONTEXT = column("Contexte")
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)
private val COLUMNS = listOf(ID, PREFIX, NAME, TYPE, CONTEXT, CONTAINER, EFFECT, ICON)
}
}

View file

@ -3,9 +3,10 @@ package com.pixelized.rplexicon.ui.screens.character.factory
import com.pixelized.rplexicon.data.model.item.Item
import com.pixelized.rplexicon.data.network.ItemDto
import com.pixelized.rplexicon.ui.screens.character.pages.inventory.InventoryItemUio
import com.pixelized.rplexicon.utilitary.extentions.string.BaldurGageImageCache
import javax.inject.Inject
class ItemUioFactory @Inject constructor() {
class InventoryItemUioFactory @Inject constructor() {
fun toUio(
items: Map<String, Item>,
@ -19,7 +20,7 @@ class ItemUioFactory @Inject constructor() {
name = item.name,
amount = amount,
container = item.isContainer,
icon = item.icon,
icon = BaldurGageImageCache.cache(item.icon),
items = null // fire.children?.let { toUio(items = items, fires = it) },
)
}

View file

@ -0,0 +1,34 @@
package com.pixelized.rplexicon.ui.screens.character.factory
import android.content.Context
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.data.model.item.Item
import com.pixelized.rplexicon.ui.screens.character.pages.inventory.item.ItemCategoryUio
import com.pixelized.rplexicon.ui.screens.character.pages.inventory.item.ItemUio
import com.pixelized.rplexicon.utilitary.extentions.string.BaldurGageImageCache
import javax.inject.Inject
class ItemElementUioFactory @Inject constructor() {
fun toUio(
context: Context,
items: List<Item>,
): List<ItemCategoryUio> {
return items
.groupBy { it.type }
.map { group: Map.Entry<String?, List<Item>> ->
ItemCategoryUio(
title = group.key
?: context.getString(R.string.character_sheet__item_list__uncategorized),
items = group.value.map { item ->
ItemUio(
id = item.id,
name = item.fullName,
description = item.description,
icon = BaldurGageImageCache.cache(uri = item.icon),
)
}
)
}
}
}

View file

@ -6,7 +6,6 @@ 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.fillMaxSize
import androidx.compose.foundation.layout.padding
@ -15,8 +14,12 @@ 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.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.surfaceColorAtElevation
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
@ -24,18 +27,21 @@ import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
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.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import com.pixelized.rplexicon.LocalSnack
import com.pixelized.rplexicon.ui.screens.character.composable.actions.EquipmentItem
import com.pixelized.rplexicon.ui.screens.character.composable.actions.EquipmentItemUio
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.composable.FloatingActionButton
import com.pixelized.rplexicon.ui.screens.character.composable.dialogs.SkillDetailDialog
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.pages.inventory.item.ItemListDialog
import com.pixelized.rplexicon.ui.screens.character.pages.inventory.item.ItemListViewModel
import com.pixelized.rplexicon.ui.theme.LexiconTheme
import com.pixelized.rplexicon.utilitary.DraggableItem
import com.pixelized.rplexicon.utilitary.dragContainer
@ -47,7 +53,8 @@ import kotlinx.coroutines.launch
@Composable
fun InventoryPage(
viewModel: InventoryViewModel,
viewModel: InventoryViewModel = hiltViewModel(),
itemViewModel: ItemListViewModel = hiltViewModel()
) {
val snack = LocalSnack.current
val snackJob = remember { mutableStateOf<Job?>(null) }
@ -55,18 +62,19 @@ fun InventoryPage(
InventoryPageContent(
modifier = Modifier.fillMaxSize(),
equipments = viewModel.equipments,
inventory = viewModel.inventory,
onEquipment = {
snackJob.value?.cancel()
viewModel.showSkillDetailDialog(item = it)
},
onAddItem = itemViewModel::show,
onInventoryItemMove = viewModel::onMove,
onInventoryIsMoveEnable = viewModel::isMoveEnable,
onInventoryItemDrop = viewModel::onDrop,
onInventoryItemDropOver = viewModel::onDropOver,
)
ItemListDialog(
viewModel = itemViewModel,
onDismissRequest = itemViewModel::hide,
)
SkillDetailDialog(
dialog = viewModel.dialog,
onDismissRequest = viewModel::hideSkillDetailDialog,
@ -85,9 +93,8 @@ fun InventoryPage(
@Composable
private fun InventoryPageContent(
modifier: Modifier = Modifier,
equipments: State<EquipmentItemUio?>,
inventory: State<List<InventoryItemUio>>,
onEquipment: (String) -> Unit,
onAddItem: () -> Unit,
onInventoryItemMove: (Int, Int) -> Unit,
onInventoryIsMoveEnable: (Int, Int) -> Boolean,
onInventoryItemDrop: (Int) -> Unit,
@ -103,16 +110,9 @@ private fun InventoryPageContent(
onDragRelease = onInventoryItemDrop,
onDropReleaseOver = onInventoryItemDropOver,
)
Column(
Box(
modifier = modifier,
) {
equipments.value?.let {
EquipmentItem(
modifier = Modifier.padding(bottom = 16.dp),
equipments = it,
onClick = onEquipment,
)
}
LazyVerticalGrid(
columns = GridCells.Fixed(5),
modifier = Modifier
@ -180,6 +180,23 @@ private fun InventoryPageContent(
}
}
}
FloatingActionButton(
modifier = Modifier
.align(alignment = Alignment.BottomEnd)
.padding(all = 16.dp),
onClick = onAddItem,
expended = true,
icon = {
Icon(
imageVector = Icons.Default.Add,
contentDescription = null,
)
},
text = {
Text(text = stringResource(id = R.string.character_sheet__inventory__add_item_action))
}
)
}
}
@ -191,9 +208,8 @@ fun InventoryPagePreview() {
Surface {
InventoryPageContent(
modifier = Modifier.fillMaxSize(),
equipments = rememberEquipmentState(),
inventory = rememberInventoryListState(),
onEquipment = { },
onAddItem = { },
onInventoryItemMove = { _, _ -> },
onInventoryIsMoveEnable = { _, _ -> true },
onInventoryItemDrop = { _ -> },

View file

@ -13,16 +13,13 @@ 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.EquipmentRepository
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.screens.character.composable.actions.EquipmentItemUio
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.InventoryItemUioFactory
import com.pixelized.rplexicon.utilitary.extentions.context
import com.pixelized.rplexicon.utilitary.extentions.string.BaldurGageImageCache.equipmentsIcon
import com.pixelized.rplexicon.utilitary.extentions.uri
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableSharedFlow
@ -33,24 +30,19 @@ import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import javax.inject.Inject
@HiltViewModel
class InventoryViewModel @Inject constructor(
private val equipmentRepository: EquipmentRepository,
private val descriptionRepository: DescriptionRepository,
private val fireRepository: RealtimeDatabaseRepository,
private val itemFactory: ItemUioFactory,
private val itemFactory: InventoryItemUioFactory,
itemsRepository: ItemsRepository,
savedStateHandle: SavedStateHandle,
application: Application
) : AndroidViewModel(application) {
private val character = savedStateHandle.characterSheetArgument.name
private val _equipments = mutableStateOf<EquipmentItemUio?>(null)
val equipments: State<EquipmentItemUio?> get() = _equipments
// Target the local indexes form specific ids (change locally to avoid flooding of firebase)
private val itemLocalIndex =
MutableStateFlow<Map<String, Int>>(hashMapOf())
@ -120,32 +112,6 @@ class InventoryViewModel @Inject constructor(
}.toMap()
}
}
launch(Dispatchers.IO) {
equipmentRepository.data.collect {
val equipments = it[character]?.let { data ->
EquipmentItemUio(
silhouette = data.silhouette ?: R.drawable.ic_woman_archer_256.uri,
head = data.head,
face = data.face,
shoulder = data.shoulder,
neck = data.neck,
body = data.body,
chest = data.chest,
arm = data.arm,
waist = data.waist,
hands = data.hand,
ringRight = data.ring1,
foot = data.foot,
ringLeft = data.ring2,
mainHand = data.mainHand,
offHand = data.offHand,
)
}
withContext(Dispatchers.Main) {
_equipments.value = equipments
}
}
}
launch(Dispatchers.IO) {
fireRepository.setInventory(

View file

@ -10,6 +10,7 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
@ -18,13 +19,15 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import androidx.compose.ui.unit.dp
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.composable.images.AsyncImage
import com.pixelized.rplexicon.ui.theme.LexiconTheme
import com.pixelized.rplexicon.utilitary.extentions.string.BaldurGageImageCache.cache
@Stable
data class ItemUio(
@ -49,8 +52,10 @@ fun Item(
.padding(paddingValues)
.then(other = modifier),
horizontalArrangement = Arrangement.spacedBy(space = 8.dp),
verticalAlignment = Alignment.CenterVertically,
) {
AsyncImage(
modifier = Modifier.size(size = 42.dp),
model = item.icon,
)
Column(
@ -62,11 +67,17 @@ fun Item(
) {
Text(
style = MaterialTheme.typography.bodyMedium,
fontWeight = FontWeight.Bold,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
text = item.name,
)
item.description?.let {
Text(
style = MaterialTheme.typography.labelSmall,
fontWeight = FontWeight.Normal,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
text = it,
)
}
@ -93,19 +104,13 @@ private class ItemProvider : PreviewParameterProvider<ItemUio> {
id = "7d27561b-f2f4-4899-a2fc-df3501b1b66b",
name = "Bourse",
description = "Un petite bourse.",
icon = cache(uri = "https://bg3.wiki/w/images/c/c6/Pouch_A_Unfaded.webp"),
),
ItemUio(
id = "43424bbc-0117-44f8-b512-6d469a05e6c2",
name = "Sac à dos",
description = "Sac à dos pour aventurier.",
icon = cache(uri = "https://bg3.wiki/w/images/6/6d/Backpack_B_Unfaded.webp"),
icon = R.drawable.icbg_pouch_a_unfaded,
),
ItemUio(
id = "2ecf41c2-06b1-4f2d-a6d9-b6adcaf05aba",
name = "Sac à dos",
description = "Sac à dos pour aventurier.",
icon = cache(uri = "https://bg3.wiki/w/images/3/39/Backpack_A_Unfaded.webp"),
description = null,
icon = R.drawable.icbg_backpack_b_unfaded,
),
)
}

View file

@ -0,0 +1,307 @@
package com.pixelized.rplexicon.ui.screens.character.pages.inventory.item
import android.content.res.Configuration
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Refresh
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.OutlinedTextFieldDefaults
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.surfaceColorAtElevation
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusManager
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import androidx.hilt.navigation.compose.hiltViewModel
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.composable.CategoryHeader
import com.pixelized.rplexicon.ui.theme.LexiconTheme
@Stable
data class ItemCategoryUio(
val title: String,
val items: List<ItemUio>,
)
@Composable
fun ItemListDialog(
viewModel: ItemListViewModel = hiltViewModel(),
onDismissRequest: () -> Unit,
) {
if (viewModel.display.value) {
Dialog(
properties = remember {
DialogProperties(
decorFitsSystemWindows = false,
usePlatformDefaultWidth = false,
)
},
onDismissRequest = onDismissRequest,
) {
Surface(
modifier = Modifier
.imePadding()
.padding(all = 24.dp)
.fillMaxSize(),
shape = remember { RoundedCornerShape(size = 16.dp) },
) {
ItemListContent(
search = viewModel.search,
items = viewModel.items,
onClear = { viewModel.filter("") },
onSearchChange = viewModel::filter,
onRefresh = viewModel::updateItemList,
onItem = {},
)
}
}
}
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun ItemListContent(
modifier: Modifier = Modifier,
search: State<String>,
items: State<List<ItemCategoryUio>>,
onClear: () -> Unit,
onRefresh: () -> Unit,
onSearchChange: (String) -> Unit,
onItem: () -> Unit,
) {
Scaffold(
modifier = modifier,
topBar = {
SearchTextField(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
.padding(top = 16.dp),
value = search,
onClear = onClear,
onRefresh = onRefresh,
onValueChange = onSearchChange,
)
},
content = { paddings ->
LazyColumn(
modifier = Modifier.padding(paddingValues = paddings),
contentPadding = PaddingValues(vertical = 16.dp),
verticalArrangement = Arrangement.spacedBy(space = 8.dp),
) {
items.value.forEachIndexed { index, category ->
stickyHeader(
key = "category:${category.title}",
) {
Surface(
modifier = Modifier.fillMaxWidth(),
) {
CategoryHeader(
modifier = Modifier
.padding(top = if (index == 0) 0.dp else 16.dp)
.padding(horizontal = 16.dp),
text = category.title,
)
}
}
items(
items = category.items,
key = { it.id },
) {
Item(
item = it,
onClick = onItem
)
}
}
}
},
)
}
@Composable
fun SearchTextField(
modifier: Modifier = Modifier,
focusManager: FocusManager = LocalFocusManager.current,
focusRequester: FocusRequester = remember { FocusRequester() },
value: State<String>,
onClear: () -> Unit,
onRefresh: () -> Unit,
onValueChange: (String) -> Unit,
) {
val surfaceColor = MaterialTheme.colorScheme.surfaceColorAtElevation(2.dp)
OutlinedTextField(
modifier = Modifier
.focusRequester(focusRequester = focusRequester)
.then(other = modifier),
colors = OutlinedTextFieldDefaults.colors(
focusedContainerColor = surfaceColor,
unfocusedContainerColor = surfaceColor,
focusedBorderColor = Color.Transparent,
unfocusedBorderColor = Color.Transparent,
),
shape = CircleShape,
trailingIcon = {
Row {
AnimatedVisibility(
visible = value.value.isNotEmpty(),
enter = fadeIn(),
exit = fadeOut(),
) {
IconButton(
modifier = Modifier.size(size = 48.dp),
onClick = onClear,
) {
Icon(
modifier = Modifier
.background(
color = MaterialTheme.colorScheme.surface,
shape = CircleShape
)
.padding(all = 12.dp)
.size(size = 18.dp),
painter = painterResource(id = R.drawable.ic_clear_24),
contentDescription = null,
)
}
}
IconButton(
modifier = Modifier
.size(size = 48.dp)
.padding(end = 4.dp),
onClick = onRefresh,
) {
Icon(
modifier = Modifier
.background(
color = MaterialTheme.colorScheme.surface,
shape = CircleShape
)
.padding(all = 12.dp)
.size(size = 18.dp),
imageVector = Icons.Default.Refresh,
contentDescription = null,
)
}
}
},
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Text,
imeAction = ImeAction.Done,
),
keyboardActions = KeyboardActions(
onDone = { focusManager.clearFocus(true) },
),
maxLines = 1,
value = value.value,
onValueChange = onValueChange,
)
}
@Composable
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun ItemLisPreview() {
LexiconTheme {
Surface {
ItemListContent(
modifier = Modifier.fillMaxSize(),
search = remember {
mutableStateOf("Pouet")
},
items = remember {
mutableStateOf(
listOf(
ItemCategoryUio(
title = "Container",
items = listOf(
ItemUio(
id = "70d0bcbe-47af-48ba-a9e5-9f9b97bc25a1",
name = "Bourse",
description = "Une petite bourse en cuir.",
icon = R.drawable.icbg_leather_pouch_a_unfaded,
),
ItemUio(
id = "7d27561b-f2f4-4899-a2fc-df3501b1b66b",
name = "Bourse",
description = "Une petite bourse.",
icon = R.drawable.icbg_pouch_a_unfaded,
),
ItemUio(
id = "2ecf41c2-06b1-4f2d-a6d9-b6adcaf05aba",
name = "Sac à dos",
description = "Sac à dos pour aventurier.",
icon = R.drawable.icbg_backpack_a_unfaded,
),
ItemUio(
id = "43424bbc-0117-44f8-b512-6d469a05e6c2",
name = "Sac à dos",
description = "Sac à dos pour aventurier.",
icon = R.drawable.icbg_backpack_b_unfaded,
),
),
),
ItemCategoryUio(
title = "Parchemin",
items = listOf(
ItemUio(
id = "471266fb-15a8-4346-9da0-d1c44e4c1569",
name = "Parchemin d'Arme spirituelle",
description = null,
icon = R.drawable.icbg_book_signedtradebisa_unfaded,
),
ItemUio(
id = "eda09856-5a91-4411-ac75-248a16ce1060",
name = "Parchemin de Bénédiction",
description = null,
icon = R.drawable.icbg_scroll_of_bless_unfaded,
),
)
),
)
)
},
onClear = { },
onRefresh = { },
onSearchChange = { },
onItem = { },
)
}
}
}

View file

@ -1,129 +0,0 @@
package com.pixelized.rplexicon.ui.screens.character.pages.inventory.item
import android.content.res.Configuration
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.pixelized.rplexicon.R
import com.pixelized.rplexicon.ui.composable.form.TextFieldAppBar
import com.pixelized.rplexicon.ui.composable.form.TextFieldUio
import com.pixelized.rplexicon.ui.theme.LexiconTheme
import com.pixelized.rplexicon.utilitary.extentions.modifier.lexiconShadow
import com.pixelized.rplexicon.utilitary.extentions.string.BaldurGageImageCache.cache
@Stable
data class ItemListUio(
val search: TextFieldUio,
val items: List<ItemUio>,
)
@Composable
fun ItemListPage() {
}
@Composable
private fun ItemListContent(
modifier: Modifier = Modifier,
page: ItemListUio,
onBack: () -> Unit,
onClear: () -> Unit,
onSearchChange: (String) -> Unit,
) {
Scaffold(
modifier = modifier,
topBar = {
Surface(
modifier = Modifier.lexiconShadow(),
) {
TextFieldAppBar(
modifier = Modifier
.fillMaxWidth()
.height(height = 64.dp),
field = page.search,
onBack = onBack,
onClear = onClear,
onValueChange = onSearchChange,
)
}
},
content = { paddings ->
LazyColumn(
modifier = Modifier.padding(paddingValues = paddings),
contentPadding = PaddingValues(vertical = 16.dp),
verticalArrangement = Arrangement.spacedBy(space = 8.dp),
) {
items(
items = page.items,
key = { it.id },
) {
Item(item = it)
}
}
},
)
}
@Composable
@Preview(uiMode = Configuration.UI_MODE_NIGHT_NO)
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun ItemLisPreview() {
LexiconTheme {
Surface {
ItemListContent(
modifier = Modifier.fillMaxSize(),
page = remember {
ItemListUio(
search = TextFieldUio(
label = R.string.search_field_title,
value = mutableStateOf("")
),
items = listOf(
ItemUio(
id = "70d0bcbe-47af-48ba-a9e5-9f9b97bc25a1",
name = "Bourse",
description = "Une petite bourse en cuir.",
icon = cache("https://bg3.wiki/w/images/5/5b/Leather_Pouch_A_Unfaded.webp"),
),
ItemUio(
id = "7d27561b-f2f4-4899-a2fc-df3501b1b66b",
name = "Bourse",
description = "Une petite bourse.",
icon = cache(uri = "https://bg3.wiki/w/images/c/c6/Pouch_A_Unfaded.webp"),
),
ItemUio(
id = "43424bbc-0117-44f8-b512-6d469a05e6c2",
name = "Sac à dos",
description = "Sac à dos pour aventurier.",
icon = cache(uri = "https://bg3.wiki/w/images/6/6d/Backpack_B_Unfaded.webp"),
),
ItemUio(
id = "2ecf41c2-06b1-4f2d-a6d9-b6adcaf05aba",
name = "Sac à dos",
description = "Sac à dos pour aventurier.",
icon = cache(uri = "https://bg3.wiki/w/images/3/39/Backpack_A_Unfaded.webp"),
),
)
)
},
onBack = {},
onClear = {},
onSearchChange = {},
)
}
}
}

View file

@ -0,0 +1,87 @@
package com.pixelized.rplexicon.ui.screens.character.pages.inventory.item
import android.icu.text.Collator
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.pixelized.rplexicon.data.model.item.Item
import com.pixelized.rplexicon.data.repository.character.ItemsRepository
import com.pixelized.rplexicon.ui.screens.character.factory.ItemElementUioFactory
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class ItemListViewModel @Inject constructor(
private val itemsRepository: ItemsRepository,
private val factory: ItemElementUioFactory,
) : ViewModel() {
private val filter = MutableStateFlow("")
private val _display = mutableStateOf(false)
val display: State<Boolean> = _display
private val _refreshing = mutableStateOf(false)
val refreshing: State<Boolean> = _refreshing
val search: State<String>
@Composable
@Stable
get() = filter.collectAsState(initial = "")
val items: State<List<ItemCategoryUio>>
@Composable
@Stable
get() {
val context = LocalContext.current
return itemsRepository.data.combine(filter) { items, filter -> items to filter }
.map { (items: Map<String, Item>, filter: String) ->
items.values
.filter {
val name = it.fullName.contains(other = filter, ignoreCase = true)
val type = it.type?.contains(other = filter, ignoreCase = true) == true
name || type
}
.sortedWith(compareBy(Collator.getInstance()) { it.fullName })
.sortedBy { it.type }
}
.map { items: List<Item> ->
factory.toUio(context = context, items = items)
}
.collectAsState(initial = emptyList())
}
fun updateItemList() {
viewModelScope.launch {
try {
_refreshing.value = true
itemsRepository.fetchItems()
} catch (exception: Exception) {
} finally {
_refreshing.value = false
}
}
}
fun filter(filter: String) {
this.filter.value = filter
}
fun show() {
_display.value = true
}
fun hide() {
_display.value = false
}
}

View file

@ -7,19 +7,94 @@ import com.pixelized.rplexicon.R
object BaldurGageImageCache {
// https://bg3.wiki/wiki/Category:Controller_UI_Icons
private val uri = mapOf(
// Drive
"https://drive.google.com/uc?export=view&id=1Dc0hBGXTNP8dnYqHJlSsK1WgWAsWtCV9" to R.drawable.icbg_lantern_of_revealing,
// 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/1/1d/Alchemy_Pouch_Unfaded.webp" to R.drawable.icbg_alchemy_pouch_unfaded,
"https://bg3.wiki/w/images/5/5b/Leather_Pouch_A_Unfaded.webp" to R.drawable.icbg_leather_pouch_a_unfaded,
"https://bg3.wiki/w/images/c/c6/Pouch_A_Unfaded.webp" to R.drawable.icbg_pouch_a_unfaded,
"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,
"https://bg3.wiki/w/images/c/c0/Keychain_Unfaded.png" to R.drawable.icbg_keychain_unfaded,
"https://bg3.wiki/w/images/4/4e/Leather_Pouch_Old_Unfaded.webp" to R.drawable.icbg_leather_pouch_old_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:Grenade Controller Icons
"https://bg3.wiki/w/images/2/2e/GRN_Holy_Water_Unfaded.png" to R.drawable.icbg_grn_holy_water_unfaded,
"https://bg3.wiki/w/images/7/7c/GRN_Haste_Spore_Grenade_Unfaded.png" to R.drawable.icbg_haste_spore_grenade_unfaded,
"https://bg3.wiki/w/images/0/00/GRN_Grease_Bottle_Unfaded.png" to R.drawable.icbg_grn_grease_bottle_unfaded,
// Category:Scroll Controller Icons
"https://bg3.wiki/w/images/d/db/BOOK_SignedTradeVisa_Unfaded.png" to R.drawable.icbg_book_signedtradebisa_unfaded,
"https://bg3.wiki/w/images/a/ac/Book_Parchment_L_Unfaded.png" to R.drawable.icbg_book_parchment_l_unfaded,
"https://bg3.wiki/w/images/8/8f/Scroll_of_Bless_Unfaded.png" to R.drawable.icbg_scroll_of_bless_unfaded,
// Category:Key Controller Icons
"https://bg3.wiki/w/images/8/8b/Key_Flower_B_Unfaded.png" to R.drawable.icbg_key_flower_b_unfaded,
// Category:Food Controller Icons
"https://bg3.wiki/w/images/6/6e/FOOD_Goodberry_Unfaded.png" to R.drawable.icbg_food_goodberry_unfaded,
"https://bg3.wiki/w/images/d/df/FOOD_Treacle_Tart_Unfaded.png" to R.drawable.icbg_food_treacle_tart_unfaded,
// Category:Drink Controller Icons
"https://bg3.wiki/w/images/c/c8/ALCH_Arkhen%27s_Hoard_Unfaded.png" to R.drawable.icbg_alch_arkhens_hoard_unfaded,
"https://bg3.wiki/w/images/d/dc/ALCH_Ashaba_Dusk_Unfaded.png" to R.drawable.icbg_alch_ashaba_dusk_unfaded,
"https://bg3.wiki/w/images/d/d6/ALCH_Farsea_Marshwine_Unfaded.png" to R.drawable.icbg_alch_farsea_marshwine_unfaded,
// Category:Ingredient Controller Icons
"https://bg3.wiki/w/images/7/72/Worg_Fang_Unfaded.png" to R.drawable.icbg_worg_fang_unfaded,
// Category:Miscellaneous Item Controller Icons
"https://bg3.wiki/w/images/4/47/Enriched_Infernal_Iron_Unfaded.png" to R.drawable.icbg_enriched_infernal_iron_unfaded,
"https://bg3.wiki/w/images/7/74/Candle_Unfaded.png" to R.drawable.icbg_candle_unfaded,
"https://bg3.wiki/w/images/1/17/Rope_Unfaded.png" to R.drawable.icbg_rope_unfaded,
"https://bg3.wiki/w/images/b/bc/Clutter_Wooden_Bowl_Unfaded.webp" to R.drawable.icbg_clutter_wooden_bowl_unfaded,
"https://bg3.wiki/w/images/a/a9/VAL_MISC_Incense_Pile_A_Unfaded.png" to R.drawable.icbg_val_misc_incense_pile_a_unfaded,
"https://bg3.wiki/w/images/7/78/VAL_MISC_Silver_Spoon_Unfaded.png" to R.drawable.icbg_val_misc_silver_spoon_unfaded,
"https://bg3.wiki/w/images/4/40/Incense_Bowl_B_Unfaded.png" to R.drawable.icbg_incense_bowl_b_unfaded,
"https://bg3.wiki/w/images/1/15/Silver_Ingot_Unfaded.png" to R.drawable.icbg_silver_ingot_unfaded,
"https://bg3.wiki/w/images/2/2a/Thieves_Tools_Unfaded.png" to R.drawable.icbg_thieves_tools_unfaded,
"https://bg3.wiki/w/images/4/46/MISC_Arrow_Unfaded.png" to R.drawable.icbg_misc_arrow_unfaded,
"https://bg3.wiki/w/images/a/a1/Arrow_of_Many_Targets_Unfaded.png" to R.drawable.icbg_arrow_of_many_targets_unfaded,
"https://bg3.wiki/w/images/4/49/Arrow_of_Piercing_Unfaded.png" to R.drawable.icbg_arrow_of_piercing_unfaded,
"https://bg3.wiki/w/images/2/20/Water_Wicker_B_Unfaded.png" to R.drawable.icbg_water_wicker_b_unfaded,
"https://bg3.wiki/w/images/0/0f/Clutter_Bedroll_Unfaded.webp" to R.drawable.icbg_clutter_bedroll_unfaded,
"https://bg3.wiki/w/images/3/37/Quill_Unfaded.png" to R.drawable.icbg_quill_unfaded,
"https://bg3.wiki/w/images/5/5f/Soap_Bar_Unfaded.png" to R.drawable.icbg_soap_bar_unfaded,
"https://bg3.wiki/w/images/d/df/Toy_Block_B_Unfaded.png" to R.drawable.icbg_toy_block_b_unfaded,
"https://bg3.wiki/w/images/f/f4/Cauldron_A_Unfaded.png" to R.drawable.icbg_cauldron_a_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,
// Category:Ring Controller Icons
"https://bg3.wiki/w/images/b/bf/Gemless_Ring_Unfaded.png" to R.drawable.icbg_gemless_ring_unfaded,
"https://bg3.wiki/w/images/1/19/Partner_Ring_A_Unfaded.png" to R.drawable.icbg_partner_ring_a_unfaded,
"https://bg3.wiki/w/images/9/9e/Bed_Linen_Unfaded.png" to R.drawable.icbg_bed_linen_unfaded,
// Category:Amulet Controller Icons
"https://bg3.wiki/w/images/e/e5/Amulet_of_Lost_Voices_Unfaded.png" to R.drawable.icbg_amulet_of_lost_voices_unfaded,
"https://bg3.wiki/w/images/a/aa/Amulet_Necklace_A_Silver_A_Unfaded.png" to R.drawable.icbg_amulet_necklace_a_silver_a_unfaded,
// Category:Armour Controller Icons
"https://bg3.wiki/w/images/d/d4/Leather_Armour_Rogue_Unfaded.png" to R.drawable.icbg_leather_armour_rogue_unfaded,
"https://bg3.wiki/w/images/6/68/Scale_Mail_Unfaded.png" to R.drawable.icbg_scale_mail_unfaded,
"https://bg3.wiki/w/images/2/2f/Chain_Shirt_3_Unfaded.png" to R.drawable.icbg_chain_shirt_3_unfaded,
// Category:Weapon Controller Icons
"https://bg3.wiki/w/images/b/b5/Dryad_Staff_Unfaded.png" to R.drawable.icbg_dryad_staff_unfaded,
"https://bg3.wiki/w/images/2/2d/Dagger_Unfaded.png" to R.drawable.icbg_dagger_unfaded,
"https://bg3.wiki/w/images/6/63/Dagger_PlusOne_Unfaded.png" to R.drawable.icbg_dagger_plusone_unfaded,
"https://bg3.wiki/w/images/0/02/Shortsword_Unfaded.png" to R.drawable.icbg_shortsword_unfaded,
"https://bg3.wiki/w/images/9/92/Shortsword_PlusOne_Unfaded.png" to R.drawable.icbg_shortsword_plusone_unfaded,
"https://bg3.wiki/w/images/3/39/Mace_Unfaded.png" to R.drawable.icbg_mace_unfaded,
"https://bg3.wiki/w/images/3/39/Battleaxe_Unfaded.png" to R.drawable.icbg_battleaxe_unfaded,
"https://bg3.wiki/w/images/5/5d/Battleaxe_PlusOne_Unfaded.png" to R.drawable.icbg_battleaxe_plus_one_unfaded,
"https://bg3.wiki/w/images/4/41/Rapier_Unfaded.png" to R.drawable.icbg_rapier_unfaded,
"https://bg3.wiki/w/images/7/72/Hand_Crossbow_Unfaded.png" to R.drawable.icbg_hand_crossbow_unfaded,
"https://bg3.wiki/w/images/7/74/Light_Crossbow_Unfaded.png" to R.drawable.icbg_light_crossbow_unfaded,
"https://bg3.wiki/w/images/2/23/Heavy_Crossbow_Unfaded.png" to R.drawable.icbg_heavy_crossbow_unfaded,
"https://bg3.wiki/w/images/b/b1/Longbow_Unfaded.png" to R.drawable.icbg_longbow_unfaded,
"https://bg3.wiki/w/images/d/d6/Javelin_Unfaded.png" to R.drawable.icbg_javelin_unfaded,
"https://bg3.wiki/w/images/1/14/Spear_PlusOne_Unfaded.png" to R.drawable.icbg_shortsword_plusone_unfaded,
// https://bg3.wiki/wiki/Category:Shield_Controller_Icons
"https://bg3.wiki/w/images/4/45/Studded_Shield_Unfaded.png" to R.drawable.icbg_studded_shield_unfaded,
"https://bg3.wiki/w/images/1/1d/Item_WPN_HUM_Shield_D_0_Unfaded.png" to R.drawable.icbg_item_wpm_hum_shield_unfaded,
// Category:Instrument Controller Icons
"https://bg3.wiki/w/images/0/08/Instrument_Lute_Unfaded.png" to R.drawable.icbg_instrument_lute_unfaded,
"https://bg3.wiki/w/images/0/01/Instrument_PanFlute_Unfaded.png" to R.drawable.icbg_instrument_panflute_unfaded,
)
private val alterations = hashMapOf(
@ -287,7 +362,8 @@ object BaldurGageImageCache {
@DrawableRes
fun spellIcon(name: String): Int? = spells[name]
fun cache(uri: Uri?): Any = cache(uri = uri.toString())
fun cache(uri: String?): Any = this.uri[uri.toString()] ?: R.drawable.icbg_generic_darkness_icon
fun cache(uri: Uri?): Any = when (uri) {
null -> R.drawable.icbg_generic_darkness_icon
else -> this.uri[uri.toString()] ?: uri
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View file

@ -180,6 +180,9 @@
<string name="character_sheet_action_spell_level_0">Sorts mineurs</string>
<string name="character_sheet_action_spell_level_X">Sorts de niveau %1$s</string>
<string name="character_sheet__item_list__uncategorized">Non classé</string>
<string name="character_sheet__inventory__add_item_action">Ajouter</string>
<string name="dice_roll_mastery_proficiency">Maîtrise \"%1$s\" </string>
<string name="dice_roll_mastery_expertise">Expertise \"%1$s\" </string>
<string name="dice_roll_mastery_saving_throw">Jet de sauvegarde</string>

View file

@ -187,6 +187,9 @@
<string name="character_sheet_action_spell_level_0">Cantrip</string>
<string name="character_sheet_action_spell_level_X">Spell level %1$s</string>
<string name="character_sheet__item_list__uncategorized">Uncategorized</string>
<string name="character_sheet__inventory__add_item_action">Add</string>
<string name="roll_overlay__close_action">Close</string>
<string name="roll_overlay__critical_success">CRITICAL SUCCESS</string>
<string name="roll_overlay__critical_failure">CRITICAL FAILURE</string>