diff --git a/src/main/kotlin/ca/gosyer/ui/library/LibraryScreen.kt b/src/main/kotlin/ca/gosyer/ui/library/LibraryScreen.kt index 238135fe..8e19ea65 100644 --- a/src/main/kotlin/ca/gosyer/ui/library/LibraryScreen.kt +++ b/src/main/kotlin/ca/gosyer/ui/library/LibraryScreen.kt @@ -60,6 +60,7 @@ fun LibraryScreen(onClickManga: (Long) -> Unit = { openMangaMenu(it) }) { val isLoading by vm.isLoading.collectAsState() val error by vm.error.collectAsState() val serverUrl by vm.serverUrl.collectAsState() + val query by vm.query.collectAsState() // val sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden) if (categories.isEmpty()) { @@ -85,7 +86,12 @@ fun LibraryScreen(onClickManga: (Long) -> Unit = { openMangaMenu(it) }) { } } )*/ - Toolbar(stringResource("location_library"), closable = false) + Toolbar( + stringResource("location_library"), + closable = false, + searchText = query, + search = vm::updateQuery + ) LibraryTabs( visible = true, // vm.showCategoryTabs, categories = categories, diff --git a/src/main/kotlin/ca/gosyer/ui/library/LibraryScreenViewModel.kt b/src/main/kotlin/ca/gosyer/ui/library/LibraryScreenViewModel.kt index 530737f5..9a73ca63 100644 --- a/src/main/kotlin/ca/gosyer/ui/library/LibraryScreenViewModel.kt +++ b/src/main/kotlin/ca/gosyer/ui/library/LibraryScreenViewModel.kt @@ -19,17 +19,51 @@ import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.buffer +import kotlinx.coroutines.flow.cancellable +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.mapLatest +import kotlinx.coroutines.flow.toList import kotlinx.coroutines.launch import javax.inject.Inject -private typealias LibraryMap = MutableMap>> +private typealias CategoryItems = Pair>, MutableStateFlow>> +private typealias LibraryMap = MutableMap private data class Library(val categories: MutableStateFlow>, val mangaMap: LibraryMap) private fun LibraryMap.getManga(order: Int) = - getOrPut(order) { MutableStateFlow(emptyList()) } -private fun LibraryMap.setManga(order: Int, manga: List) { - getManga(order).value = manga + getOrPut(order) { MutableStateFlow(emptyList()) to MutableStateFlow(emptyList()) } +private suspend fun LibraryMap.setManga(query: String, order: Int, manga: List) { + getManga(order).let { (items, unfilteredItems) -> + items.value = filterManga(query, manga) + unfilteredItems.value = manga + } +} +private suspend fun LibraryMap.updateMangaFilter(query: String) { + values.forEach { (items, unfilteredItems) -> + items.value = filterManga(query, unfilteredItems.value) + } +} + +private suspend fun filterManga(query: String, mangaList: List): List { + if (query.isEmpty()) return mangaList + val queries = query.split(" ") + return mangaList.asFlow() + .filter { manga -> + queries.all { query -> + manga.title.contains(query, true) || + manga.author.orEmpty().contains(query, true) || + manga.artist.orEmpty().contains(query, true) || + manga.genre.any { it.contains(query, true) } || + manga.description.orEmpty().contains(query, true) + } + } + .cancellable() + .buffer() + .toList() } class LibraryScreenViewModel @Inject constructor( @@ -54,8 +88,15 @@ class LibraryScreenViewModel @Inject constructor( private val _error = MutableStateFlow(null) val error = _error.asStateFlow() + private val _query = MutableStateFlow("") + val query = _query.asStateFlow() + init { getLibrary() + + _query.mapLatest { + library.mangaMap.updateMangaFilter(it) + }.launchIn(scope) } private fun getLibrary() { @@ -82,14 +123,14 @@ class LibraryScreenViewModel @Inject constructor( } fun getLibraryForCategoryIndex(index: Int): StateFlow> { - return library.mangaMap.getManga(index).asStateFlow() + return library.mangaMap.getManga(index).first.asStateFlow() } private suspend fun updateCategories(categories: List) { withDefaultContext { categories.map { async { - library.mangaMap.setManga(it.order, categoryHandler.getMangaFromCategory(it)) + library.mangaMap.setManga("", it.order, categoryHandler.getMangaFromCategory(it)) } }.awaitAll() } @@ -98,7 +139,7 @@ class LibraryScreenViewModel @Inject constructor( private fun getCategoriesToUpdate(mangaId: Long): List { return library.mangaMap .filter { mangaMapEntry -> - mangaMapEntry.value.value.firstOrNull { it.id == mangaId } != null + mangaMapEntry.value.first.value.firstOrNull { it.id == mangaId } != null } .map { library.categories.value[it.key] } } @@ -109,4 +150,8 @@ class LibraryScreenViewModel @Inject constructor( updateCategories(getCategoriesToUpdate(mangaId)) } } + + fun updateQuery(query: String) { + _query.value = query + } }