fix: RSS搜索框
This commit is contained in:
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user