fix: RSS搜索框

This commit is contained in:
HapeLee
2026-06-14 00:21:14 +08:00
parent fc096c92e8
commit b362161b7f
4 changed files with 114 additions and 11 deletions
@@ -404,7 +404,16 @@ fun MainActivity.mainEntryProvider(
RssSortRouteScreen(
sourceUrl = route.sourceUrl,
initialSortUrl = route.sortUrl,
initialSearchKey = route.key,
onBackClick = { onNavigateBack() },
onSearch = { key ->
onNavigateToRoute(
MainRouteRssSort(
sourceUrl = route.sourceUrl,
key = key
)
)
},
onOpenRead = { title, origin, link, openUrl ->
if (link?.contains("@js:") == true) {
onNavigateToRoute(
@@ -29,17 +29,23 @@ import androidx.compose.ui.res.stringResource
fun RssSortRouteScreen(
sourceUrl: String?,
initialSortUrl: String?,
initialSearchKey: String?,
onBackClick: () -> Unit,
onSearch: (String) -> Unit,
onOpenRead: (title: String?, origin: String, link: String?, openUrl: String?) -> Unit,
viewModel: RssSortViewModel = koinViewModel()
) {
val context = LocalContext.current
val scope = rememberCoroutineScope()
var sortList by remember(sourceUrl) { mutableStateOf<List<Pair<String, String>>>(emptyList()) }
var articleStyle by remember(sourceUrl) { mutableIntStateOf(0) }
var redirectPolicy by remember(sourceUrl) { mutableStateOf(RedirectPolicy.ALLOW_ALL) }
var screenTitle by remember(sourceUrl) { mutableStateOf("") }
var sortList by remember(sourceUrl, initialSortUrl, initialSearchKey) {
mutableStateOf<List<Pair<String, String>>>(emptyList())
}
var articleStyle by remember(sourceUrl, initialSortUrl, initialSearchKey) { mutableIntStateOf(0) }
var redirectPolicy by remember(sourceUrl, initialSortUrl, initialSearchKey) {
mutableStateOf(RedirectPolicy.ALLOW_ALL)
}
var screenTitle by remember(sourceUrl, initialSortUrl, initialSearchKey) { mutableStateOf("") }
val setSourceVariableText = stringResource(R.string.set_source_variable)
val errorText = stringResource(R.string.error)
@@ -51,9 +57,9 @@ fun RssSortRouteScreen(
withContext(Dispatchers.IO) {
viewModel.initDataSource(sourceUrl)
}
sortList = viewModel.loadSorts()
sortList = viewModel.loadSorts(initialSortUrl, initialSearchKey)
articleStyle = viewModel.currentArticleStyle()
screenTitle = viewModel.rssSource?.sourceName.orEmpty()
screenTitle = initialSearchKey ?: viewModel.rssSource?.sourceName.orEmpty()
redirectPolicy = RedirectPolicy.fromString(viewModel.rssSource?.redirectPolicy)
}
@@ -65,7 +71,7 @@ fun RssSortRouteScreen(
}
}
LaunchedEffect(sourceUrl) {
LaunchedEffect(sourceUrl, initialSortUrl, initialSearchKey) {
reloadSourceState()
}
@@ -73,12 +79,15 @@ fun RssSortRouteScreen(
title = screenTitle.ifBlank { stringResource(R.string.rss) },
sortList = sortList,
preferredSortUrl = initialSortUrl,
searchKey = initialSearchKey,
hasSearch = !viewModel.rssSource?.searchUrl.isNullOrBlank(),
hasLogin = !viewModel.rssSource?.loginUrl.isNullOrBlank(),
redirectPolicy = redirectPolicy,
showReadRecordSheet = showReadRecordSheet,
readRecords = readRecords,
sourceVariableSheet = sourceVariableSheet,
onBackClick = onBackClick,
onSearch = onSearch,
onLogin = {
context.startActivity<SourceLoginActivity> {
putExtra("type", "rssSource")
@@ -88,7 +97,7 @@ fun RssSortRouteScreen(
onRefreshSort = {
scope.launch {
viewModel.clearSortCache()
sortList = viewModel.loadSorts()
sortList = viewModel.loadSorts(initialSortUrl, initialSearchKey)
}
},
onSetSourceVariable = {
@@ -157,7 +166,7 @@ fun RssSortRouteScreen(
},
pagerContent = { _, sort, paddingValues ->
val pageViewModel: RssArticlesViewModel = koinViewModel(
key = "rss_${viewModel.url}_${sort.first}_${sort.second}"
key = "rss_${viewModel.url}_${sort.first}_${sort.second}_${initialSearchKey.orEmpty()}"
)
RssArticlesPage(
sortName = sort.first,
@@ -166,6 +175,7 @@ fun RssSortRouteScreen(
rssUrl = viewModel.url,
rssSource = viewModel.rssSource,
viewModel = pageViewModel,
searchKey = initialSearchKey,
paddingValues = paddingValues,
onRead = { article ->
viewModel.read(article)
@@ -54,6 +54,7 @@ import io.legado.app.ui.theme.adaptiveHorizontalPaddingTab
import io.legado.app.ui.widget.components.AppScaffold
import io.legado.app.ui.widget.components.AppTextField
import io.legado.app.ui.widget.components.EmptyMessage
import io.legado.app.ui.widget.components.SearchBar
import io.legado.app.ui.widget.components.button.series.MediumPlainButton
import io.legado.app.ui.widget.components.button.series.SmallPlainButton
import io.legado.app.ui.widget.components.button.series.SmallToggleButton
@@ -85,12 +86,15 @@ fun RssSortScreen(
title: String,
sortList: List<Pair<String, String>>,
preferredSortUrl: String?,
searchKey: String?,
hasSearch: Boolean,
hasLogin: Boolean,
redirectPolicy: RedirectPolicy,
showReadRecordSheet: Boolean,
readRecords: List<RssReadRecord>,
sourceVariableSheet: RssSourceVariableSheetState?,
onBackClick: () -> Unit,
onSearch: (String) -> Unit,
onLogin: () -> Unit,
onRefreshSort: () -> Unit,
onSetSourceVariable: () -> Unit,
@@ -120,15 +124,25 @@ fun RssSortScreen(
val tabTitles = sortList.map { it.first }
var showMainMenu by remember { mutableStateOf(false) }
var showSearchSheet by remember { mutableStateOf(false) }
var searchQuery by remember(searchKey) { mutableStateOf(searchKey.orEmpty()) }
var showRedirectMenu by remember { mutableStateOf(false) }
var showGroupMenu by remember { mutableStateOf(false) }
BackHandler(enabled = showMainMenu || showRedirectMenu || showGroupMenu) {
BackHandler(enabled = showMainMenu || showSearchSheet || showRedirectMenu || showGroupMenu) {
showMainMenu = false
showSearchSheet = false
showRedirectMenu = false
showGroupMenu = false
}
fun submitSearch(query: String) {
val normalized = query.trim()
if (normalized.isEmpty()) return
showSearchSheet = false
onSearch(normalized)
}
LaunchedEffect(sortList.size) {
if (sortList.isEmpty()) return@LaunchedEffect
val safeIndex = pagerState.currentPage.coerceIn(0, sortList.lastIndex)
@@ -147,6 +161,13 @@ fun RssSortScreen(
TopBarNavigationButton(onClick = onBackClick, imageVector = AppIcons.Back)
},
actions = {
if (hasSearch) {
TopBarActionButton(
onClick = { showSearchSheet = true },
imageVector = AppIcons.Search,
contentDescription = stringResource(R.string.search)
)
}
TopBarActionButton(
onClick = { showMainMenu = true },
imageVector = AppIcons.MoreVert,
@@ -328,6 +349,44 @@ fun RssSortScreen(
onDismissRequest = onDismissSourceVariable,
onSave = onSaveSourceVariable
)
RssSearchSheet(
show = showSearchSheet,
query = searchQuery,
onQueryChange = { searchQuery = it },
onSearch = ::submitSearch,
onDismissRequest = { showSearchSheet = false }
)
}
@Composable
private fun RssSearchSheet(
show: Boolean,
query: String,
onQueryChange: (String) -> Unit,
onSearch: (String) -> Unit,
onDismissRequest: () -> Unit
) {
AppModalBottomSheet(
show = show,
onDismissRequest = onDismissRequest,
title = stringResource(R.string.search),
endAction = {
SmallPlainButton(
onClick = { onSearch(query) },
icon = AppIcons.Search,
contentDescription = stringResource(R.string.search)
)
}
) {
SearchBar(
query = query,
onQueryChange = onQueryChange,
onSearch = onSearch,
placeholder = stringResource(R.string.search_content),
modifier = Modifier.fillMaxWidth()
)
}
}
@Composable
@@ -9,6 +9,9 @@ import io.legado.app.data.entities.RssReadRecord
import io.legado.app.data.entities.RssSource
import io.legado.app.help.source.removeSortCache
import io.legado.app.help.source.sortUrls
import io.legado.app.utils.GSONStrict
import io.legado.app.utils.fromJsonObject
import io.legado.app.utils.isJsonObject
import io.legado.app.utils.toastOnUi
import splitties.init.appCtx
@@ -32,10 +35,32 @@ class RssSortViewModel(application: Application) : BaseViewModel(application) {
}
}
suspend fun loadSorts(): List<Pair<String, String>> {
suspend fun loadSorts(sortUrl: String? = null, searchKey: String? = null): List<Pair<String, String>> {
if (searchKey != null) {
return rssSource?.searchUrl?.takeIf { it.isNotBlank() }?.let {
listOf("搜索" to it)
}.orEmpty()
}
sortUrl?.takeIf { it.isNotBlank() }?.let { url ->
return parseSortUrl(url)
}
return rssSource?.sortUrls().orEmpty()
}
private fun parseSortUrl(sortUrl: String): List<Pair<String, String>> {
return try {
if (sortUrl.isJsonObject()) {
GSONStrict.fromJsonObject<Map<String, String>>(sortUrl)
.getOrThrow()
.map { it.key to it.value }
} else {
listOf("" to sortUrl)
}
} catch (_: Exception) {
listOf("" to sortUrl)
}
}
fun currentArticleStyle(): Int = rssSource?.articleStyle ?: 0
fun switchLayout() {