Fix extension search query cursor and debounce (#8972)
* Fix extension search query cursor * debounce * extract debounce constant
This commit is contained in:
parent
cdf242e8c8
commit
1a319601de
6 changed files with 17 additions and 14 deletions
|
@ -58,7 +58,7 @@ import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||||
fun ExtensionScreen(
|
fun ExtensionScreen(
|
||||||
state: ExtensionsState,
|
state: ExtensionsState,
|
||||||
contentPadding: PaddingValues,
|
contentPadding: PaddingValues,
|
||||||
searchQuery: String? = null,
|
searchQuery: String?,
|
||||||
onLongClickItem: (Extension) -> Unit,
|
onLongClickItem: (Extension) -> Unit,
|
||||||
onClickItemCancel: (Extension) -> Unit,
|
onClickItemCancel: (Extension) -> Unit,
|
||||||
onInstallExtension: (Extension.Available) -> Unit,
|
onInstallExtension: (Extension.Available) -> Unit,
|
||||||
|
|
|
@ -48,6 +48,8 @@ import eu.kanade.presentation.util.runOnEnterKeyPressed
|
||||||
import eu.kanade.presentation.util.secondaryItemAlpha
|
import eu.kanade.presentation.util.secondaryItemAlpha
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
|
|
||||||
|
const val SEARCH_DEBOUNCE_MILLIS = 250L
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AppBar(
|
fun AppBar(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
|
|
@ -44,7 +44,7 @@ data class BrowseTab(
|
||||||
|
|
||||||
// Hoisted for extensions tab's search bar
|
// Hoisted for extensions tab's search bar
|
||||||
val extensionsScreenModel = rememberScreenModel { ExtensionsScreenModel() }
|
val extensionsScreenModel = rememberScreenModel { ExtensionsScreenModel() }
|
||||||
val extensionsQuery by extensionsScreenModel.query.collectAsState()
|
val extensionsState by extensionsScreenModel.state.collectAsState()
|
||||||
|
|
||||||
TabbedScreen(
|
TabbedScreen(
|
||||||
titleRes = R.string.browse,
|
titleRes = R.string.browse,
|
||||||
|
@ -54,7 +54,7 @@ data class BrowseTab(
|
||||||
migrateSourceTab(),
|
migrateSourceTab(),
|
||||||
),
|
),
|
||||||
startIndex = 1.takeIf { toExtensions },
|
startIndex = 1.takeIf { toExtensions },
|
||||||
searchQuery = extensionsQuery,
|
searchQuery = extensionsState.searchQuery,
|
||||||
onChangeSearchQuery = extensionsScreenModel::search,
|
onChangeSearchQuery = extensionsScreenModel::search,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@ import cafe.adriel.voyager.core.model.StateScreenModel
|
||||||
import cafe.adriel.voyager.core.model.coroutineScope
|
import cafe.adriel.voyager.core.model.coroutineScope
|
||||||
import eu.kanade.domain.extension.interactor.GetExtensionsByType
|
import eu.kanade.domain.extension.interactor.GetExtensionsByType
|
||||||
import eu.kanade.domain.source.service.SourcePreferences
|
import eu.kanade.domain.source.service.SourcePreferences
|
||||||
|
import eu.kanade.presentation.components.SEARCH_DEBOUNCE_MILLIS
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.extension.ExtensionManager
|
import eu.kanade.tachiyomi.extension.ExtensionManager
|
||||||
import eu.kanade.tachiyomi.extension.model.Extension
|
import eu.kanade.tachiyomi.extension.model.Extension
|
||||||
|
@ -15,11 +16,12 @@ import eu.kanade.tachiyomi.util.lang.launchIO
|
||||||
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
import eu.kanade.tachiyomi.util.system.LocaleHelper
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import kotlinx.coroutines.flow.debounce
|
||||||
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
|
@ -33,9 +35,6 @@ class ExtensionsScreenModel(
|
||||||
private val getExtensions: GetExtensionsByType = Injekt.get(),
|
private val getExtensions: GetExtensionsByType = Injekt.get(),
|
||||||
) : StateScreenModel<ExtensionsState>(ExtensionsState()) {
|
) : StateScreenModel<ExtensionsState>(ExtensionsState()) {
|
||||||
|
|
||||||
private val _query: MutableStateFlow<String?> = MutableStateFlow(null)
|
|
||||||
val query: StateFlow<String?> = _query.asStateFlow()
|
|
||||||
|
|
||||||
private var _currentDownloads = MutableStateFlow<Map<String, InstallStep>>(hashMapOf())
|
private var _currentDownloads = MutableStateFlow<Map<String, InstallStep>>(hashMapOf())
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
@ -74,7 +73,7 @@ class ExtensionsScreenModel(
|
||||||
|
|
||||||
coroutineScope.launchIO {
|
coroutineScope.launchIO {
|
||||||
combine(
|
combine(
|
||||||
_query,
|
state.map { it.searchQuery }.distinctUntilChanged().debounce(SEARCH_DEBOUNCE_MILLIS),
|
||||||
_currentDownloads,
|
_currentDownloads,
|
||||||
getExtensions.subscribe(),
|
getExtensions.subscribe(),
|
||||||
) { query, downloads, (_updates, _installed, _available, _untrusted) ->
|
) { query, downloads, (_updates, _installed, _available, _untrusted) ->
|
||||||
|
@ -124,8 +123,8 @@ class ExtensionsScreenModel(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun search(query: String?) {
|
fun search(query: String?) {
|
||||||
coroutineScope.launchIO {
|
mutableState.update {
|
||||||
_query.emit(query)
|
it.copy(searchQuery = query)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,6 +210,7 @@ data class ExtensionsState(
|
||||||
val isRefreshing: Boolean = false,
|
val isRefreshing: Boolean = false,
|
||||||
val items: ItemGroups = mutableMapOf(),
|
val items: ItemGroups = mutableMapOf(),
|
||||||
val updates: Int = 0,
|
val updates: Int = 0,
|
||||||
|
val searchQuery: String? = null,
|
||||||
) {
|
) {
|
||||||
val isEmpty = items.isEmpty()
|
val isEmpty = items.isEmpty()
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,6 @@ fun extensionsTab(
|
||||||
): TabContent {
|
): TabContent {
|
||||||
val navigator = LocalNavigator.currentOrThrow
|
val navigator = LocalNavigator.currentOrThrow
|
||||||
val state by extensionsScreenModel.state.collectAsState()
|
val state by extensionsScreenModel.state.collectAsState()
|
||||||
val searchQuery by extensionsScreenModel.query.collectAsState()
|
|
||||||
|
|
||||||
return TabContent(
|
return TabContent(
|
||||||
titleRes = R.string.label_extensions,
|
titleRes = R.string.label_extensions,
|
||||||
|
@ -38,7 +37,7 @@ fun extensionsTab(
|
||||||
ExtensionScreen(
|
ExtensionScreen(
|
||||||
state = state,
|
state = state,
|
||||||
contentPadding = contentPadding,
|
contentPadding = contentPadding,
|
||||||
searchQuery = searchQuery,
|
searchQuery = state.searchQuery,
|
||||||
onLongClickItem = { extension ->
|
onLongClickItem = { extension ->
|
||||||
when (extension) {
|
when (extension) {
|
||||||
is Extension.Available -> extensionsScreenModel.installExtension(extension)
|
is Extension.Available -> extensionsScreenModel.installExtension(extension)
|
||||||
|
|
|
@ -26,6 +26,7 @@ import eu.kanade.domain.manga.interactor.GetLibraryManga
|
||||||
import eu.kanade.domain.manga.interactor.UpdateManga
|
import eu.kanade.domain.manga.interactor.UpdateManga
|
||||||
import eu.kanade.domain.manga.model.isLocal
|
import eu.kanade.domain.manga.model.isLocal
|
||||||
import eu.kanade.domain.track.interactor.GetTracksPerManga
|
import eu.kanade.domain.track.interactor.GetTracksPerManga
|
||||||
|
import eu.kanade.presentation.components.SEARCH_DEBOUNCE_MILLIS
|
||||||
import eu.kanade.presentation.library.components.LibraryToolbarTitle
|
import eu.kanade.presentation.library.components.LibraryToolbarTitle
|
||||||
import eu.kanade.presentation.manga.DownloadAction
|
import eu.kanade.presentation.manga.DownloadAction
|
||||||
import eu.kanade.tachiyomi.data.cache.CoverCache
|
import eu.kanade.tachiyomi.data.cache.CoverCache
|
||||||
|
@ -44,6 +45,7 @@ import eu.kanade.tachiyomi.widget.ExtendedNavigationView.Item.TriStateGroup
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import kotlinx.coroutines.flow.debounce
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.flow.flowOf
|
import kotlinx.coroutines.flow.flowOf
|
||||||
|
@ -92,7 +94,7 @@ class LibraryScreenModel(
|
||||||
init {
|
init {
|
||||||
coroutineScope.launchIO {
|
coroutineScope.launchIO {
|
||||||
combine(
|
combine(
|
||||||
state.map { it.searchQuery }.distinctUntilChanged(),
|
state.map { it.searchQuery }.distinctUntilChanged().debounce(SEARCH_DEBOUNCE_MILLIS),
|
||||||
getLibraryFlow(),
|
getLibraryFlow(),
|
||||||
getTracksPerManga.subscribe(),
|
getTracksPerManga.subscribe(),
|
||||||
getTrackingFilterFlow(),
|
getTrackingFilterFlow(),
|
||||||
|
|
Reference in a new issue