feat(ui): lazy tag for mark article as read
This commit is contained in:
parent
d4541dd026
commit
1343978fc9
@ -90,6 +90,7 @@ private const val TAG = "ArticleItem"
|
||||
fun ArticleItem(
|
||||
modifier: Modifier = Modifier,
|
||||
articleWithFeed: ArticleWithFeed,
|
||||
isUnread: Boolean = articleWithFeed.article.isUnread,
|
||||
onClick: (ArticleWithFeed) -> Unit = {},
|
||||
onLongClick: (() -> Unit)? = null
|
||||
) {
|
||||
@ -105,7 +106,7 @@ fun ArticleItem(
|
||||
dateString = article.dateString,
|
||||
imgData = article.img,
|
||||
isStarred = article.isStarred,
|
||||
isUnread = article.isUnread,
|
||||
isUnread = isUnread,
|
||||
onClick = { onClick(articleWithFeed) },
|
||||
onLongClick = onLongClick
|
||||
)
|
||||
@ -280,7 +281,7 @@ private const val SwipeActionDelay = 300L
|
||||
@Composable
|
||||
fun SwipeableArticleItem(
|
||||
articleWithFeed: ArticleWithFeed,
|
||||
isFilterUnread: Boolean = false,
|
||||
isUnread: Boolean = articleWithFeed.article.isUnread,
|
||||
articleListTonalElevation: Int = 0,
|
||||
onClick: (ArticleWithFeed) -> Unit = {},
|
||||
isSwipeEnabled: () -> Boolean = { false },
|
||||
@ -311,7 +312,7 @@ fun SwipeableArticleItem(
|
||||
|
||||
SwipeActionBox(
|
||||
articleWithFeed = articleWithFeed,
|
||||
isRead = !articleWithFeed.article.isUnread,
|
||||
isRead = !isUnread,
|
||||
isStarred = articleWithFeed.article.isStarred,
|
||||
onToggleStarred = onToggleStarred,
|
||||
onToggleRead = onToggleRead
|
||||
@ -337,6 +338,7 @@ fun SwipeableArticleItem(
|
||||
) {
|
||||
ArticleItem(
|
||||
articleWithFeed = articleWithFeed,
|
||||
isUnread = isUnread,
|
||||
onClick = onClick,
|
||||
onLongClick = onLongClick
|
||||
)
|
||||
|
@ -16,7 +16,7 @@ import me.ash.reader.domain.model.article.ArticleWithFeed
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
fun LazyListScope.ArticleList(
|
||||
pagingItems: LazyPagingItems<ArticleFlowItem>,
|
||||
isFilterUnread: Boolean,
|
||||
diffMap: Map<String, Diff>,
|
||||
isShowFeedIcon: Boolean,
|
||||
isShowStickyHeader: Boolean,
|
||||
articleListTonalElevation: Int,
|
||||
@ -40,9 +40,10 @@ fun LazyListScope.ArticleList(
|
||||
) { index ->
|
||||
when (val item = pagingItems[index]) {
|
||||
is ArticleFlowItem.Article -> {
|
||||
val article = item.articleWithFeed.article
|
||||
SwipeableArticleItem(
|
||||
articleWithFeed = item.articleWithFeed,
|
||||
isFilterUnread = isFilterUnread,
|
||||
isUnread = diffMap[article.id]?.isUnread ?: article.isUnread,
|
||||
articleListTonalElevation = articleListTonalElevation,
|
||||
onClick = onClick,
|
||||
isSwipeEnabled = isSwipeEnabled,
|
||||
@ -70,9 +71,10 @@ fun LazyListScope.ArticleList(
|
||||
when (val item = pagingItems.peek(index)) {
|
||||
is ArticleFlowItem.Article -> {
|
||||
item(key = key(item), contentType = contentType(item)) {
|
||||
val article = item.articleWithFeed.article
|
||||
SwipeableArticleItem(
|
||||
articleWithFeed = item.articleWithFeed,
|
||||
isFilterUnread = isFilterUnread,
|
||||
isUnread = diffMap[article.id]?.isUnread ?: article.isUnread,
|
||||
articleListTonalElevation = articleListTonalElevation,
|
||||
onClick = onClick,
|
||||
isSwipeEnabled = isSwipeEnabled,
|
||||
|
@ -32,6 +32,7 @@ import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalLifecycleOwner
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
@ -43,7 +44,6 @@ import kotlinx.coroutines.launch
|
||||
import me.ash.reader.R
|
||||
import me.ash.reader.domain.model.article.ArticleWithFeed
|
||||
import me.ash.reader.domain.model.general.Filter
|
||||
import me.ash.reader.domain.model.general.MarkAsReadConditions
|
||||
import me.ash.reader.infrastructure.preference.LocalFlowArticleListDateStickyHeader
|
||||
import me.ash.reader.infrastructure.preference.LocalFlowArticleListFeedIcon
|
||||
import me.ash.reader.infrastructure.preference.LocalFlowArticleListTonalElevation
|
||||
@ -107,6 +107,12 @@ fun FlowPage(
|
||||
}
|
||||
}
|
||||
|
||||
DisposableEffect(pagingItems) {
|
||||
onDispose {
|
||||
flowViewModel.commitDiff()
|
||||
}
|
||||
}
|
||||
|
||||
DisposableEffect(owner) {
|
||||
homeViewModel.syncWorkLiveData.observe(owner) { workInfoList ->
|
||||
workInfoList.let {
|
||||
@ -127,15 +133,16 @@ fun FlowPage(
|
||||
|
||||
val onToggleRead: (ArticleWithFeed) -> Unit = remember {
|
||||
{ article ->
|
||||
flowViewModel.updateReadStatus(
|
||||
groupId = null,
|
||||
feedId = null,
|
||||
articleId = article.article.id,
|
||||
conditions = MarkAsReadConditions.All,
|
||||
isUnread = !article.article.isUnread,
|
||||
)
|
||||
val id = article.article.id
|
||||
val isUnread = article.article.isUnread
|
||||
|
||||
with(flowViewModel.diffMap) {
|
||||
if (contains(id)) remove(id)
|
||||
else put(id, Diff(isUnread = !isUnread))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val onMarkAboveAsRead: ((ArticleWithFeed) -> Unit)? = remember {
|
||||
{
|
||||
flowViewModel.markAsReadFromListByDate(
|
||||
@ -326,7 +333,7 @@ fun FlowPage(
|
||||
}
|
||||
ArticleList(
|
||||
pagingItems = pagingItems,
|
||||
isFilterUnread = filterUiState.filter == Filter.Unread,
|
||||
diffMap = flowViewModel.diffMap,
|
||||
isShowFeedIcon = articleListFeedIcon.value,
|
||||
isShowStickyHeader = articleListDateStickyHeader.value,
|
||||
articleListTonalElevation = articleListTonalElevation.value,
|
||||
|
@ -1,24 +1,22 @@
|
||||
package me.ash.reader.ui.page.home.flow
|
||||
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.runtime.mutableStateMapOf
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.paging.compose.LazyPagingItems
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import me.ash.reader.domain.model.article.ArticleFlowItem
|
||||
import me.ash.reader.domain.model.article.ArticleWithFeed
|
||||
import me.ash.reader.domain.model.general.MarkAsReadConditions
|
||||
import me.ash.reader.domain.service.RssService
|
||||
import me.ash.reader.infrastructure.di.ApplicationScope
|
||||
import me.ash.reader.infrastructure.di.IODispatcher
|
||||
import java.util.Date
|
||||
import java.util.function.BiPredicate
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
@ -32,6 +30,7 @@ class FlowViewModel @Inject constructor(
|
||||
|
||||
private val _flowUiState = MutableStateFlow(FlowUiState())
|
||||
val flowUiState: StateFlow<FlowUiState> = _flowUiState.asStateFlow()
|
||||
val diffMap = mutableStateMapOf<String, Diff>()
|
||||
|
||||
fun sync() {
|
||||
applicationScope.launch(ioDispatcher) {
|
||||
@ -45,10 +44,8 @@ class FlowViewModel @Inject constructor(
|
||||
articleId: String?,
|
||||
conditions: MarkAsReadConditions,
|
||||
isUnread: Boolean,
|
||||
withDelay: Long = 0,
|
||||
) {
|
||||
applicationScope.launch(ioDispatcher) {
|
||||
delay(withDelay)
|
||||
rssService.get().markAsRead(
|
||||
groupId = groupId,
|
||||
feedId = feedId,
|
||||
@ -62,11 +59,8 @@ class FlowViewModel @Inject constructor(
|
||||
fun updateStarredStatus(
|
||||
articleId: String?,
|
||||
isStarred: Boolean,
|
||||
withDelay: Long = 0,
|
||||
) {
|
||||
applicationScope.launch(ioDispatcher) {
|
||||
// FIXME: a dirty hack to ensure the swipe animation doesn't get interrupted when recomposed, remove this after implementing a lazy tag!
|
||||
delay(withDelay)
|
||||
if (articleId != null) {
|
||||
rssService.get().markAsStarred(
|
||||
articleId = articleId,
|
||||
@ -97,6 +91,21 @@ class FlowViewModel @Inject constructor(
|
||||
rssService.get().batchMarkAsRead(articleIds = articleIdSet, isUnread = false)
|
||||
}
|
||||
}
|
||||
|
||||
fun commitDiff() {
|
||||
applicationScope.launch(ioDispatcher) {
|
||||
val markAsReadArticles =
|
||||
diffMap.filter { !it.value.isUnread }.map { it.key }.toSet()
|
||||
val markAsUnreadArticles =
|
||||
diffMap.filter { it.value.isUnread }.map { it.key }.toSet()
|
||||
|
||||
rssService.get()
|
||||
.batchMarkAsRead(articleIds = markAsReadArticles, isUnread = false)
|
||||
rssService.get()
|
||||
.batchMarkAsRead(articleIds = markAsUnreadArticles, isUnread = true)
|
||||
|
||||
}.invokeOnCompletion { diffMap.clear() }
|
||||
}
|
||||
}
|
||||
|
||||
data class FlowUiState(
|
||||
@ -105,3 +114,5 @@ data class FlowUiState(
|
||||
val isBack: Boolean = false,
|
||||
val syncWorkInfo: String = "",
|
||||
)
|
||||
|
||||
data class Diff(val isUnread: Boolean)
|
||||
|
Loading…
x
Reference in New Issue
Block a user