From 5da2e85395cd6be3ada8ed8803427ed0607f576d Mon Sep 17 00:00:00 2001 From: aoi Date: Tue, 2 Jun 2026 18:32:04 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BF=9D=E7=95=99ExpandedSourceSheet?= =?UTF-8?q?=E7=9A=84=E6=B5=8F=E8=A7=88=E8=BF=9B=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 合并 PR #1279 by woruteyqi --- .../app/ui/book/search/SearchContract.kt | 4 ++ .../legado/app/ui/book/search/SearchScreen.kt | 29 +++++++++- .../app/ui/book/search/SearchViewModel.kt | 58 ++++++++++++------- 3 files changed, 69 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/book/search/SearchContract.kt b/app/src/main/java/io/legado/app/ui/book/search/SearchContract.kt index 434a4fee0..49d11ab0d 100644 --- a/app/src/main/java/io/legado/app/ui/book/search/SearchContract.kt +++ b/app/src/main/java/io/legado/app/ui/book/search/SearchContract.kt @@ -51,6 +51,9 @@ data class SearchUiState( val expandedSourceEnd: Boolean = false, val expandedSourceError: String? = null, val expandedSourcePage: Int = 1, + val showExpandedSource: Boolean = false, + val expandedSourceSavedScrollIndex: Int = 0, + val expandedSourceSavedScrollOffset: Int = 0, ) data class SearchEmptyScopeAction( @@ -90,6 +93,7 @@ sealed interface SearchIntent { data object DismissEmptyScopeAction : SearchIntent data object OpenSourceManage : SearchIntent data class SaveScrollState(val index: Int, val offset: Int) : SearchIntent + data class SaveExpandedSourceScrollState(val index: Int, val offset: Int) : SearchIntent } sealed interface SearchEffect { diff --git a/app/src/main/java/io/legado/app/ui/book/search/SearchScreen.kt b/app/src/main/java/io/legado/app/ui/book/search/SearchScreen.kt index e2e50addc..664c45515 100644 --- a/app/src/main/java/io/legado/app/ui/book/search/SearchScreen.kt +++ b/app/src/main/java/io/legado/app/ui/book/search/SearchScreen.kt @@ -698,14 +698,19 @@ fun SearchScreen( ) ExpandedSourceSheet( - show = state.expandedSourceUrl != null, + show = state.showExpandedSource, sourceName = state.expandedSourceName ?: "", books = state.expandedSourceBooks, isLoading = state.expandedSourceLoading, isEnd = state.expandedSourceEnd, errorMsg = state.expandedSourceError, + savedScrollIndex = state.expandedSourceSavedScrollIndex, + savedScrollOffset = state.expandedSourceSavedScrollOffset, onDismiss = { viewModel.onIntent(SearchIntent.DismissExpandedSource) }, onLoadMore = { viewModel.onIntent(SearchIntent.LoadMoreExpandedSource) }, + onSaveScrollState = { index, offset -> + viewModel.onIntent(SearchIntent.SaveExpandedSourceScrollState(index, offset)) + }, onBookClick = { book, coverKey -> viewModel.onIntent(SearchIntent.OpenExpandedSourceBook(book, coverKey)) }, @@ -888,8 +893,11 @@ private fun ExpandedSourceSheet( isLoading: Boolean, isEnd: Boolean, errorMsg: String?, + savedScrollIndex: Int, + savedScrollOffset: Int, onDismiss: () -> Unit, onLoadMore: () -> Unit, + onSaveScrollState: (Int, Int) -> Unit, onBookClick: (SearchBook, String?) -> Unit, onBookLongClick: ((SearchBook, String?) -> Unit)? = null, ) { @@ -900,7 +908,7 @@ private fun ExpandedSourceSheet( ) { val listState = rememberLazyListState() val showLoadMoreFooter = isLoading || errorMsg != null || isEnd - + val currentOnSaveScrollState by rememberUpdatedState(onSaveScrollState) val shouldLoadMore by remember { derivedStateOf { val total = listState.layoutInfo.totalItemsCount @@ -915,6 +923,23 @@ private fun ExpandedSourceSheet( } } + LaunchedEffect(savedScrollIndex, savedScrollOffset) { + if (savedScrollIndex > 0 || savedScrollOffset > 0) { + listState.scrollToItem(savedScrollIndex, savedScrollOffset) + onSaveScrollState(0, 0) + } + } + + DisposableEffect(listState) { + onDispose { + val first = listState.firstVisibleItemIndex + val offset = listState.firstVisibleItemScrollOffset + if (first > 0 || offset > 0) { + currentOnSaveScrollState(first, offset) + } + } + } + LazyColumn( state = listState, modifier = Modifier.fillMaxSize(), diff --git a/app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt b/app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt index ac1376ad8..028dc044e 100644 --- a/app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt @@ -225,6 +225,11 @@ class SearchViewModel( SearchIntent.OpenSourceManage -> emitEffect(SearchEffect.OpenSourceManage) is SearchIntent.ExpandSource -> { + val state = _uiState.value + if (state.expandedSourceUrl == intent.sourceUrl) { + _uiState.update { it.copy(showExpandedSource = true) } + return + } _uiState.update { it.copy( expandedSourceUrl = intent.sourceUrl, @@ -234,6 +239,9 @@ class SearchViewModel( expandedSourceEnd = false, expandedSourceError = null, expandedSourcePage = 1, + showExpandedSource = true, + expandedSourceSavedScrollIndex = 0, + expandedSourceSavedScrollOffset = 0, ) } loadExpandedSourcePage(intent.sourceUrl, page = 1) @@ -241,15 +249,7 @@ class SearchViewModel( SearchIntent.DismissExpandedSource -> { _uiState.update { - it.copy( - expandedSourceUrl = null, - expandedSourceName = null, - expandedSourceBooks = persistentListOf(), - expandedSourceLoading = false, - expandedSourceEnd = false, - expandedSourceError = null, - expandedSourcePage = 1, - ) + it.copy(showExpandedSource = false) } } @@ -267,17 +267,6 @@ class SearchViewModel( } is SearchIntent.OpenExpandedSourceBook -> { - _uiState.update { - it.copy( - expandedSourceUrl = null, - expandedSourceName = null, - expandedSourceBooks = persistentListOf(), - expandedSourceLoading = false, - expandedSourceEnd = false, - expandedSourceError = null, - expandedSourcePage = 1, - ) - } emitEffect( SearchEffect.OpenBookInfo( name = intent.book.name, @@ -298,6 +287,15 @@ class SearchViewModel( ) } } + + is SearchIntent.SaveExpandedSourceScrollState -> { + _uiState.update { + it.copy( + expandedSourceSavedScrollIndex = intent.index, + expandedSourceSavedScrollOffset = intent.offset, + ) + } + } } } @@ -426,6 +424,16 @@ class SearchViewModel( processedSources = 0, totalSources = 0, emptyScopeAction = null, + expandedSourceUrl = null, + expandedSourceName = null, + expandedSourceBooks = persistentListOf(), + expandedSourceLoading = false, + expandedSourceEnd = false, + expandedSourceError = null, + expandedSourcePage = 1, + showExpandedSource = false, + expandedSourceSavedScrollIndex = 0, + expandedSourceSavedScrollOffset = 0, ) } @@ -550,6 +558,16 @@ class SearchViewModel( hasMore = true, showSuggestions = true, emptyScopeAction = null, + expandedSourceUrl = null, + expandedSourceName = null, + expandedSourceBooks = persistentListOf(), + expandedSourceLoading = false, + expandedSourceEnd = false, + expandedSourceError = null, + expandedSourcePage = 1, + showExpandedSource = false, + expandedSourceSavedScrollIndex = 0, + expandedSourceSavedScrollOffset = 0, ) } }