implement search

This commit is contained in:
2023-07-14 13:00:12 +03:00
parent 2178a0c9a4
commit bcd2591ba7
2 changed files with 77 additions and 16 deletions
@@ -189,15 +189,17 @@ fun HomePage(
navigateToDetailsScreen: (item: PackageItem) -> Unit,
settingsPrefs: SettingsPreferences,
settingsViewModel: SettingsViewModel,
packageListViewModel: PackageListViewModel = viewModel(
factory = PackageListViewModel.Factory
),
) {
var isSearchOpen by rememberSaveable { mutableStateOf(false)}
var searchQuery by rememberSaveable { mutableStateOf("")}
val searchBar = remember { FocusRequester() }
var layoutDialogOpen by remember { mutableStateOf(false) }
val context = LocalContext.current
val systemUiController = rememberSystemUiController()
val navbarScrimColor = MaterialTheme.colorScheme.surfaceColorAtElevation(3.dp)
val uiState = packageListViewModel.uiState.collectAsStateWithLifecycle().value
val tsvs = settingsViewModel.availableTsvs()
var selectedBarItem by rememberSaveable { mutableStateOf(ConsoleType.PSVITA) }
@@ -215,22 +217,26 @@ fun HomePage(
topBar = {
TopAppBar(
title = {
if (!isSearchOpen) {
if (!uiState.isSearchOpen) {
Text(text = stringResource(id = R.string.app_name))
}
},
actions = {
if (isSearchOpen) {
if (uiState.isSearchOpen) {
NPSIconButton(
tooltip = "Close Search",
onClick = { isSearchOpen = false }
onClick = {
packageListViewModel.closeSearch()
}
){
Icon(imageVector = Icons.Filled.ArrowBack, contentDescription = "Close Search")
}
Spacer(Modifier.width(12.dp))
TextField(
value = searchQuery,
onValueChange = {str -> searchQuery = str},
value = uiState.searchQuery,
onValueChange = {
packageListViewModel.search(it)
},
Modifier
.weight(1.0f)
.focusRequester(searchBar),
@@ -248,10 +254,12 @@ fun HomePage(
),
)
Spacer(Modifier.width(12.dp))
if (searchQuery.isNotEmpty()) {
if (uiState.searchQuery.isNotEmpty()) {
NPSIconButton(
tooltip = "Clear Search",
onClick = { searchQuery = "" }
onClick = {
packageListViewModel.clearSearch()
}
){
Icon(imageVector = Icons.Filled.Close, contentDescription = "Clear Search")
}
@@ -261,10 +269,10 @@ fun HomePage(
searchBar.requestFocus()
}
}
if (!isSearchOpen) {
if (!uiState.isSearchOpen) {
NPSIconButton(
tooltip = stringResource(id = R.string.search_tooltip),
onClick = { isSearchOpen = true }
onClick = { packageListViewModel.openSearch() }
){
Icon(imageVector = Icons.Filled.Search, contentDescription = stringResource(
id = R.string.search_tooltip
@@ -306,7 +314,8 @@ fun HomePage(
PackageList(
navigateToDetailsScreen = navigateToDetailsScreen,
type = selectedBarItem,
settingsPrefs = settingsPrefs
settingsPrefs = settingsPrefs,
viewModel = packageListViewModel,
)
}
}
@@ -321,6 +330,18 @@ fun HomePage(
}
}
@Composable
fun NoResults() {
Box(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
contentAlignment = Alignment.Center
) {
Text(text = "No results found", color = MaterialTheme.colorScheme.outline)
}
}
@Composable
fun NavBar(selectedTab: ConsoleType, onTabSelected: (ConsoleType) -> Unit) {
NavigationBar {
@@ -411,9 +432,7 @@ fun NoTsvs(padding: PaddingValues = PaddingValues(0.dp), navigateToSettings: ()
fun PackageList(
type: ConsoleType = ConsoleType.PSVITA,
navigateToDetailsScreen: (item: PackageItem) -> Unit,
viewModel: PackageListViewModel = viewModel(
factory = PackageListViewModel.Factory
),
viewModel: PackageListViewModel,
settingsPrefs: SettingsPreferences,
) {
val uiState = viewModel.uiState.collectAsStateWithLifecycle().value
@@ -447,7 +466,7 @@ fun PackageList(
CircularProgressIndicator()
}
} else {
if (packages.isEmpty()) {
if (packages.isEmpty() && uiState.searchQuery.isEmpty()) {
Box(
modifier = Modifier
.fillMaxSize()
@@ -461,6 +480,8 @@ fun PackageList(
color = MaterialTheme.colorScheme.outline
)
}
} else if (packages.isEmpty() && uiState.searchQuery.isNotEmpty()) {
NoResults()
} else {
if (settingsPrefs.layout == ItemLayout.LIST.ordinal) {
LazyColumn {
@@ -19,6 +19,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import okhttp3.internal.toLongOrDefault
import java.io.InputStream
@@ -27,10 +28,13 @@ data class PackageListUiState(
val packages: ArrayList<PackageItem> = ArrayList(),
val error: String = "",
val isLoading: Boolean = true,
val isSearchOpen: Boolean = false,
val searchQuery: String = "",
)
class PackageListViewModel(app: NPSApp, settingsRepo: SettingsPreferencesRepository): ViewModel() {
private val _uiState = MutableStateFlow(PackageListUiState())
private val immutablePackages = ArrayList<PackageItem>()
val uiState: StateFlow<PackageListUiState> = _uiState.asStateFlow()
private val tsvReader = csvReader {
@@ -52,6 +56,7 @@ class PackageListViewModel(app: NPSApp, settingsRepo: SettingsPreferencesReposit
list.sortBy { it.name }
_uiState.emit(PackageListUiState(isLoading = false, packages = list))
immutablePackages.addAll(list)
settingsRepo.tsvFiles().collect { tsv ->
_uiState.emit(PackageListUiState())
@@ -70,10 +75,45 @@ class PackageListViewModel(app: NPSApp, settingsRepo: SettingsPreferencesReposit
list.sortBy { it.name }
_uiState.emit(PackageListUiState(isLoading = false, packages = list))
immutablePackages.addAll(list)
}
}
}
fun search(query: String) {
_uiState.update {
it.copy(
packages = immutablePackages.filter { item ->
item.name.contains(query, true)
} as ArrayList<PackageItem>,
searchQuery = query
)
}
}
fun openSearch() {
_uiState.update {
it.copy(isSearchOpen = true)
}
}
fun closeSearch() {
_uiState.update {
it.copy(
packages = immutablePackages,
searchQuery = "",
isSearchOpen = false
)
}
}
fun clearSearch() {
_uiState.update {
it.copy(packages = immutablePackages, searchQuery = "")
}
}
private fun parsePackageFromTSV(
it: Triple<String, ConsoleType, PackageItemType>, app: NPSApp
): ArrayList<PackageItem> {