Implement title keyword search in history (#840)
* Implement title keyword search in history * Don't use Dispatchers.IO
This commit is contained in:
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,
|
||||
|
||||
Reference in new issue
Block a user