Implement title keyword search in history (#840)

* Implement title keyword search in history

* Don't use Dispatchers.IO
This commit is contained in:
FooIbar authored and GitHub committed 2024-03-03 14:51:30 +00:00
1 parent 01f51b1000
commit 7738b7bc29
8 files changed
+70 -48

No files matched your search

@@ -194,6 +194,8 @@ object EhDB {
val historyLazyList
get() = db.historyDao().joinListLazy()
fun searchHistory(keyword: String) = db.historyDao().joinListLazy("%$keyword%")
val localFavLazyList
get() = db.localFavoritesDao().joinListLazy()
@@ -18,6 +18,10 @@ interface HistoryDao {
@Query("SELECT * FROM HISTORY JOIN GALLERIES USING(GID) ORDER BY TIME DESC")
fun joinListLazy(): PagingSource<Int, BaseGalleryInfo>
@RewriteQueriesToDropUnusedColumns
@Query("SELECT * FROM HISTORY JOIN GALLERIES USING(GID) WHERE TITLE LIKE :title OR TITLE_JPN LIKE :title ORDER BY TIME DESC")
fun joinListLazy(title: String): PagingSource<Int, BaseGalleryInfo>
@Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun insertOrIgnore(historyInfoList: List<HistoryInfo>)
@@ -23,7 +23,7 @@ interface LocalFavoritesDao {
fun joinListLazy(): PagingSource<Int, BaseGalleryInfo>
@RewriteQueriesToDropUnusedColumns
@Query("SELECT * FROM LOCAL_FAVORITES JOIN GALLERIES USING(GID) WHERE TITLE LIKE :title ORDER BY TIME DESC")
@Query("SELECT * FROM LOCAL_FAVORITES JOIN GALLERIES USING(GID) WHERE TITLE LIKE :title OR TITLE_JPN LIKE :title ORDER BY TIME DESC")
fun joinListLazy(title: String): PagingSource<Int, BaseGalleryInfo>
@RewriteQueriesToDropUnusedColumns
@@ -57,7 +57,7 @@ import com.hippo.ehviewer.util.displayString
import kotlinx.coroutines.flow.drop
@Stable
private operator fun PaddingValues.plus(r: PaddingValues) = object : PaddingValues {
operator fun PaddingValues.plus(r: PaddingValues) = object : PaddingValues {
val l = this@plus
override fun calculateBottomPadding() = l.calculateBottomPadding() + r.calculateBottomPadding()
override fun calculateLeftPadding(layoutDirection: LayoutDirection) = l.calculateLeftPadding(layoutDirection) + r.calculateLeftPadding(layoutDirection)
@@ -25,7 +25,6 @@ import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells
import androidx.compose.foundation.lazy.staggeredgrid.items
import androidx.compose.foundation.text.input.rememberTextFieldState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.DriveFileMove
import androidx.compose.material.icons.automirrored.filled.Sort
@@ -131,7 +130,6 @@ fun DownloadsScreen(navigator: DestinationsNavigator) = composing(navigator) {
var filterState by rememberSaveable { mutableStateOf(DownloadsFilterState(Settings.recentDownloadLabel.value)) }
var invalidateKey by rememberSaveable { mutableStateOf(false) }
var searchBarOffsetY by remember(filterState.label) { mutableIntStateOf(0) }
val searchFieldState = rememberTextFieldState()
var fabExpanded by remember { mutableStateOf(false) }
var fabHidden by remember { mutableStateOf(false) }
@@ -350,7 +348,6 @@ fun DownloadsScreen(navigator: DestinationsNavigator) = composing(navigator) {
SearchBarScreen(
title = title,
searchFieldState = searchFieldState,
searchFieldHint = hint,
onApplySearch = { filterState = filterState.copy(keyword = it) },
onSearchExpanded = {
@@ -10,7 +10,6 @@ import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.input.rememberTextFieldState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.DriveFileMove
@@ -221,8 +220,6 @@ fun FavouritesScreen(navigator: DestinationsNavigator) {
}
}
val searchFieldState = rememberTextFieldState()
val refreshState = rememberPullToRefreshState {
data.loadState.refresh is LoadState.NotLoading
}
@@ -235,7 +232,6 @@ fun FavouritesScreen(navigator: DestinationsNavigator) {
SearchBarScreen(
title = title,
searchFieldState = searchFieldState,
searchFieldHint = searchBarHint,
onApplySearch = { refresh(FavListUrlBuilder(urlBuilder.favCat, it)) },
onSearchExpanded = {
@@ -2,6 +2,7 @@ package com.hippo.ehviewer.ui.screen
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
@@ -9,21 +10,26 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ClearAll
import androidx.compose.material.icons.filled.Menu
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SwipeToDismissBoxValue
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberSwipeToDismissBoxState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
@@ -40,10 +46,10 @@ import com.hippo.ehviewer.Settings
import com.hippo.ehviewer.collectAsState
import com.hippo.ehviewer.icons.EhIcons
import com.hippo.ehviewer.icons.big.History
import com.hippo.ehviewer.ui.LocalNavDrawerState
import com.hippo.ehviewer.ui.composing
import com.hippo.ehviewer.ui.doGalleryInfoAction
import com.hippo.ehviewer.ui.main.GalleryInfoListItem
import com.hippo.ehviewer.ui.main.plus
import com.hippo.ehviewer.ui.tools.Deferred
import com.hippo.ehviewer.ui.tools.FastScrollLazyColumn
import com.hippo.ehviewer.ui.tools.SwipeToDismissBox2
@@ -51,54 +57,70 @@ import com.hippo.ehviewer.ui.tools.rememberInVM
import com.hippo.ehviewer.util.FavouriteStatusRouter
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import eu.kanade.tachiyomi.util.lang.launchIO
import kotlin.math.roundToInt
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@Destination
@Composable
fun HistoryScreen(navigator: DestinationsNavigator) = composing(navigator) {
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
val title = stringResource(id = R.string.history)
val hint = stringResource(R.string.search_bar_hint, title)
var searchBarOffsetY by remember { mutableIntStateOf(0) }
var keyword by rememberSaveable { mutableStateOf("") }
val density = LocalDensity.current
val historyData = rememberInVM {
Pager(config = PagingConfig(pageSize = 20, jumpThreshold = 40)) {
EhDB.historyLazyList
if (keyword.isNotEmpty()) {
EhDB.searchHistory(keyword)
} else {
EhDB.historyLazyList
}
}.flow.cachedIn(viewModelScope)
}.collectAsLazyPagingItems()
FavouriteStatusRouter.Observe(historyData)
Scaffold(
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
topBar = {
TopAppBar(
title = { Text(text = stringResource(id = R.string.history)) },
navigationIcon = {
val drawerState = LocalNavDrawerState.current
IconButton(onClick = { launch { drawerState.open() } }) {
Icon(imageVector = Icons.Default.Menu, contentDescription = null)
}
},
actions = {
IconButton(onClick = {
launchIO {
awaitPermissionOrCancel(
confirmText = R.string.clear_all,
text = { Text(text = stringResource(id = R.string.clear_all_history)) },
)
EhDB.clearHistoryInfo()
}
}) {
Icon(imageVector = Icons.Default.ClearAll, contentDescription = null)
}
},
scrollBehavior = scrollBehavior,
)
SearchBarScreen(
title = title,
searchFieldHint = hint,
onApplySearch = {
keyword = it
historyData.refresh()
},
onSearchExpanded = {},
onSearchHidden = {},
searchBarOffsetY = { searchBarOffsetY },
trailingIcon = {
IconButton(onClick = {
launch {
awaitPermissionOrCancel(
confirmText = R.string.clear_all,
text = { Text(text = stringResource(id = R.string.clear_all_history)) },
)
EhDB.clearHistoryInfo()
}
}) {
Icon(imageVector = Icons.Default.ClearAll, contentDescription = null)
}
},
) { paddingValues ->
val searchBarConnection = remember {
val topPaddingPx = with(density) { paddingValues.calculateTopPadding().roundToPx() }
object : NestedScrollConnection {
override fun onPostScroll(consumed: Offset, available: Offset, source: NestedScrollSource): Offset {
val dy = -consumed.y
searchBarOffsetY = (searchBarOffsetY - dy).roundToInt().coerceIn(-topPaddingPx, 0)
return Offset.Zero // We never consume it
}
}
}
val marginH = dimensionResource(id = R.dimen.gallery_list_margin_h)
val cardHeight by collectListThumbSizeAsState()
val showPages by Settings.showGalleryPages.collectAsState()
FastScrollLazyColumn(
modifier = Modifier.padding(horizontal = marginH).fillMaxSize(),
contentPadding = paddingValues,
modifier = Modifier.nestedScroll(searchBarConnection).fillMaxSize(),
contentPadding = paddingValues + PaddingValues(horizontal = marginH),
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.gallery_list_interval)),
) {
items(
@@ -111,7 +133,7 @@ fun HistoryScreen(navigator: DestinationsNavigator) = composing(navigator) {
val dismissState = rememberSwipeToDismissBoxState(
confirmValueChange = {
if (it == SwipeToDismissBoxValue.EndToStart) {
launchIO { EhDB.deleteHistoryInfo(info) }
launch { EhDB.deleteHistoryInfo(info) }
}
true
},
@@ -123,7 +145,7 @@ fun HistoryScreen(navigator: DestinationsNavigator) = composing(navigator) {
) {
GalleryInfoListItem(
onClick = { navigate(info.asDst()) },
onLongClick = { launchIO { doGalleryInfoAction(info) } },
onLongClick = { launch { doGalleryInfoAction(info) } },
info = info,
showPages = showPages,
modifier = Modifier.height(cardHeight),
@@ -26,6 +26,7 @@ import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.text.input.TextFieldState
import androidx.compose.foundation.text.input.clearText
import androidx.compose.foundation.text.input.forEachTextValue
import androidx.compose.foundation.text.input.rememberTextFieldState
import androidx.compose.foundation.text.input.setTextAndPlaceCursorAtEnd
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
@@ -95,7 +96,7 @@ suspend fun SearchDao.suggestions(prefix: String, limit: Int) =
@Composable
fun SearchBarScreen(
title: String? = null,
searchFieldState: TextFieldState,
searchFieldState: TextFieldState = rememberTextFieldState(),
searchFieldHint: String? = null,
onApplySearch: (String) -> Unit,
onSearchExpanded: () -> Unit,