fix: RSS搜索框
This commit is contained in:
@@ -404,7 +404,16 @@ fun MainActivity.mainEntryProvider(
|
|||||||
RssSortRouteScreen(
|
RssSortRouteScreen(
|
||||||
sourceUrl = route.sourceUrl,
|
sourceUrl = route.sourceUrl,
|
||||||
initialSortUrl = route.sortUrl,
|
initialSortUrl = route.sortUrl,
|
||||||
|
initialSearchKey = route.key,
|
||||||
onBackClick = { onNavigateBack() },
|
onBackClick = { onNavigateBack() },
|
||||||
|
onSearch = { key ->
|
||||||
|
onNavigateToRoute(
|
||||||
|
MainRouteRssSort(
|
||||||
|
sourceUrl = route.sourceUrl,
|
||||||
|
key = key
|
||||||
|
)
|
||||||
|
)
|
||||||
|
},
|
||||||
onOpenRead = { title, origin, link, openUrl ->
|
onOpenRead = { title, origin, link, openUrl ->
|
||||||
if (link?.contains("@js:") == true) {
|
if (link?.contains("@js:") == true) {
|
||||||
onNavigateToRoute(
|
onNavigateToRoute(
|
||||||
|
|||||||
@@ -29,17 +29,23 @@ import androidx.compose.ui.res.stringResource
|
|||||||
fun RssSortRouteScreen(
|
fun RssSortRouteScreen(
|
||||||
sourceUrl: String?,
|
sourceUrl: String?,
|
||||||
initialSortUrl: String?,
|
initialSortUrl: String?,
|
||||||
|
initialSearchKey: String?,
|
||||||
onBackClick: () -> Unit,
|
onBackClick: () -> Unit,
|
||||||
|
onSearch: (String) -> Unit,
|
||||||
onOpenRead: (title: String?, origin: String, link: String?, openUrl: String?) -> Unit,
|
onOpenRead: (title: String?, origin: String, link: String?, openUrl: String?) -> Unit,
|
||||||
viewModel: RssSortViewModel = koinViewModel()
|
viewModel: RssSortViewModel = koinViewModel()
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
var sortList by remember(sourceUrl) { mutableStateOf<List<Pair<String, String>>>(emptyList()) }
|
var sortList by remember(sourceUrl, initialSortUrl, initialSearchKey) {
|
||||||
var articleStyle by remember(sourceUrl) { mutableIntStateOf(0) }
|
mutableStateOf<List<Pair<String, String>>>(emptyList())
|
||||||
var redirectPolicy by remember(sourceUrl) { mutableStateOf(RedirectPolicy.ALLOW_ALL) }
|
}
|
||||||
var screenTitle by remember(sourceUrl) { mutableStateOf("") }
|
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 setSourceVariableText = stringResource(R.string.set_source_variable)
|
||||||
val errorText = stringResource(R.string.error)
|
val errorText = stringResource(R.string.error)
|
||||||
|
|
||||||
@@ -51,9 +57,9 @@ fun RssSortRouteScreen(
|
|||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
viewModel.initDataSource(sourceUrl)
|
viewModel.initDataSource(sourceUrl)
|
||||||
}
|
}
|
||||||
sortList = viewModel.loadSorts()
|
sortList = viewModel.loadSorts(initialSortUrl, initialSearchKey)
|
||||||
articleStyle = viewModel.currentArticleStyle()
|
articleStyle = viewModel.currentArticleStyle()
|
||||||
screenTitle = viewModel.rssSource?.sourceName.orEmpty()
|
screenTitle = initialSearchKey ?: viewModel.rssSource?.sourceName.orEmpty()
|
||||||
redirectPolicy = RedirectPolicy.fromString(viewModel.rssSource?.redirectPolicy)
|
redirectPolicy = RedirectPolicy.fromString(viewModel.rssSource?.redirectPolicy)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,7 +71,7 @@ fun RssSortRouteScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(sourceUrl) {
|
LaunchedEffect(sourceUrl, initialSortUrl, initialSearchKey) {
|
||||||
reloadSourceState()
|
reloadSourceState()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,12 +79,15 @@ fun RssSortRouteScreen(
|
|||||||
title = screenTitle.ifBlank { stringResource(R.string.rss) },
|
title = screenTitle.ifBlank { stringResource(R.string.rss) },
|
||||||
sortList = sortList,
|
sortList = sortList,
|
||||||
preferredSortUrl = initialSortUrl,
|
preferredSortUrl = initialSortUrl,
|
||||||
|
searchKey = initialSearchKey,
|
||||||
|
hasSearch = !viewModel.rssSource?.searchUrl.isNullOrBlank(),
|
||||||
hasLogin = !viewModel.rssSource?.loginUrl.isNullOrBlank(),
|
hasLogin = !viewModel.rssSource?.loginUrl.isNullOrBlank(),
|
||||||
redirectPolicy = redirectPolicy,
|
redirectPolicy = redirectPolicy,
|
||||||
showReadRecordSheet = showReadRecordSheet,
|
showReadRecordSheet = showReadRecordSheet,
|
||||||
readRecords = readRecords,
|
readRecords = readRecords,
|
||||||
sourceVariableSheet = sourceVariableSheet,
|
sourceVariableSheet = sourceVariableSheet,
|
||||||
onBackClick = onBackClick,
|
onBackClick = onBackClick,
|
||||||
|
onSearch = onSearch,
|
||||||
onLogin = {
|
onLogin = {
|
||||||
context.startActivity<SourceLoginActivity> {
|
context.startActivity<SourceLoginActivity> {
|
||||||
putExtra("type", "rssSource")
|
putExtra("type", "rssSource")
|
||||||
@@ -88,7 +97,7 @@ fun RssSortRouteScreen(
|
|||||||
onRefreshSort = {
|
onRefreshSort = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
viewModel.clearSortCache()
|
viewModel.clearSortCache()
|
||||||
sortList = viewModel.loadSorts()
|
sortList = viewModel.loadSorts(initialSortUrl, initialSearchKey)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onSetSourceVariable = {
|
onSetSourceVariable = {
|
||||||
@@ -157,7 +166,7 @@ fun RssSortRouteScreen(
|
|||||||
},
|
},
|
||||||
pagerContent = { _, sort, paddingValues ->
|
pagerContent = { _, sort, paddingValues ->
|
||||||
val pageViewModel: RssArticlesViewModel = koinViewModel(
|
val pageViewModel: RssArticlesViewModel = koinViewModel(
|
||||||
key = "rss_${viewModel.url}_${sort.first}_${sort.second}"
|
key = "rss_${viewModel.url}_${sort.first}_${sort.second}_${initialSearchKey.orEmpty()}"
|
||||||
)
|
)
|
||||||
RssArticlesPage(
|
RssArticlesPage(
|
||||||
sortName = sort.first,
|
sortName = sort.first,
|
||||||
@@ -166,6 +175,7 @@ fun RssSortRouteScreen(
|
|||||||
rssUrl = viewModel.url,
|
rssUrl = viewModel.url,
|
||||||
rssSource = viewModel.rssSource,
|
rssSource = viewModel.rssSource,
|
||||||
viewModel = pageViewModel,
|
viewModel = pageViewModel,
|
||||||
|
searchKey = initialSearchKey,
|
||||||
paddingValues = paddingValues,
|
paddingValues = paddingValues,
|
||||||
onRead = { article ->
|
onRead = { article ->
|
||||||
viewModel.read(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.AppScaffold
|
||||||
import io.legado.app.ui.widget.components.AppTextField
|
import io.legado.app.ui.widget.components.AppTextField
|
||||||
import io.legado.app.ui.widget.components.EmptyMessage
|
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.MediumPlainButton
|
||||||
import io.legado.app.ui.widget.components.button.series.SmallPlainButton
|
import io.legado.app.ui.widget.components.button.series.SmallPlainButton
|
||||||
import io.legado.app.ui.widget.components.button.series.SmallToggleButton
|
import io.legado.app.ui.widget.components.button.series.SmallToggleButton
|
||||||
@@ -85,12 +86,15 @@ fun RssSortScreen(
|
|||||||
title: String,
|
title: String,
|
||||||
sortList: List<Pair<String, String>>,
|
sortList: List<Pair<String, String>>,
|
||||||
preferredSortUrl: String?,
|
preferredSortUrl: String?,
|
||||||
|
searchKey: String?,
|
||||||
|
hasSearch: Boolean,
|
||||||
hasLogin: Boolean,
|
hasLogin: Boolean,
|
||||||
redirectPolicy: RedirectPolicy,
|
redirectPolicy: RedirectPolicy,
|
||||||
showReadRecordSheet: Boolean,
|
showReadRecordSheet: Boolean,
|
||||||
readRecords: List<RssReadRecord>,
|
readRecords: List<RssReadRecord>,
|
||||||
sourceVariableSheet: RssSourceVariableSheetState?,
|
sourceVariableSheet: RssSourceVariableSheetState?,
|
||||||
onBackClick: () -> Unit,
|
onBackClick: () -> Unit,
|
||||||
|
onSearch: (String) -> Unit,
|
||||||
onLogin: () -> Unit,
|
onLogin: () -> Unit,
|
||||||
onRefreshSort: () -> Unit,
|
onRefreshSort: () -> Unit,
|
||||||
onSetSourceVariable: () -> Unit,
|
onSetSourceVariable: () -> Unit,
|
||||||
@@ -120,15 +124,25 @@ fun RssSortScreen(
|
|||||||
val tabTitles = sortList.map { it.first }
|
val tabTitles = sortList.map { it.first }
|
||||||
|
|
||||||
var showMainMenu by remember { mutableStateOf(false) }
|
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 showRedirectMenu by remember { mutableStateOf(false) }
|
||||||
var showGroupMenu by remember { mutableStateOf(false) }
|
var showGroupMenu by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
BackHandler(enabled = showMainMenu || showRedirectMenu || showGroupMenu) {
|
BackHandler(enabled = showMainMenu || showSearchSheet || showRedirectMenu || showGroupMenu) {
|
||||||
showMainMenu = false
|
showMainMenu = false
|
||||||
|
showSearchSheet = false
|
||||||
showRedirectMenu = false
|
showRedirectMenu = false
|
||||||
showGroupMenu = false
|
showGroupMenu = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun submitSearch(query: String) {
|
||||||
|
val normalized = query.trim()
|
||||||
|
if (normalized.isEmpty()) return
|
||||||
|
showSearchSheet = false
|
||||||
|
onSearch(normalized)
|
||||||
|
}
|
||||||
|
|
||||||
LaunchedEffect(sortList.size) {
|
LaunchedEffect(sortList.size) {
|
||||||
if (sortList.isEmpty()) return@LaunchedEffect
|
if (sortList.isEmpty()) return@LaunchedEffect
|
||||||
val safeIndex = pagerState.currentPage.coerceIn(0, sortList.lastIndex)
|
val safeIndex = pagerState.currentPage.coerceIn(0, sortList.lastIndex)
|
||||||
@@ -147,6 +161,13 @@ fun RssSortScreen(
|
|||||||
TopBarNavigationButton(onClick = onBackClick, imageVector = AppIcons.Back)
|
TopBarNavigationButton(onClick = onBackClick, imageVector = AppIcons.Back)
|
||||||
},
|
},
|
||||||
actions = {
|
actions = {
|
||||||
|
if (hasSearch) {
|
||||||
|
TopBarActionButton(
|
||||||
|
onClick = { showSearchSheet = true },
|
||||||
|
imageVector = AppIcons.Search,
|
||||||
|
contentDescription = stringResource(R.string.search)
|
||||||
|
)
|
||||||
|
}
|
||||||
TopBarActionButton(
|
TopBarActionButton(
|
||||||
onClick = { showMainMenu = true },
|
onClick = { showMainMenu = true },
|
||||||
imageVector = AppIcons.MoreVert,
|
imageVector = AppIcons.MoreVert,
|
||||||
@@ -328,6 +349,44 @@ fun RssSortScreen(
|
|||||||
onDismissRequest = onDismissSourceVariable,
|
onDismissRequest = onDismissSourceVariable,
|
||||||
onSave = onSaveSourceVariable
|
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
|
@Composable
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ import io.legado.app.data.entities.RssReadRecord
|
|||||||
import io.legado.app.data.entities.RssSource
|
import io.legado.app.data.entities.RssSource
|
||||||
import io.legado.app.help.source.removeSortCache
|
import io.legado.app.help.source.removeSortCache
|
||||||
import io.legado.app.help.source.sortUrls
|
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 io.legado.app.utils.toastOnUi
|
||||||
import splitties.init.appCtx
|
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()
|
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 currentArticleStyle(): Int = rssSource?.articleStyle ?: 0
|
||||||
|
|
||||||
fun switchLayout() {
|
fun switchLayout() {
|
||||||
|
|||||||
Reference in New Issue
Block a user