HapeLee 5 месяцев назад
Родитель
Сommit
d350f7fcb3

+ 0 - 5
app/src/main/java/io/legado/app/data/repository/ExploreRepository.kt

@@ -19,7 +19,6 @@ interface ExploreRepository {
     fun getExploreGroups(): Flow<List<String>>
     fun getExploreSources(query: String, selectedGroup: String): Flow<List<BookSourcePart>>
     suspend fun getBookSource(sourceUrl: String): BookSource?
-    suspend fun saveSearchBooks(books: List<SearchBook>)
     suspend fun getSourceExploreKinds(sourceUrl: String): List<ExploreKind>
     suspend fun topSource(bookSource: BookSourcePart)
     suspend fun deleteSource(sourceUrl: String)
@@ -91,10 +90,6 @@ class ExploreRepositoryImpl(
         return@withContext source?.exploreKinds() ?: emptyList()
     }
 
-    override suspend fun saveSearchBooks(books: List<SearchBook>) {
-        appDb.searchBookDao.insert(*books.toTypedArray())
-    }
-
     override suspend fun topSource(bookSource: BookSourcePart) {
         val minOrder = appDb.bookSourceDao.minOrder
         appDb.bookSourceDao.upOrder(bookSource.copy(customOrder = minOrder - 1))

+ 7 - 7
app/src/main/java/io/legado/app/domain/usecase/AddToBookshelfUseCase.kt

@@ -1,20 +1,20 @@
 package io.legado.app.domain.usecase
 
 import io.legado.app.constant.BookType
-import io.legado.app.data.appDb
 import io.legado.app.data.entities.SearchBook
+import io.legado.app.data.repository.BookRepository
 import io.legado.app.help.book.removeType
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.withContext
 
-class AddToBookshelfUseCase {
+class AddToBookshelfUseCase(
+    private val bookRepository: BookRepository,
+) {
 
-    suspend fun execute(book: SearchBook) = withContext(Dispatchers.IO) {
+    suspend fun execute(book: SearchBook) {
         val b = book.toBook()
         b.removeType(BookType.notShelf)
         if (b.order == 0) {
-            b.order = appDb.bookDao.minOrder - 1
+            b.order = bookRepository.getMinOrder() - 1
         }
-        b.save()
+        bookRepository.insert(b)
     }
 }

+ 1 - 1
app/src/main/java/io/legado/app/ui/book/explore/ExploreShowContract.kt

@@ -36,7 +36,7 @@ sealed interface ExploreShowSheet {
 
 sealed interface ExploreShowIntent {
     data class InitData(
-        val sourceUrl: String?,
+        val sourceUrl: String,
         val exploreUrl: String?,
     ) : ExploreShowIntent
 

+ 57 - 33
app/src/main/java/io/legado/app/ui/book/explore/ExploreShowScreen.kt

@@ -135,30 +135,38 @@ fun ExploreShowScreen(
     val scrollBehavior = GlassTopAppBarDefaults.defaultScrollBehavior()
     val isGridMode = state.layoutState == 1
     val hazeState = remember { HazeState() }
-    val shouldLoadMoreList = remember {
+    val showLoadMoreFooter = !state.isRefreshing &&
+        (state.isLoading || state.errorMsg != null || state.isEnd)
+    val canLoadMore = state.books.isNotEmpty() &&
+        !state.isLoading &&
+        !state.isRefreshing &&
+        !state.isEnd &&
+        state.errorMsg == null
+    val shouldLoadMore by remember(isGridMode) {
         derivedStateOf {
-            val total = listState.layoutInfo.totalItemsCount
-            val last = listState.layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0
-            total > 0 && last >= total - 3
+            if (isGridMode) {
+                val total = gridState.layoutInfo.totalItemsCount
+                val last = gridState.layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0
+                total > 0 && last >= total - 1
+            } else {
+                val total = listState.layoutInfo.totalItemsCount
+                val last = listState.layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0
+                total > 0 && last >= total - 3
+            }
         }
     }
 
-    val shouldLoadMoreGrid = remember {
-        derivedStateOf {
-            val total = gridState.layoutInfo.totalItemsCount
-            val last = gridState.layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0
-            total > 0 && last >= total - 1
+    LaunchedEffect(
+        shouldLoadMore,
+        isGridMode,
+        canLoadMore,
+        state.books.size,
+    ) {
+        if (shouldLoadMore && canLoadMore) {
+            viewModel.onIntent(ExploreShowIntent.LoadMore)
         }
     }
 
-    LaunchedEffect(shouldLoadMoreList.value, isGridMode) {
-        if (!isGridMode && shouldLoadMoreList.value) viewModel.onIntent(ExploreShowIntent.LoadMore)
-    }
-
-    LaunchedEffect(shouldLoadMoreGrid.value, isGridMode) {
-        if (isGridMode && shouldLoadMoreGrid.value) viewModel.onIntent(ExploreShowIntent.LoadMore)
-    }
-
     LaunchedEffect(isGridMode) {
         if (isGridMode) {
             if (listState.firstVisibleItemIndex > 0) {
@@ -325,14 +333,14 @@ fun ExploreShowScreen(
                             )
                         }
 
-                        item(span = { GridItemSpan(maxLineSpan) }) {
-                            LoadMoreFooter(
-                                isLoading = state.isLoading && !state.isRefreshing,
-                                errorMsg = state.errorMsg,
-                                isEnd = state.isEnd,
-                                onRetry = { viewModel.onIntent(ExploreShowIntent.LoadMore) },
-                                onLoadMore = { viewModel.onIntent(ExploreShowIntent.ForceLoadNext) }
-                            )
+                        if (showLoadMoreFooter) {
+                            item(span = { GridItemSpan(maxLineSpan) }) {
+                                ExploreShowLoadMoreFooter(
+                                    state = state,
+                                    onRetry = { viewModel.onIntent(ExploreShowIntent.LoadMore) },
+                                    onLoadMore = { viewModel.onIntent(ExploreShowIntent.ForceLoadNext) },
+                                )
+                            }
                         }
                     }
                 } else {
@@ -376,14 +384,14 @@ fun ExploreShowScreen(
                             )
                         }
 
-                        item {
-                            LoadMoreFooter(
-                                isLoading = state.isLoading && !state.isRefreshing,
-                                errorMsg = state.errorMsg,
-                                isEnd = state.isEnd,
-                                onRetry = { viewModel.onIntent(ExploreShowIntent.LoadMore) },
-                                onLoadMore = { viewModel.onIntent(ExploreShowIntent.ForceLoadNext) }
-                            )
+                        if (showLoadMoreFooter) {
+                            item {
+                                ExploreShowLoadMoreFooter(
+                                    state = state,
+                                    onRetry = { viewModel.onIntent(ExploreShowIntent.LoadMore) },
+                                    onLoadMore = { viewModel.onIntent(ExploreShowIntent.ForceLoadNext) },
+                                )
+                            }
                         }
                     }
                 }
@@ -410,6 +418,22 @@ fun ExploreShowScreen(
     )
 }
 
+@Composable
+private fun ExploreShowLoadMoreFooter(
+    state: ExploreShowUiState,
+    onRetry: () -> Unit,
+    onLoadMore: () -> Unit,
+) {
+    LoadMoreFooter(
+        isLoading = state.isLoading,
+        errorMsg = state.errorMsg,
+        isEnd = state.isEnd,
+        onRetry = onRetry,
+        onLoadMore = onLoadMore,
+        autoLoad = false,
+    )
+}
+
 @OptIn(ExperimentalSharedTransitionApi::class)
 @Composable
 fun ExploreBookItem(

+ 126 - 116
app/src/main/java/io/legado/app/ui/book/explore/ExploreShowViewModel.kt

@@ -2,7 +2,6 @@ package io.legado.app.ui.book.explore
 
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.viewModelScope
-import io.legado.app.data.entities.BookSource
 import io.legado.app.data.entities.SearchBook
 import io.legado.app.data.entities.rule.ExploreKind
 import io.legado.app.data.repository.ExploreRepository
@@ -25,6 +24,25 @@ import kotlinx.coroutines.delay
 import kotlinx.coroutines.launch
 import splitties.init.appCtx
 
+private data class ExploreShowLoadState(
+    val isLoading: Boolean = false,
+    val isRefreshing: Boolean = false,
+    val isEnd: Boolean = false,
+    val errorMsg: String? = null,
+)
+
+private data class ExploreShowKindState(
+    val kinds: List<ExploreKind> = emptyList(),
+    val selectedKindTitle: String? = null,
+)
+
+private data class ExploreShowDisplayState(
+    val sourceUrl: String? = null,
+    val layoutState: Int,
+    val gridCount: Int,
+    val sheet: ExploreShowSheet = ExploreShowSheet.None,
+)
+
 class ExploreShowViewModel(
     private val repository: ExploreRepository,
     private val resolveBookShelfStateUseCase: ResolveBookShelfStateUseCase,
@@ -35,15 +53,18 @@ class ExploreShowViewModel(
 
     private val _rawBooks = MutableStateFlow<List<SearchBook>>(emptyList())
     private val _bookshelf = MutableStateFlow<Set<BookShelfKey>>(emptySet())
-    private val _isLoading = MutableStateFlow(false)
-    private val _isEnd = MutableStateFlow(false)
-    private val _errorMsg = MutableStateFlow<String?>(null)
-    private val _kinds = MutableStateFlow<List<ExploreKind>>(emptyList())
-    private val _selectedKindTitle = MutableStateFlow<String?>(null)
+    private val _loadState = MutableStateFlow(ExploreShowLoadState())
+    private val _kindState = MutableStateFlow(ExploreShowKindState())
+    private val _displayState = MutableStateFlow(
+        ExploreShowDisplayState(
+            layoutState = AppConfig.exploreLayoutState,
+            gridCount = appCtx.exploreLayoutGrid,
+        )
+    )
 
-    private var bookSource: BookSource? = null
     private var sourceUrl: String? = null
     private var exploreUrl: String? = null
+    private var initialized = false
     private var page = 1
     private var autoPageCount = 0
 
@@ -54,8 +75,8 @@ class ExploreShowViewModel(
 
     private val _uiState = MutableStateFlow(
         ExploreShowUiState(
-            layoutState = AppConfig.exploreLayoutState,
-            gridCount = appCtx.exploreLayoutGrid,
+            layoutState = _displayState.value.layoutState,
+            gridCount = _displayState.value.gridCount,
         )
     )
     val uiState = _uiState.asStateFlow()
@@ -77,8 +98,8 @@ class ExploreShowViewModel(
             is ExploreShowIntent.SwitchKind -> switchKind(intent.kind)
             ExploreShowIntent.ToggleLayout -> toggleLayout()
             is ExploreShowIntent.SaveGridCount -> saveGridCount(intent.count)
-            is ExploreShowIntent.ShowSheet -> _uiState.update { it.copy(sheet = intent.sheet) }
-            ExploreShowIntent.DismissSheet -> _uiState.update { it.copy(sheet = ExploreShowSheet.None) }
+            is ExploreShowIntent.ShowSheet -> _displayState.update { it.copy(sheet = intent.sheet) }
+            ExploreShowIntent.DismissSheet -> _displayState.update { it.copy(sheet = ExploreShowSheet.None) }
             is ExploreShowIntent.OpenBook -> emitEffect(
                 ExploreShowEffect.OpenBookInfo(
                     name = intent.book.name,
@@ -111,21 +132,10 @@ class ExploreShowViewModel(
             combine(
                 _rawBooks,
                 _bookshelf,
-                _isLoading,
-                _isEnd,
-                _errorMsg,
-                _kinds,
-                _selectedKindTitle,
-            ) { values ->
-                @Suppress("UNCHECKED_CAST")
-                val rawBooks = values[0] as List<SearchBook>
-                val bookshelf = values[1] as Set<BookShelfKey>
-                val isLoading = values[2] as Boolean
-                val isEnd = values[3] as Boolean
-                val errorMsg = values[4] as String?
-                val kinds = values[5] as List<ExploreKind>
-                val selectedKindTitle = values[6] as String?
-
+                _loadState,
+                _kindState,
+                _displayState,
+            ) { rawBooks, bookshelf, loadState, kindState, displayState ->
                 val books = rawBooks.map { item ->
                     ExploreBookItemUi(
                         book = item,
@@ -139,154 +149,154 @@ class ExploreShowViewModel(
                 }
 
                 ExploreShowUiState(
-                    sourceUrl = sourceUrl,
+                    sourceUrl = displayState.sourceUrl,
                     books = books.toImmutableList(),
-                    kinds = kinds.toImmutableList(),
-                    selectedKindTitle = selectedKindTitle,
-                    layoutState = _uiState.value.layoutState,
-                    gridCount = _uiState.value.gridCount,
-                    isLoading = isLoading,
-                    isRefreshing = isLoading && page == 1,
-                    isEnd = isEnd,
-                    errorMsg = errorMsg,
-                    sheet = _uiState.value.sheet,
+                    kinds = kindState.kinds.toImmutableList(),
+                    selectedKindTitle = kindState.selectedKindTitle,
+                    layoutState = displayState.layoutState,
+                    gridCount = displayState.gridCount,
+                    isLoading = loadState.isLoading,
+                    isRefreshing = loadState.isRefreshing,
+                    isEnd = loadState.isEnd,
+                    errorMsg = loadState.errorMsg,
+                    sheet = displayState.sheet,
                 )
             }.collect { newState ->
-                val oldState = _uiState.value
                 _uiState.value = newState
-
-                if (newState.books.isEmpty() && _rawBooks.value.isNotEmpty() && !newState.isEnd && !newState.isLoading) {
-                    loadMore()
-                }
             }
         }
     }
 
-    private fun initData(incomingSourceUrl: String?, incomingExploreUrl: String?) {
-        if (sourceUrl == incomingSourceUrl && exploreUrl == incomingExploreUrl && bookSource != null) {
+    private fun initData(incomingSourceUrl: String, incomingExploreUrl: String?) {
+        if (initialized && sourceUrl == incomingSourceUrl && exploreUrl == incomingExploreUrl) {
             return
         }
+        initialized = true
         sourceUrl = incomingSourceUrl
         exploreUrl = incomingExploreUrl
         page = 1
         autoPageCount = 0
-        bookSource = null
         _rawBooks.value = emptyList()
-        _isEnd.value = false
-        _errorMsg.value = null
-        _selectedKindTitle.value = null
-        _kinds.value = emptyList()
-
-        viewModelScope.launch {
-            if (bookSource == null && incomingSourceUrl != null) {
-                bookSource = repository.getBookSource(incomingSourceUrl)
-            }
+        _loadState.value = ExploreShowLoadState()
+        _kindState.value = ExploreShowKindState()
+        _displayState.update {
+            it.copy(
+                sourceUrl = incomingSourceUrl,
+                sheet = ExploreShowSheet.None,
+            )
+        }
 
-            if (exploreUrl == null && bookSource != null) {
-                loadKinds(incomingSourceUrl!!)
+        if (incomingExploreUrl == null) {
+            viewModelScope.launch {
+                loadKinds(incomingSourceUrl)
             }
-
-            loadMore(isRefresh = true)
         }
+
+        loadMore(isRefresh = true)
     }
 
-    private fun loadKinds(sourceUrl: String) {
-        viewModelScope.launch {
-            _kinds.value = repository.getSourceExploreKinds(sourceUrl)
-        }
+    private suspend fun loadKinds(sourceUrl: String) {
+        _kindState.update { it.copy(kinds = repository.getSourceExploreKinds(sourceUrl)) }
     }
 
     private fun switchKind(kind: ExploreKind) {
-        _selectedKindTitle.value = kind.title
+        _kindState.update { it.copy(selectedKindTitle = kind.title) }
         exploreUrl = kind.url
-        _isEnd.value = false
+        _loadState.update { it.copy(isEnd = false) }
         autoPageCount = 0
         loadMore(isRefresh = true)
     }
 
     private fun toggleLayout() {
-        _uiState.update {
-            val newState = if (it.layoutState == 0) 1 else 0
-            AppConfig.exploreLayoutState = newState
-            it.copy(layoutState = newState)
+        _displayState.update {
+            val layoutState = if (it.layoutState == 0) 1 else 0
+            AppConfig.exploreLayoutState = layoutState
+            it.copy(layoutState = layoutState)
         }
     }
 
     private fun saveGridCount(count: Int) {
         appCtx.exploreLayoutGrid = count
-        _uiState.update { it.copy(gridCount = count) }
+        _displayState.update { it.copy(gridCount = count) }
     }
 
     private fun loadMore(isRefresh: Boolean = false, forceLoad: Boolean = false) {
-        val source = bookSource
-        val url = exploreUrl ?: source?.exploreUrl
-        if (source == null || url == null || _isLoading.value || (_isEnd.value && !isRefresh && !forceLoad)) return
+        val source = sourceUrl
+        val url = exploreUrl
+        val loadState = _loadState.value
+        if (source == null || loadState.isLoading || (loadState.isEnd && !isRefresh && !forceLoad)) return
+
+        _loadState.update {
+            it.copy(
+                isLoading = true,
+                isRefreshing = isRefresh,
+                isEnd = if (isRefresh || forceLoad) false else it.isEnd,
+                errorMsg = null,
+            )
+        }
 
         viewModelScope.launch {
-            _isLoading.value = true
-            _errorMsg.value = null
-
             if (isRefresh) {
                 page = 1
-                _isEnd.value = false
                 autoPageCount = 0
                 _rawBooks.value = emptyList()
             }
 
             if (forceLoad) {
                 autoPageCount = 0
-                _isEnd.value = false
             }
 
             fetchPage(source, url)
         }
     }
 
-    private suspend fun fetchPage(source: BookSource, url: String) {
+    private suspend fun fetchPage(sourceUrl: String, url: String?) {
         kotlin.runCatching {
-            exploreBooksUseCase.execute(source.bookSourceUrl, url, args = null, page)
+            exploreBooksUseCase.execute(sourceUrl, url, args = null, page)
         }.onSuccess { result ->
-            if (result.books.isEmpty()) {
-                page++
-                autoPageCount++
-                if (autoPageCount >= MAX_AUTO_PAGES) {
-                    _isEnd.value = true
-                    _isLoading.value = false
-                } else {
-                    delay(AUTO_PAGE_DELAY_MS)
-                    fetchPage(source, url)
-                }
-            } else {
+            val currentList = _rawBooks.value
+            val existingUrls = currentList.map { it.bookUrl }.toSet()
+            val uniqueNewBooks = result.books
+                .filter { it.bookUrl !in existingUrls }
+                .distinctBy { it.bookUrl }
+
+            if (result.books.isNotEmpty()) {
                 saveSearchBooksUseCase.save(result.books)
+            }
 
-                val currentList = _rawBooks.value
-                val existingUrls = currentList.map { it.bookUrl }.toSet()
-
-                val uniqueNewBooks = result.books
-                    .filter { it.bookUrl !in existingUrls }
-                    .distinctBy { it.bookUrl }
-
-                if (uniqueNewBooks.isEmpty()) {
-                    page++
-                    autoPageCount++
-                    if (autoPageCount >= MAX_AUTO_PAGES) {
-                        _isEnd.value = true
-                        _isLoading.value = false
-                    } else {
-                        delay(AUTO_PAGE_DELAY_MS)
-                        fetchPage(source, url)
-                    }
-                } else {
-                    _rawBooks.value = currentList + uniqueNewBooks
-                    page++
-                    autoPageCount = 0
-                    _isEnd.value = false
-                    _isLoading.value = false
-                }
+            if (uniqueNewBooks.isEmpty()) {
+                fetchNextAutoPageOrFinish(sourceUrl, url)
+            } else {
+                _rawBooks.value = currentList + uniqueNewBooks
+                page++
+                autoPageCount = 0
+                _loadState.update { it.copy(isEnd = false) }
+                finishLoading()
             }
-        }.onFailure {
-            _errorMsg.value = it.stackTraceStr
-            _isLoading.value = false
+        }.onFailure { throwable ->
+            _loadState.update { it.copy(errorMsg = throwable.stackTraceStr) }
+            finishLoading()
+        }
+    }
+
+    private suspend fun fetchNextAutoPageOrFinish(sourceUrl: String, url: String?) {
+        page++
+        autoPageCount++
+        if (autoPageCount >= MAX_AUTO_PAGES) {
+            _loadState.update { it.copy(isEnd = true) }
+            finishLoading()
+        } else {
+            delay(AUTO_PAGE_DELAY_MS)
+            fetchPage(sourceUrl, url)
+        }
+    }
+
+    private fun finishLoading() {
+        _loadState.update {
+            it.copy(
+                isLoading = false,
+                isRefreshing = false,
+            )
         }
     }
 

+ 11 - 7
app/src/main/java/io/legado/app/ui/book/search/SearchScreen.kt

@@ -899,6 +899,7 @@ private fun ExpandedSourceSheet(
         title = sourceName,
     ) {
         val listState = rememberLazyListState()
+        val showLoadMoreFooter = isLoading || errorMsg != null || isEnd
 
         val shouldLoadMore by remember {
             derivedStateOf {
@@ -935,13 +936,16 @@ private fun ExpandedSourceSheet(
                 )
             }
 
-            item {
-                LoadMoreFooter(
-                    isLoading = isLoading,
-                    errorMsg = errorMsg,
-                    isEnd = isEnd,
-                    onRetry = onLoadMore,
-                )
+            if (showLoadMoreFooter) {
+                item {
+                    LoadMoreFooter(
+                        isLoading = isLoading,
+                        errorMsg = errorMsg,
+                        isEnd = isEnd,
+                        onRetry = onLoadMore,
+                        autoLoad = false,
+                    )
+                }
             }
         }
     }

+ 32 - 21
app/src/main/java/io/legado/app/ui/rss/article/RssArticlesCompose.kt

@@ -135,6 +135,8 @@ fun RssArticlesPage(
         modifier = modifier.fillMaxSize(),
         topPadding = paddingValues.calculateTopPadding()
     ) {
+        val showLoadMoreFooter = !loadState.isRefreshing &&
+            (loadState.isLoadingMore || loadState.errorMessage != null || !loadState.hasMore)
         when (layout) {
             RssArticleLayout.List, RssArticleLayout.LargeCard -> {
                 val listState = rememberLazyListState()
@@ -160,13 +162,16 @@ fun RssArticlesPage(
                             onClick = onRead
                         )
                     }
-                    item {
-                        LoadMoreFooter(
-                            isLoading = loadState.isRefreshing || loadState.isLoadingMore,
-                            errorMsg = loadState.errorMessage,
-                            isEnd = !loadState.hasMore,
-                            onRetry = { rssSource?.let(viewModel::loadMore) }
-                        )
+                    if (showLoadMoreFooter) {
+                        item {
+                            LoadMoreFooter(
+                                isLoading = loadState.isLoadingMore,
+                                errorMsg = loadState.errorMessage,
+                                isEnd = !loadState.hasMore,
+                                onRetry = { rssSource?.let(viewModel::loadMore) },
+                                autoLoad = false
+                            )
+                        }
                     }
                 }
             }
@@ -197,13 +202,16 @@ fun RssArticlesPage(
                             onClick = onRead
                         )
                     }
-                    item(span = { GridItemSpan(maxLineSpan) }) {
-                        LoadMoreFooter(
-                            isLoading = loadState.isRefreshing || loadState.isLoadingMore,
-                            errorMsg = loadState.errorMessage,
-                            isEnd = !loadState.hasMore,
-                            onRetry = { rssSource?.let(viewModel::loadMore) }
-                        )
+                    if (showLoadMoreFooter) {
+                        item(span = { GridItemSpan(maxLineSpan) }) {
+                            LoadMoreFooter(
+                                isLoading = loadState.isLoadingMore,
+                                errorMsg = loadState.errorMessage,
+                                isEnd = !loadState.hasMore,
+                                onRetry = { rssSource?.let(viewModel::loadMore) },
+                                autoLoad = false
+                            )
+                        }
                     }
                 }
             }
@@ -234,13 +242,16 @@ fun RssArticlesPage(
                             onClick = onRead
                         )
                     }
-                    item(span = StaggeredGridItemSpan.FullLine) {
-                        LoadMoreFooter(
-                            isLoading = loadState.isRefreshing || loadState.isLoadingMore,
-                            errorMsg = loadState.errorMessage,
-                            isEnd = !loadState.hasMore,
-                            onRetry = { rssSource?.let(viewModel::loadMore) }
-                        )
+                    if (showLoadMoreFooter) {
+                        item(span = StaggeredGridItemSpan.FullLine) {
+                            LoadMoreFooter(
+                                isLoading = loadState.isLoadingMore,
+                                errorMsg = loadState.errorMessage,
+                                isEnd = !loadState.hasMore,
+                                onRetry = { rssSource?.let(viewModel::loadMore) },
+                                autoLoad = false
+                            )
+                        }
                     }
                 }
             }

+ 41 - 32
app/src/main/java/io/legado/app/ui/widget/components/LoadMoreFooter.kt

@@ -44,16 +44,23 @@ fun LoadMoreFooter(
     isEnd: Boolean,
     onRetry: () -> Unit,
     onLoadMore: (() -> Unit)? = null,
+    autoLoad: Boolean = true,
 ) {
     val context = LocalContext.current
     var showFullError by remember { mutableStateOf<String?>(null) }
 
-    LaunchedEffect(isLoading, errorMsg, isEnd) {
-        if (!isLoading && errorMsg == null && !isEnd) {
-            onRetry()
+    if (autoLoad) {
+        LaunchedEffect(isLoading, errorMsg, isEnd) {
+            if (!isLoading && errorMsg == null && !isEnd) {
+                onRetry()
+            }
         }
     }
 
+    if (autoLoad && !isLoading && errorMsg == null && !isEnd) {
+        return
+    }
+
     AppAlertDialog(
         data = showFullError,
         onDismissRequest = { showFullError = null },
@@ -270,39 +277,41 @@ fun LoadMoreFooter(
                 }
 
                 else -> {
-                    GlassCard(
-                        modifier = Modifier
-                            .fillMaxWidth(),
-                        containerColor = LegadoTheme.colorScheme.surfaceContainer,
-                        onClick = onRetry
-                    ) {
-                        Column(
-                            modifier = Modifier.fillMaxWidth()
+                    if (!autoLoad) {
+                        GlassCard(
+                            modifier = Modifier
+                                .fillMaxWidth(),
+                            containerColor = LegadoTheme.colorScheme.surfaceContainer,
+                            onClick = onRetry
                         ) {
-                            Row(
-                                modifier = Modifier
-                                    .fillMaxWidth()
-                                    .padding(
-                                        all = 16.dp
-                                    ),
-                                verticalAlignment = Alignment.CenterVertically,
-                                horizontalArrangement = Arrangement.spacedBy(12.dp)
+                            Column(
+                                modifier = Modifier.fillMaxWidth()
                             ) {
+                                Row(
+                                    modifier = Modifier
+                                        .fillMaxWidth()
+                                        .padding(
+                                            all = 16.dp
+                                        ),
+                                    verticalAlignment = Alignment.CenterVertically,
+                                    horizontalArrangement = Arrangement.spacedBy(12.dp)
+                                ) {
 
-                                AppIcon(
-                                    imageVector = Icons.Outlined.Info,
-                                    contentDescription = null,
-                                    tint = LegadoTheme.colorScheme.onSurface
-                                )
+                                    AppIcon(
+                                        imageVector = Icons.Outlined.Info,
+                                        contentDescription = null,
+                                        tint = LegadoTheme.colorScheme.onSurface
+                                    )
 
-                                AppText(
-                                    text = "加载更多",
-                                    color = LegadoTheme.colorScheme.onSurface,
-                                    style = LegadoTheme.typography.bodySmall,
-                                    modifier = Modifier.weight(1f),
-                                    maxLines = 2,
-                                    overflow = TextOverflow.Ellipsis,
-                                )
+                                    AppText(
+                                        text = "加载更多",
+                                        color = LegadoTheme.colorScheme.onSurface,
+                                        style = LegadoTheme.typography.bodySmall,
+                                        modifier = Modifier.weight(1f),
+                                        maxLines = 2,
+                                        overflow = TextOverflow.Ellipsis,
+                                    )
+                                }
                             }
                         }
                     }