Adjust feed list
This commit is contained in:
@@ -22,7 +22,7 @@ import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListUpdateCallback
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.xtimms.tokusho.sections.history.HistoryItemModel
|
||||
import org.xtimms.tokusho.core.model.ListModel
|
||||
import java.time.Instant
|
||||
|
||||
enum class RowEntityType { Header, Item }
|
||||
@@ -32,7 +32,7 @@ data class RowEntity(
|
||||
val key: String,
|
||||
var contentHash: String? = null,
|
||||
val day: Instant,
|
||||
var historyItemModel: HistoryItemModel?,
|
||||
var itemModel: ListModel?,
|
||||
)
|
||||
|
||||
@SuppressLint("ComposableNaming", "UnusedTransitionTargetStateParameter")
|
||||
@@ -126,7 +126,7 @@ fun updateAnimatedItemsState(
|
||||
|
||||
override fun onChanged(position: Int, count: Int, payload: Any?) {
|
||||
for (i in 0 until count) {
|
||||
compositeList[position + i].item.historyItemModel = (payload as RowEntity).historyItemModel
|
||||
compositeList[position + i].item.itemModel = (payload as RowEntity).itemModel
|
||||
compositeList[position + i].item.contentHash = payload.contentHash
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.xtimms.tokusho.core.tracker.model
|
||||
|
||||
import org.koitharu.kotatsu.parsers.model.Manga
|
||||
import org.xtimms.tokusho.core.model.ListModel
|
||||
import java.time.Instant
|
||||
|
||||
data class TrackingLogItem(
|
||||
@@ -9,4 +10,10 @@ data class TrackingLogItem(
|
||||
val chapters: List<String>,
|
||||
val createdAt: Instant,
|
||||
val isNew: Boolean,
|
||||
)
|
||||
) : ListModel {
|
||||
|
||||
override fun areItemsTheSame(other: ListModel): Boolean {
|
||||
return other is TrackingLogItem && other.manga.id == manga.id
|
||||
}
|
||||
|
||||
}
|
||||
@@ -7,8 +7,6 @@ import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.ClearAll
|
||||
import androidx.compose.material.icons.outlined.Refresh
|
||||
@@ -39,10 +37,15 @@ import org.xtimms.tokusho.core.components.DismissButton
|
||||
import org.xtimms.tokusho.core.components.ListGroupHeader
|
||||
import org.xtimms.tokusho.core.components.ScaffoldWithClassicTopAppBar
|
||||
import org.xtimms.tokusho.core.components.TokushoDialog
|
||||
import org.xtimms.tokusho.core.components.effects.RowEntity
|
||||
import org.xtimms.tokusho.core.components.effects.RowEntityType
|
||||
import org.xtimms.tokusho.core.components.effects.animatedItemsIndexed
|
||||
import org.xtimms.tokusho.core.components.effects.updateAnimatedItemsState
|
||||
import org.xtimms.tokusho.core.screens.EmptyScreen
|
||||
import org.xtimms.tokusho.core.tracker.model.TrackingLogItem
|
||||
import org.xtimms.tokusho.sections.feed.model.toFeedItem
|
||||
import org.xtimms.tokusho.utils.lang.calculateTimeAgo
|
||||
import org.xtimms.tokusho.utils.lang.isSameDay
|
||||
import java.time.Instant
|
||||
|
||||
const val FEED_DESTINATION = "feed"
|
||||
@@ -60,6 +63,39 @@ fun FeedView(
|
||||
|
||||
val feed by viewModel.content.collectAsStateWithLifecycle(emptyList())
|
||||
|
||||
val animatedList = run {
|
||||
val list = emptyList<RowEntity>().toMutableList()
|
||||
var createdAt: Instant? = null
|
||||
feed.forEach { item ->
|
||||
|
||||
if (createdAt === null || !isSameDay(
|
||||
item.createdAt.toEpochMilli(),
|
||||
createdAt!!.toEpochMilli()
|
||||
)
|
||||
) {
|
||||
createdAt = item.createdAt
|
||||
|
||||
list.add(
|
||||
RowEntity(
|
||||
type = RowEntityType.Header,
|
||||
key = "header-${createdAt}",
|
||||
itemModel = null,
|
||||
day = createdAt!!,
|
||||
)
|
||||
)
|
||||
}
|
||||
list.add(
|
||||
RowEntity(
|
||||
type = RowEntityType.Item,
|
||||
key = "item-${item.manga.id}-${item.createdAt}",
|
||||
day = createdAt!!,
|
||||
itemModel = item
|
||||
)
|
||||
)
|
||||
}
|
||||
updateAnimatedItemsState(newList = list.toList().map { it })
|
||||
}
|
||||
|
||||
ScaffoldWithClassicTopAppBar(
|
||||
title = stringResource(R.string.feed),
|
||||
navigateBack = navigateBack,
|
||||
@@ -93,7 +129,29 @@ fun FeedView(
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
contentPadding = padding
|
||||
) {
|
||||
feedUiItems(coil, viewModel.getUiModel())
|
||||
|
||||
|
||||
animatedItemsIndexed(
|
||||
state = animatedList.value,
|
||||
key = { rowItem -> rowItem.key },
|
||||
) { _, item ->
|
||||
when (item.type) {
|
||||
RowEntityType.Header -> ListGroupHeader(
|
||||
calculateTimeAgo(item.day).format(
|
||||
LocalContext.current.resources
|
||||
)
|
||||
)
|
||||
|
||||
RowEntityType.Item -> FeedViewItem(
|
||||
modifier = Modifier.animateItemPlacement(),
|
||||
coil = coil,
|
||||
selected = false,
|
||||
feed = (item.itemModel as TrackingLogItem).toFeedItem(),
|
||||
onClick = { /*TODO*/ },
|
||||
onLongClick = { /*TODO*/ }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (feed.isEmpty()) {
|
||||
@@ -179,52 +237,4 @@ private fun ClearFeedDialogPreview() {
|
||||
onDismissRequest = {},
|
||||
isClearInfoAboutNewChaptersSelected = false
|
||||
)
|
||||
}
|
||||
|
||||
sealed interface FeedUiModel {
|
||||
data class Header(val date: Instant) : FeedUiModel
|
||||
data class Item(val item: TrackingLogItem) : FeedUiModel
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
internal fun LazyListScope.feedUiItems(
|
||||
coil: ImageLoader,
|
||||
uiModels: List<FeedUiModel>
|
||||
) {
|
||||
items(
|
||||
items = uiModels,
|
||||
contentType = {
|
||||
when (it) {
|
||||
is FeedUiModel.Header -> "header"
|
||||
is FeedUiModel.Item -> "item"
|
||||
}
|
||||
},
|
||||
key = {
|
||||
when (it) {
|
||||
is FeedUiModel.Header -> "feedHeader-${it.hashCode()}"
|
||||
is FeedUiModel.Item -> "feed-${it.item.manga.id}"
|
||||
}
|
||||
},
|
||||
) { item ->
|
||||
when (item) {
|
||||
is FeedUiModel.Header -> {
|
||||
ListGroupHeader(
|
||||
modifier = Modifier.animateItemPlacement(),
|
||||
text = calculateTimeAgo(item.date).format(
|
||||
LocalContext.current.resources
|
||||
)
|
||||
)
|
||||
}
|
||||
is FeedUiModel.Item -> {
|
||||
val track = item.item
|
||||
FeedViewItem(
|
||||
modifier = Modifier.animateItemPlacement(),
|
||||
coil = coil,
|
||||
selected = false,
|
||||
feed = track.toFeedItem(),
|
||||
onClick = { /*TODO*/ },
|
||||
onLongClick = { /*TODO*/ })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,7 @@ fun FeedViewItem(
|
||||
) {
|
||||
|
||||
val haptic = LocalHapticFeedback.current
|
||||
val textAlpha = if (!feed.isNew) 1f else ReadItemAlpha
|
||||
val textAlpha = if (feed.isNew) 1f else ReadItemAlpha
|
||||
|
||||
Row(
|
||||
modifier = modifier
|
||||
@@ -85,7 +85,7 @@ fun FeedViewItem(
|
||||
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
var textHeight by remember { mutableIntStateOf(0) }
|
||||
if (!feed.isNew) {
|
||||
if (feed.isNew) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Circle,
|
||||
contentDescription = stringResource(R.string.unread),
|
||||
|
||||
@@ -54,18 +54,4 @@ class FeedViewModel @Inject constructor(
|
||||
fun updateFeed() {
|
||||
trackScheduler.startNow()
|
||||
}
|
||||
|
||||
fun getUiModel(): List<FeedUiModel> {
|
||||
return content.value
|
||||
.map { FeedUiModel.Item(it) }
|
||||
.insertSeparators { before, after ->
|
||||
val beforeDate = before?.item?.createdAt
|
||||
val afterDate = after?.item?.createdAt
|
||||
when {
|
||||
beforeDate != afterDate && afterDate != null -> FeedUiModel.Header(afterDate)
|
||||
// Return null to avoid adding a separator between two items.
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,7 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.clipToBounds
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -97,7 +98,7 @@ fun HistoryView(
|
||||
RowEntity(
|
||||
type = RowEntityType.Header,
|
||||
key = "header-${readDate}",
|
||||
historyItemModel = null,
|
||||
itemModel = null,
|
||||
day = readDate!!,
|
||||
)
|
||||
)
|
||||
@@ -107,7 +108,7 @@ fun HistoryView(
|
||||
type = RowEntityType.Item,
|
||||
key = "item-${item.manga.id}",
|
||||
day = readDate!!,
|
||||
historyItemModel = item
|
||||
itemModel = item
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -115,7 +116,9 @@ fun HistoryView(
|
||||
}
|
||||
|
||||
Box(
|
||||
Modifier.fillMaxSize()
|
||||
modifier = Modifier
|
||||
.clipToBounds()
|
||||
.fillMaxSize(),
|
||||
) {
|
||||
history.let {
|
||||
if (it == null) {
|
||||
@@ -140,7 +143,7 @@ fun HistoryView(
|
||||
animatedItemsIndexed(
|
||||
state = animatedList.value,
|
||||
key = { rowItem -> rowItem.key },
|
||||
) { index, item ->
|
||||
) { _, item ->
|
||||
when (item.type) {
|
||||
RowEntityType.Header -> ListGroupHeader(
|
||||
calculateTimeAgo(item.day).format(
|
||||
@@ -157,7 +160,7 @@ fun HistoryView(
|
||||
icon = Icons.Outlined.DeleteForever,
|
||||
stayDismissed = true,
|
||||
onDismiss = {
|
||||
viewModel.removeFromHistory(item.historyItemModel!!)
|
||||
viewModel.removeFromHistory(item.itemModel!! as HistoryItemModel)
|
||||
}
|
||||
),
|
||||
endActionsConfig = SwipeActionsConfig(
|
||||
@@ -233,8 +236,8 @@ fun HistoryView(
|
||||
) {
|
||||
HistoryItem(
|
||||
coil = coil,
|
||||
history = item.historyItemModel!!,
|
||||
onClick = { navigateToDetails(item.historyItemModel!!.manga.id) },
|
||||
history = (item.itemModel!! as HistoryItemModel),
|
||||
onClick = { navigateToDetails((item.itemModel!! as HistoryItemModel).manga.id) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,10 +29,7 @@ fun calculateTimeAgo(instant: Instant, showMonths: Boolean = false): DateTimeAgo
|
||||
val diffDays = localDate.until(now, ChronoUnit.DAYS)
|
||||
|
||||
return when {
|
||||
diffDays == 0L -> {
|
||||
if (instant.until(Instant.now(), ChronoUnit.MINUTES) < 3) DateTimeAgo.JustNow
|
||||
else DateTimeAgo.Today
|
||||
}
|
||||
diffDays == 0L -> DateTimeAgo.Today
|
||||
diffDays == 1L -> DateTimeAgo.Yesterday
|
||||
diffDays < 6 -> DateTimeAgo.DaysAgo(diffDays.toInt())
|
||||
else -> {
|
||||
|
||||
Reference in New Issue
Block a user