Adjust feed list

master
Zakhar Timoshenko 2 years ago
parent d947e8234d
commit f0f25a4d48
Signed by: Xtimms
SSH Key Fingerprint: SHA256:wH6spYepK/A5erBh7ZyAnr1ru9H4eaMVBEuiw6DSpxI

@ -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 -> {

Loading…
Cancel
Save