mirror of
https://github.com/IllusionMan1212/NPS-Browser.git
synced 2026-10-24 02:12:37 +00:00
implement search
This commit is contained in:
@@ -189,15 +189,17 @@ fun HomePage(
|
|||||||
navigateToDetailsScreen: (item: PackageItem) -> Unit,
|
navigateToDetailsScreen: (item: PackageItem) -> Unit,
|
||||||
settingsPrefs: SettingsPreferences,
|
settingsPrefs: SettingsPreferences,
|
||||||
settingsViewModel: SettingsViewModel,
|
settingsViewModel: SettingsViewModel,
|
||||||
|
packageListViewModel: PackageListViewModel = viewModel(
|
||||||
|
factory = PackageListViewModel.Factory
|
||||||
|
),
|
||||||
) {
|
) {
|
||||||
var isSearchOpen by rememberSaveable { mutableStateOf(false)}
|
|
||||||
var searchQuery by rememberSaveable { mutableStateOf("")}
|
|
||||||
val searchBar = remember { FocusRequester() }
|
val searchBar = remember { FocusRequester() }
|
||||||
var layoutDialogOpen by remember { mutableStateOf(false) }
|
var layoutDialogOpen by remember { mutableStateOf(false) }
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val systemUiController = rememberSystemUiController()
|
val systemUiController = rememberSystemUiController()
|
||||||
val navbarScrimColor = MaterialTheme.colorScheme.surfaceColorAtElevation(3.dp)
|
val navbarScrimColor = MaterialTheme.colorScheme.surfaceColorAtElevation(3.dp)
|
||||||
|
|
||||||
|
val uiState = packageListViewModel.uiState.collectAsStateWithLifecycle().value
|
||||||
val tsvs = settingsViewModel.availableTsvs()
|
val tsvs = settingsViewModel.availableTsvs()
|
||||||
var selectedBarItem by rememberSaveable { mutableStateOf(ConsoleType.PSVITA) }
|
var selectedBarItem by rememberSaveable { mutableStateOf(ConsoleType.PSVITA) }
|
||||||
|
|
||||||
@@ -215,22 +217,26 @@ fun HomePage(
|
|||||||
topBar = {
|
topBar = {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
title = {
|
title = {
|
||||||
if (!isSearchOpen) {
|
if (!uiState.isSearchOpen) {
|
||||||
Text(text = stringResource(id = R.string.app_name))
|
Text(text = stringResource(id = R.string.app_name))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions = {
|
actions = {
|
||||||
if (isSearchOpen) {
|
if (uiState.isSearchOpen) {
|
||||||
NPSIconButton(
|
NPSIconButton(
|
||||||
tooltip = "Close Search",
|
tooltip = "Close Search",
|
||||||
onClick = { isSearchOpen = false }
|
onClick = {
|
||||||
|
packageListViewModel.closeSearch()
|
||||||
|
}
|
||||||
){
|
){
|
||||||
Icon(imageVector = Icons.Filled.ArrowBack, contentDescription = "Close Search")
|
Icon(imageVector = Icons.Filled.ArrowBack, contentDescription = "Close Search")
|
||||||
}
|
}
|
||||||
Spacer(Modifier.width(12.dp))
|
Spacer(Modifier.width(12.dp))
|
||||||
TextField(
|
TextField(
|
||||||
value = searchQuery,
|
value = uiState.searchQuery,
|
||||||
onValueChange = {str -> searchQuery = str},
|
onValueChange = {
|
||||||
|
packageListViewModel.search(it)
|
||||||
|
},
|
||||||
Modifier
|
Modifier
|
||||||
.weight(1.0f)
|
.weight(1.0f)
|
||||||
.focusRequester(searchBar),
|
.focusRequester(searchBar),
|
||||||
@@ -248,10 +254,12 @@ fun HomePage(
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
Spacer(Modifier.width(12.dp))
|
Spacer(Modifier.width(12.dp))
|
||||||
if (searchQuery.isNotEmpty()) {
|
if (uiState.searchQuery.isNotEmpty()) {
|
||||||
NPSIconButton(
|
NPSIconButton(
|
||||||
tooltip = "Clear Search",
|
tooltip = "Clear Search",
|
||||||
onClick = { searchQuery = "" }
|
onClick = {
|
||||||
|
packageListViewModel.clearSearch()
|
||||||
|
}
|
||||||
){
|
){
|
||||||
Icon(imageVector = Icons.Filled.Close, contentDescription = "Clear Search")
|
Icon(imageVector = Icons.Filled.Close, contentDescription = "Clear Search")
|
||||||
}
|
}
|
||||||
@@ -261,10 +269,10 @@ fun HomePage(
|
|||||||
searchBar.requestFocus()
|
searchBar.requestFocus()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!isSearchOpen) {
|
if (!uiState.isSearchOpen) {
|
||||||
NPSIconButton(
|
NPSIconButton(
|
||||||
tooltip = stringResource(id = R.string.search_tooltip),
|
tooltip = stringResource(id = R.string.search_tooltip),
|
||||||
onClick = { isSearchOpen = true }
|
onClick = { packageListViewModel.openSearch() }
|
||||||
){
|
){
|
||||||
Icon(imageVector = Icons.Filled.Search, contentDescription = stringResource(
|
Icon(imageVector = Icons.Filled.Search, contentDescription = stringResource(
|
||||||
id = R.string.search_tooltip
|
id = R.string.search_tooltip
|
||||||
@@ -306,7 +314,8 @@ fun HomePage(
|
|||||||
PackageList(
|
PackageList(
|
||||||
navigateToDetailsScreen = navigateToDetailsScreen,
|
navigateToDetailsScreen = navigateToDetailsScreen,
|
||||||
type = selectedBarItem,
|
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
|
@Composable
|
||||||
fun NavBar(selectedTab: ConsoleType, onTabSelected: (ConsoleType) -> Unit) {
|
fun NavBar(selectedTab: ConsoleType, onTabSelected: (ConsoleType) -> Unit) {
|
||||||
NavigationBar {
|
NavigationBar {
|
||||||
@@ -411,9 +432,7 @@ fun NoTsvs(padding: PaddingValues = PaddingValues(0.dp), navigateToSettings: ()
|
|||||||
fun PackageList(
|
fun PackageList(
|
||||||
type: ConsoleType = ConsoleType.PSVITA,
|
type: ConsoleType = ConsoleType.PSVITA,
|
||||||
navigateToDetailsScreen: (item: PackageItem) -> Unit,
|
navigateToDetailsScreen: (item: PackageItem) -> Unit,
|
||||||
viewModel: PackageListViewModel = viewModel(
|
viewModel: PackageListViewModel,
|
||||||
factory = PackageListViewModel.Factory
|
|
||||||
),
|
|
||||||
settingsPrefs: SettingsPreferences,
|
settingsPrefs: SettingsPreferences,
|
||||||
) {
|
) {
|
||||||
val uiState = viewModel.uiState.collectAsStateWithLifecycle().value
|
val uiState = viewModel.uiState.collectAsStateWithLifecycle().value
|
||||||
@@ -447,7 +466,7 @@ fun PackageList(
|
|||||||
CircularProgressIndicator()
|
CircularProgressIndicator()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (packages.isEmpty()) {
|
if (packages.isEmpty() && uiState.searchQuery.isEmpty()) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
@@ -461,6 +480,8 @@ fun PackageList(
|
|||||||
color = MaterialTheme.colorScheme.outline
|
color = MaterialTheme.colorScheme.outline
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
} else if (packages.isEmpty() && uiState.searchQuery.isNotEmpty()) {
|
||||||
|
NoResults()
|
||||||
} else {
|
} else {
|
||||||
if (settingsPrefs.layout == ItemLayout.LIST.ordinal) {
|
if (settingsPrefs.layout == ItemLayout.LIST.ordinal) {
|
||||||
LazyColumn {
|
LazyColumn {
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import okhttp3.internal.toLongOrDefault
|
import okhttp3.internal.toLongOrDefault
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
@@ -27,10 +28,13 @@ data class PackageListUiState(
|
|||||||
val packages: ArrayList<PackageItem> = ArrayList(),
|
val packages: ArrayList<PackageItem> = ArrayList(),
|
||||||
val error: String = "",
|
val error: String = "",
|
||||||
val isLoading: Boolean = true,
|
val isLoading: Boolean = true,
|
||||||
|
val isSearchOpen: Boolean = false,
|
||||||
|
val searchQuery: String = "",
|
||||||
)
|
)
|
||||||
|
|
||||||
class PackageListViewModel(app: NPSApp, settingsRepo: SettingsPreferencesRepository): ViewModel() {
|
class PackageListViewModel(app: NPSApp, settingsRepo: SettingsPreferencesRepository): ViewModel() {
|
||||||
private val _uiState = MutableStateFlow(PackageListUiState())
|
private val _uiState = MutableStateFlow(PackageListUiState())
|
||||||
|
private val immutablePackages = ArrayList<PackageItem>()
|
||||||
val uiState: StateFlow<PackageListUiState> = _uiState.asStateFlow()
|
val uiState: StateFlow<PackageListUiState> = _uiState.asStateFlow()
|
||||||
|
|
||||||
private val tsvReader = csvReader {
|
private val tsvReader = csvReader {
|
||||||
@@ -52,6 +56,7 @@ class PackageListViewModel(app: NPSApp, settingsRepo: SettingsPreferencesReposit
|
|||||||
list.sortBy { it.name }
|
list.sortBy { it.name }
|
||||||
|
|
||||||
_uiState.emit(PackageListUiState(isLoading = false, packages = list))
|
_uiState.emit(PackageListUiState(isLoading = false, packages = list))
|
||||||
|
immutablePackages.addAll(list)
|
||||||
|
|
||||||
settingsRepo.tsvFiles().collect { tsv ->
|
settingsRepo.tsvFiles().collect { tsv ->
|
||||||
_uiState.emit(PackageListUiState())
|
_uiState.emit(PackageListUiState())
|
||||||
@@ -70,10 +75,45 @@ class PackageListViewModel(app: NPSApp, settingsRepo: SettingsPreferencesReposit
|
|||||||
list.sortBy { it.name }
|
list.sortBy { it.name }
|
||||||
|
|
||||||
_uiState.emit(PackageListUiState(isLoading = false, packages = list))
|
_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(
|
private fun parsePackageFromTSV(
|
||||||
it: Triple<String, ConsoleType, PackageItemType>, app: NPSApp
|
it: Triple<String, ConsoleType, PackageItemType>, app: NPSApp
|
||||||
): ArrayList<PackageItem> {
|
): ArrayList<PackageItem> {
|
||||||
|
|||||||
Reference in New Issue
Block a user