Skip to content
Snippets Groups Projects
Commit 6bc6cbe1 authored by Jonas Broeckmann's avatar Jonas Broeckmann
Browse files

Added item search

parent 921f1be8
No related branches found
No related tags found
No related merge requests found
......@@ -16,7 +16,6 @@ import net.novagamestudios.kaffeekasse.model.kaffeekasse.MutableCart
import net.novagamestudios.kaffeekasse.model.kaffeekasse.isNotEmpty
import net.novagamestudios.kaffeekasse.model.session.Session
import net.novagamestudios.kaffeekasse.repositories.RepositoryProvider
import net.novagamestudios.kaffeekasse.ui.AppModuleSelection
import net.novagamestudios.kaffeekasse.ui.kaffeekasse.ManualBillScreenModel.Companion.backNavigationHandler
import net.novagamestudios.kaffeekasse.ui.navigation.AppSubpageTitle
import net.novagamestudios.kaffeekasse.ui.navigation.KaffeekasseNavigation
......@@ -34,7 +33,6 @@ class KaffeekasseModuleScreenModel private constructor(
else -> null
}
companion object : ScreenModelFactory<KaffeekasseNavigation.Tab, KaffeekasseModuleScreenModel> {
context (RepositoryProvider)
override fun create(screen: KaffeekasseNavigation.Tab) = KaffeekasseModuleScreenModel(
......@@ -52,12 +50,8 @@ fun KaffeekasseTopBarTitle(
model: KaffeekasseModuleScreenModel,
navigator: Navigator
) {
when (navigator.lastItem) {
is KaffeekasseNavigation.ManualBillScreen -> {
val name = model.accountName
if (name != null) AppSubpageTitle("$name")
else AppModuleSelection()
}
when (val screen = navigator.lastItem) {
is KaffeekasseNavigation.ManualBillScreen -> ManualBillTopBarTitle(screen.model, navigator)
is KaffeekasseNavigation.AccountScreen -> AppSubpageTitle("Konto")
is KaffeekasseNavigation.TransactionsScreen -> AppSubpageTitle("Übersicht")
}
......
......@@ -14,6 +14,7 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material.icons.filled.Person
import androidx.compose.material.icons.filled.Receipt
import androidx.compose.material.icons.filled.Search
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
......@@ -42,19 +43,23 @@ import net.novagamestudios.common_utils.Logger
import net.novagamestudios.common_utils.compose.components.CircularProgressIndicator
import net.novagamestudios.common_utils.compose.tabIndicatorOffset
import net.novagamestudios.kaffeekasse.KaffeekasseModule.Companion.kaffeekasseCartProvider
import net.novagamestudios.kaffeekasse.model.kaffeekasse.Item
import net.novagamestudios.kaffeekasse.model.kaffeekasse.MutableCart
import net.novagamestudios.kaffeekasse.model.kaffeekasse.isEmpty
import net.novagamestudios.kaffeekasse.model.session.Session
import net.novagamestudios.kaffeekasse.model.session.deviceOrNull
import net.novagamestudios.kaffeekasse.repositories.RepositoryProvider
import net.novagamestudios.kaffeekasse.repositories.i11.KaffeekasseRepository
import net.novagamestudios.kaffeekasse.ui.AppModuleSelection
import net.novagamestudios.kaffeekasse.ui.kaffeekasse.components.CategorizedItems
import net.novagamestudios.kaffeekasse.ui.kaffeekasse.components.CategorizedItemsState
import net.novagamestudios.kaffeekasse.ui.kaffeekasse.components.Checkout
import net.novagamestudios.kaffeekasse.ui.kaffeekasse.components.CheckoutState
import net.novagamestudios.kaffeekasse.ui.kaffeekasse.components.CustomItems
import net.novagamestudios.kaffeekasse.ui.kaffeekasse.components.CustomItemsState
import net.novagamestudios.kaffeekasse.ui.kaffeekasse.components.ItemSearchField
import net.novagamestudios.kaffeekasse.ui.login.LogoutTopBarAction
import net.novagamestudios.kaffeekasse.ui.navigation.AppSubpageTitle
import net.novagamestudios.kaffeekasse.ui.navigation.KaffeekasseNavigation
import net.novagamestudios.kaffeekasse.ui.util.FailureRetryScreen
import net.novagamestudios.kaffeekasse.ui.util.RichDataContent
......@@ -70,13 +75,43 @@ class ManualBillScreenModel private constructor(
private val kaffeekasse: KaffeekasseRepository,
val cart: MutableCart
) : ScreenModel, Logger {
val accountName = when (session) {
is Session.WithDevice -> session.realUser.displayName ?: "Unknown User"
else -> null
}
private var _showSearch by mutableStateOf(false)
var showSearch
get() = _showSearch
set(value) {
_showSearch = value
if (!value) searchQuery = ""
}
var searchQuery by mutableStateOf("")
val stock = kaffeekasse.stock.collectAsRichStateHere()
val itemGroups get() = stock.dataOrNull?.itemGroups ?: emptyList()
fun Sequence<Item>.filtered(): Sequence<Item> {
val query = searchQuery.takeIf { showSearch }?.takeUnless { it.isBlank() }
return if (query != null) filter { item ->
item.cleanFullName.contains(query, ignoreCase = true)
} else this
}
val searchItems by derivedStateOf {
if (showSearch) CategorizedItemsState(
itemGroups
.asSequence()
.flatMap { it.items }
.filtered()
.toList()
) else null
}
val categorizedItemsByGroup by derivedStateOf {
itemGroups.map { CategorizedItemsState(it.items) }
itemGroups.map { group -> CategorizedItemsState(group.items.asSequence().filtered().toList()) }
}
val customItemsState by lazy {
......@@ -127,10 +162,25 @@ class ManualBillScreenModel private constructor(
@Composable
fun ManualBillScreenModel.backNavigationHandler(navigator: Navigator): BackNavigationHandler {
val categorizedItems = categorizedItemsByGroup.getOrNull(currentGroupIndex)
val categorizedItems = if (currentPagerIndex == 0) {
searchItems
} else {
categorizedItemsByGroup.getOrNull(currentGroupIndex)
}
return remember(categorizedItems, cart) {
categorizedItems?.backNavigation then CartBackNavigationHandler(cart)
categorizedItems?.backNavigation then SearchBackNavigationHandler() then CartBackNavigationHandler(cart)
}
}
}
private inner class SearchBackNavigationHandler : BackNavigationHandler {
override fun canNavigateBack() = showSearch
override fun onNavigateBack(): Boolean {
if (showSearch) {
showSearch = false
return true
}
return false
}
}
......@@ -242,27 +292,75 @@ private fun TabbedItemGroups(
model.pagerState,
beyondViewportPageCount = 4
) { index ->
if (index == 0) CustomItems(
state = model.customItemsState,
cart = model.cart,
Modifier
.weight(1f)
.fillMaxHeight()
) else CategorizedItems(
state = model.categorizedItemsByGroup[index - 1],
cart = model.cart,
Modifier
.weight(1f)
.fillMaxHeight()
if (index == 0) CustomItemsTab(
model = model,
Modifier.fillMaxHeight()
) else ItemGroupTab(
model = model,
groupIndex = index - 1,
Modifier.fillMaxHeight()
)
}
}
@Composable
private fun CustomItemsTab(
model: ManualBillScreenModel,
modifier: Modifier = Modifier
) {
val state = model.searchItems
if (state != null) {
CategorizedItems(
state = state,
cart = model.cart,
modifier
)
} else CustomItems(
state = model.customItemsState,
cart = model.cart,
modifier
)
}
@Composable
private fun ItemGroupTab(
model: ManualBillScreenModel,
groupIndex: Int,
modifier: Modifier = Modifier
) {
CategorizedItems(
state = model.categorizedItemsByGroup[groupIndex],
cart = model.cart,
modifier
)
}
@Composable
fun ManualBillTopBarTitle(
model: ManualBillScreenModel,
navigator: Navigator
) {
if (model.showSearch) {
ItemSearchField(
query = model.searchQuery,
onQueryChange = { model.searchQuery = it },
)
} else {
val name = model.accountName
if (name != null) AppSubpageTitle("$name")
else AppModuleSelection()
}
}
@Composable
fun ManualBillTopBarActions(
model: ManualBillScreenModel
) {
if (!model.showSearch) IconButton(onClick = { model.showSearch = !model.showSearch }) {
Icon(Icons.Default.Search, "Suchen")
}
val navigator = LocalNavigator.currentOrThrow
IconButton(onClick = {
navigator.push(KaffeekasseNavigation.AccountScreen(model.session))
......
......@@ -77,7 +77,6 @@ import net.novagamestudios.kaffeekasse.util.richdata.RichDataState
import net.novagamestudios.kaffeekasse.util.richdata.collectAsRichStateIn
import net.novagamestudios.kaffeekasse.util.richdata.combineRich
import net.novagamestudios.kaffeekasse.util.richdata.dataOrNull
import net.novagamestudios.kaffeekasse.util.richdata.flatMapLatestRich
import net.novagamestudios.kaffeekasse.util.richdata.mapRich
import net.novagamestudios.kaffeekasse.util.richdata.stateIn
import kotlin.time.Duration.Companion.seconds
......@@ -229,9 +228,9 @@ private class APIPurchaseController(
}.stateIn(coroutineScope, SharingStarted.Eagerly)
private val selfId get() = selfUserBasic.value.dataOrNull?.id
private val selfUserExtended = selfUserBasic.flatMapLatestRich {
kaffeekasse.getExtendedUserInfo(it.id)
}
// private val selfUserExtended = selfUserBasic.flatMapLatestRich {
// kaffeekasse.getExtendedUserInfo(it.id)
// }
// override val purchaseAccounts: RichDataFlow<List<PurchaseAccount>> = combineRich(
// selfUserExtended,
......
package net.novagamestudios.kaffeekasse.ui.kaffeekasse.components
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Clear
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.SearchBarDefaults
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
@Composable
fun ItemSearchField(
query: String,
onQueryChange: (String) -> Unit,
modifier: Modifier = Modifier
) {
val focusRequester = remember { FocusRequester() }
LaunchedEffect(Unit) {
focusRequester.requestFocus()
}
SearchBarDefaults.InputField(
query = query,
onQueryChange = onQueryChange,
onSearch = { },
expanded = false,
onExpandedChange = { },
modifier = modifier.focusRequester(focusRequester),
placeholder = { Text("Suchen") },
trailingIcon = {
if (query.isNotEmpty()) IconButton(onClick = { onQueryChange("") }) {
Icon(Icons.Default.Clear, "Clear")
}
},
)
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment