优化发现刷新逻辑
This commit is contained in:
@@ -41,6 +41,7 @@ sealed interface ExploreShowIntent {
|
||||
) : ExploreShowIntent
|
||||
|
||||
data object LoadMore : ExploreShowIntent
|
||||
data object ForceLoadNext : ExploreShowIntent
|
||||
data object Refresh : ExploreShowIntent
|
||||
data class SwitchKind(val kind: ExploreKind) : ExploreShowIntent
|
||||
data object ToggleLayout : ExploreShowIntent
|
||||
|
||||
@@ -330,7 +330,8 @@ fun ExploreShowScreen(
|
||||
isLoading = state.isLoading && !state.isRefreshing,
|
||||
errorMsg = state.errorMsg,
|
||||
isEnd = state.isEnd,
|
||||
onRetry = { viewModel.onIntent(ExploreShowIntent.LoadMore) }
|
||||
onRetry = { viewModel.onIntent(ExploreShowIntent.LoadMore) },
|
||||
onLoadMore = { viewModel.onIntent(ExploreShowIntent.ForceLoadNext) }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -380,7 +381,8 @@ fun ExploreShowScreen(
|
||||
isLoading = state.isLoading && !state.isRefreshing,
|
||||
errorMsg = state.errorMsg,
|
||||
isEnd = state.isEnd,
|
||||
onRetry = { viewModel.onIntent(ExploreShowIntent.LoadMore) }
|
||||
onRetry = { viewModel.onIntent(ExploreShowIntent.LoadMore) },
|
||||
onLoadMore = { viewModel.onIntent(ExploreShowIntent.ForceLoadNext) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import kotlinx.coroutines.flow.asSharedFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import splitties.init.appCtx
|
||||
|
||||
@@ -44,6 +45,12 @@ class ExploreShowViewModel(
|
||||
private var sourceUrl: String? = null
|
||||
private var exploreUrl: String? = null
|
||||
private var page = 1
|
||||
private var autoPageCount = 0
|
||||
|
||||
companion object {
|
||||
private const val MAX_AUTO_PAGES = 3
|
||||
private const val AUTO_PAGE_DELAY_MS = 500L
|
||||
}
|
||||
|
||||
private val _uiState = MutableStateFlow(
|
||||
ExploreShowUiState(
|
||||
@@ -65,6 +72,7 @@ class ExploreShowViewModel(
|
||||
when (intent) {
|
||||
is ExploreShowIntent.InitData -> initData(intent.sourceUrl, intent.exploreUrl)
|
||||
ExploreShowIntent.LoadMore -> loadMore()
|
||||
ExploreShowIntent.ForceLoadNext -> loadMore(forceLoad = true)
|
||||
ExploreShowIntent.Refresh -> loadMore(isRefresh = true)
|
||||
is ExploreShowIntent.SwitchKind -> switchKind(intent.kind)
|
||||
ExploreShowIntent.ToggleLayout -> toggleLayout()
|
||||
@@ -161,6 +169,7 @@ class ExploreShowViewModel(
|
||||
sourceUrl = incomingSourceUrl
|
||||
exploreUrl = incomingExploreUrl
|
||||
page = 1
|
||||
autoPageCount = 0
|
||||
bookSource = null
|
||||
_rawBooks.value = emptyList()
|
||||
_isEnd.value = false
|
||||
@@ -191,6 +200,7 @@ class ExploreShowViewModel(
|
||||
_selectedKindTitle.value = kind.title
|
||||
exploreUrl = kind.url
|
||||
_isEnd.value = false
|
||||
autoPageCount = 0
|
||||
loadMore(isRefresh = true)
|
||||
}
|
||||
|
||||
@@ -207,10 +217,10 @@ class ExploreShowViewModel(
|
||||
_uiState.update { it.copy(gridCount = count) }
|
||||
}
|
||||
|
||||
private fun loadMore(isRefresh: Boolean = false) {
|
||||
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)) return
|
||||
if (source == null || url == null || _isLoading.value || (_isEnd.value && !isRefresh && !forceLoad)) return
|
||||
|
||||
viewModelScope.launch {
|
||||
_isLoading.value = true
|
||||
@@ -219,36 +229,63 @@ class ExploreShowViewModel(
|
||||
if (isRefresh) {
|
||||
page = 1
|
||||
_isEnd.value = false
|
||||
autoPageCount = 0
|
||||
_rawBooks.value = emptyList()
|
||||
}
|
||||
|
||||
kotlin.runCatching {
|
||||
exploreBooksUseCase.execute(source.bookSourceUrl, url, args = null, page)
|
||||
}.onSuccess { result ->
|
||||
if (result.books.isEmpty()) {
|
||||
_isEnd.value = true
|
||||
} else {
|
||||
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()) {
|
||||
_isEnd.value = true
|
||||
} else {
|
||||
_rawBooks.value = currentList + uniqueNewBooks
|
||||
page++
|
||||
_isEnd.value = false
|
||||
}
|
||||
}
|
||||
}.onFailure {
|
||||
_errorMsg.value = it.stackTraceStr
|
||||
if (forceLoad) {
|
||||
autoPageCount = 0
|
||||
_isEnd.value = false
|
||||
}
|
||||
|
||||
fetchPage(source, url)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun fetchPage(source: BookSource, url: String) {
|
||||
kotlin.runCatching {
|
||||
exploreBooksUseCase.execute(source.bookSourceUrl, 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 {
|
||||
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
|
||||
}
|
||||
}
|
||||
}.onFailure {
|
||||
_errorMsg.value = it.stackTraceStr
|
||||
_isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,8 @@ fun LoadMoreFooter(
|
||||
isLoading: Boolean,
|
||||
errorMsg: String?,
|
||||
isEnd: Boolean,
|
||||
onRetry: () -> Unit
|
||||
onRetry: () -> Unit,
|
||||
onLoadMore: (() -> Unit)? = null,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
var showFullError by remember { mutableStateOf<String?>(null) }
|
||||
@@ -179,38 +180,90 @@ fun LoadMoreFooter(
|
||||
}
|
||||
|
||||
end -> {
|
||||
GlassCard(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
containerColor = LegadoTheme.colorScheme.surfaceContainer,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
if (onLoadMore != null) {
|
||||
GlassCard(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
containerColor = LegadoTheme.colorScheme.surfaceContainer,
|
||||
) {
|
||||
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
|
||||
)
|
||||
AppText(
|
||||
text = "已经到底了~",
|
||||
color = LegadoTheme.colorScheme.onSurface,
|
||||
style = LegadoTheme.typography.bodySmall,
|
||||
modifier = Modifier.weight(1f),
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
|
||||
AppIcon(
|
||||
imageVector = Icons.Outlined.Info,
|
||||
contentDescription = null,
|
||||
tint = LegadoTheme.colorScheme.onSurface
|
||||
HorizontalDivider(
|
||||
color = LegadoTheme.colorScheme.outlineVariant.copy(alpha = 0.3f)
|
||||
)
|
||||
|
||||
AppText(
|
||||
text = "已经到底了~",
|
||||
color = LegadoTheme.colorScheme.onSurface,
|
||||
style = LegadoTheme.typography.bodySmall,
|
||||
modifier = Modifier.weight(1f),
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable(onClick = onLoadMore)
|
||||
.padding(vertical = 10.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
AppIcon(
|
||||
imageVector = Icons.Default.Refresh,
|
||||
contentDescription = null,
|
||||
tint = LegadoTheme.colorScheme.primary
|
||||
)
|
||||
AppText(
|
||||
text = "尝试加载下一页",
|
||||
color = LegadoTheme.colorScheme.primary,
|
||||
style = LegadoTheme.typography.labelMedium
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
GlassCard(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
containerColor = LegadoTheme.colorScheme.surfaceContainer,
|
||||
) {
|
||||
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
|
||||
)
|
||||
AppText(
|
||||
text = "已经到底了~",
|
||||
color = LegadoTheme.colorScheme.onSurface,
|
||||
style = LegadoTheme.typography.bodySmall,
|
||||
modifier = Modifier.weight(1f),
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user