Group update entries together (#610)
* collapsible grouping Updates * - Reverse chapter order - Only show cover & marker on the first one * - Bigger cover & support panorama view
This commit is contained in:
parent
a6f3a2cdcf
commit
1f3d2cbd3c
5 changed files with 226 additions and 27 deletions
|
|
@ -99,9 +99,9 @@ enum class MangaCover(val ratio: Float) {
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(
|
.size(
|
||||||
when (size) {
|
when (size) {
|
||||||
Size.Big -> 16.dp
|
Size.Big -> COVER_TEMPLATE_SIZE_BIG
|
||||||
Size.Medium -> 24.dp
|
Size.Medium -> COVER_TEMPLATE_SIZE_MEDIUM
|
||||||
else -> 32.dp
|
else -> COVER_TEMPLATE_SIZE_NORMAL
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.align(Alignment.Center),
|
.align(Alignment.Center),
|
||||||
|
|
@ -122,9 +122,9 @@ enum class MangaCover(val ratio: Float) {
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(
|
.size(
|
||||||
when (size) {
|
when (size) {
|
||||||
Size.Big -> 16.dp
|
Size.Big -> COVER_TEMPLATE_SIZE_BIG
|
||||||
Size.Medium -> 24.dp
|
Size.Medium -> COVER_TEMPLATE_SIZE_MEDIUM
|
||||||
else -> 32.dp
|
else -> COVER_TEMPLATE_SIZE_NORMAL
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.align(Alignment.Center),
|
.align(Alignment.Center),
|
||||||
|
|
@ -149,6 +149,12 @@ enum class MangaCover(val ratio: Float) {
|
||||||
contentScale = scale,
|
contentScale = scale,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val COVER_TEMPLATE_SIZE_BIG = 16.dp
|
||||||
|
val COVER_TEMPLATE_SIZE_MEDIUM = 24.dp
|
||||||
|
val COVER_TEMPLATE_SIZE_NORMAL = 32.dp
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class MangaCoverHide(private val ratio: Float) {
|
enum class MangaCoverHide(private val ratio: Float) {
|
||||||
|
|
@ -200,7 +206,7 @@ enum class MangaCoverHide(private val ratio: Float) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal val RatioSwitchToPanorama = 0.75f
|
internal const val RatioSwitchToPanorama = 0.75f
|
||||||
|
|
||||||
internal val CoverPlaceholderColor = Color(0x1F888888)
|
internal val CoverPlaceholderColor = Color(0x1F888888)
|
||||||
internal val CoverPlaceholderOnBgColor = Color(0x8F888888)
|
internal val CoverPlaceholderOnBgColor = Color(0x8F888888)
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,10 @@ import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.outlined.CalendarMonth
|
import androidx.compose.material.icons.outlined.CalendarMonth
|
||||||
import androidx.compose.material.icons.outlined.FlipToBack
|
import androidx.compose.material.icons.outlined.FlipToBack
|
||||||
|
import androidx.compose.material.icons.outlined.Panorama
|
||||||
import androidx.compose.material.icons.outlined.Refresh
|
import androidx.compose.material.icons.outlined.Refresh
|
||||||
import androidx.compose.material.icons.outlined.SelectAll
|
import androidx.compose.material.icons.outlined.SelectAll
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.SnackbarHost
|
import androidx.compose.material3.SnackbarHost
|
||||||
import androidx.compose.material3.SnackbarHostState
|
import androidx.compose.material3.SnackbarHostState
|
||||||
import androidx.compose.material3.TopAppBarScrollBehavior
|
import androidx.compose.material3.TopAppBarScrollBehavior
|
||||||
|
|
@ -27,10 +29,12 @@ import eu.kanade.presentation.manga.components.MangaBottomActionMenu
|
||||||
import eu.kanade.tachiyomi.data.download.model.Download
|
import eu.kanade.tachiyomi.data.download.model.Download
|
||||||
import eu.kanade.tachiyomi.ui.updates.UpdatesItem
|
import eu.kanade.tachiyomi.ui.updates.UpdatesItem
|
||||||
import eu.kanade.tachiyomi.ui.updates.UpdatesScreenModel
|
import eu.kanade.tachiyomi.ui.updates.UpdatesScreenModel
|
||||||
|
import eu.kanade.tachiyomi.ui.updates.groupByDateAndManga
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
|
import tachiyomi.i18n.kmk.KMR
|
||||||
import tachiyomi.presentation.core.components.FastScrollLazyColumn
|
import tachiyomi.presentation.core.components.FastScrollLazyColumn
|
||||||
import tachiyomi.presentation.core.components.material.PullRefresh
|
import tachiyomi.presentation.core.components.material.PullRefresh
|
||||||
import tachiyomi.presentation.core.components.material.Scaffold
|
import tachiyomi.presentation.core.components.material.Scaffold
|
||||||
|
|
@ -59,7 +63,13 @@ fun UpdateScreen(
|
||||||
onMultiDeleteClicked: (List<UpdatesItem>) -> Unit,
|
onMultiDeleteClicked: (List<UpdatesItem>) -> Unit,
|
||||||
onUpdateSelected: (UpdatesItem, Boolean, Boolean, Boolean) -> Unit,
|
onUpdateSelected: (UpdatesItem, Boolean, Boolean, Boolean) -> Unit,
|
||||||
onOpenChapter: (UpdatesItem) -> Unit,
|
onOpenChapter: (UpdatesItem) -> Unit,
|
||||||
|
// KMK -->
|
||||||
|
collapseToggle: (key: String) -> Unit,
|
||||||
|
// KMK <--
|
||||||
) {
|
) {
|
||||||
|
// KMK -->
|
||||||
|
val usePanoramaCover = remember { mutableStateOf(false) }
|
||||||
|
// KMK <--
|
||||||
BackHandler(enabled = state.selectionMode, onBack = { onSelectAll(false) })
|
BackHandler(enabled = state.selectionMode, onBack = { onSelectAll(false) })
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
|
|
@ -72,6 +82,10 @@ fun UpdateScreen(
|
||||||
onInvertSelection = { onInvertSelection() },
|
onInvertSelection = { onInvertSelection() },
|
||||||
onCancelActionMode = { onSelectAll(false) },
|
onCancelActionMode = { onSelectAll(false) },
|
||||||
scrollBehavior = scrollBehavior,
|
scrollBehavior = scrollBehavior,
|
||||||
|
// KMK -->
|
||||||
|
usePanoramaCover = usePanoramaCover.value,
|
||||||
|
usePanoramaCoverClick = { usePanoramaCover.value = !usePanoramaCover.value },
|
||||||
|
// KMK
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
bottomBar = {
|
bottomBar = {
|
||||||
|
|
@ -116,7 +130,19 @@ fun UpdateScreen(
|
||||||
updatesLastUpdatedItem(lastUpdated)
|
updatesLastUpdatedItem(lastUpdated)
|
||||||
|
|
||||||
updatesUiItems(
|
updatesUiItems(
|
||||||
uiModels = state.getUiModel(),
|
uiModels = state.getUiModel()
|
||||||
|
// KMK -->
|
||||||
|
.filter {
|
||||||
|
when (it) {
|
||||||
|
is UpdatesUiModel.Header, is UpdatesUiModel.Leader -> true
|
||||||
|
is UpdatesUiModel.Item ->
|
||||||
|
state.expandedState.contains(it.item.update.groupByDateAndManga())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
expandedState = state.expandedState,
|
||||||
|
collapseToggle = collapseToggle,
|
||||||
|
usePanoramaCover = usePanoramaCover.value,
|
||||||
|
// KMK <--
|
||||||
selectionMode = state.selectionMode,
|
selectionMode = state.selectionMode,
|
||||||
// SY -->
|
// SY -->
|
||||||
preserveReadingPosition = preserveReadingPosition,
|
preserveReadingPosition = preserveReadingPosition,
|
||||||
|
|
@ -143,6 +169,10 @@ private fun UpdatesAppBar(
|
||||||
onInvertSelection: () -> Unit,
|
onInvertSelection: () -> Unit,
|
||||||
onCancelActionMode: () -> Unit,
|
onCancelActionMode: () -> Unit,
|
||||||
scrollBehavior: TopAppBarScrollBehavior,
|
scrollBehavior: TopAppBarScrollBehavior,
|
||||||
|
// KMK -->
|
||||||
|
usePanoramaCover: Boolean,
|
||||||
|
usePanoramaCoverClick: () -> Unit,
|
||||||
|
// KMK <--
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
AppBar(
|
AppBar(
|
||||||
|
|
@ -151,6 +181,14 @@ private fun UpdatesAppBar(
|
||||||
actions = {
|
actions = {
|
||||||
AppBarActions(
|
AppBarActions(
|
||||||
persistentListOf(
|
persistentListOf(
|
||||||
|
// KMK -->
|
||||||
|
AppBar.Action(
|
||||||
|
title = stringResource(KMR.strings.action_panorama_cover),
|
||||||
|
icon = Icons.Outlined.Panorama,
|
||||||
|
iconTint = MaterialTheme.colorScheme.primary.takeIf { usePanoramaCover },
|
||||||
|
onClick = usePanoramaCoverClick,
|
||||||
|
),
|
||||||
|
// KMK <--
|
||||||
AppBar.Action(
|
AppBar.Action(
|
||||||
title = stringResource(MR.strings.action_view_upcoming),
|
title = stringResource(MR.strings.action_view_upcoming),
|
||||||
icon = Icons.Outlined.CalendarMonth,
|
icon = Icons.Outlined.CalendarMonth,
|
||||||
|
|
@ -222,5 +260,9 @@ private fun UpdatesBottomBar(
|
||||||
|
|
||||||
sealed interface UpdatesUiModel {
|
sealed interface UpdatesUiModel {
|
||||||
data class Header(val date: LocalDate) : UpdatesUiModel
|
data class Header(val date: LocalDate) : UpdatesUiModel
|
||||||
data class Item(val item: UpdatesItem) : UpdatesUiModel
|
open class Item(open val item: UpdatesItem, open val isExpandable: Boolean = false) : UpdatesUiModel
|
||||||
|
// KMK -->
|
||||||
|
/** The first [Item] in a group of chapters from same manga */
|
||||||
|
data class Leader(override val item: UpdatesItem, override val isExpandable: Boolean) : Item(item)
|
||||||
|
// KMK <--
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,9 @@ import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.sizeIn
|
import androidx.compose.foundation.layout.sizeIn
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.lazy.LazyListScope
|
import androidx.compose.foundation.lazy.LazyListScope
|
||||||
|
|
@ -15,12 +15,17 @@ import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Bookmark
|
import androidx.compose.material.icons.filled.Bookmark
|
||||||
import androidx.compose.material.icons.filled.Circle
|
import androidx.compose.material.icons.filled.Circle
|
||||||
|
import androidx.compose.material.icons.filled.KeyboardArrowDown
|
||||||
|
import androidx.compose.material.icons.filled.KeyboardArrowUp
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.LocalContentColor
|
import androidx.compose.material3.LocalContentColor
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.MutableFloatState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableFloatStateOf
|
||||||
import androidx.compose.runtime.mutableIntStateOf
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
|
|
@ -38,10 +43,12 @@ import eu.kanade.presentation.manga.components.ChapterDownloadAction
|
||||||
import eu.kanade.presentation.manga.components.ChapterDownloadIndicator
|
import eu.kanade.presentation.manga.components.ChapterDownloadIndicator
|
||||||
import eu.kanade.presentation.manga.components.DotSeparatorText
|
import eu.kanade.presentation.manga.components.DotSeparatorText
|
||||||
import eu.kanade.presentation.manga.components.MangaCover
|
import eu.kanade.presentation.manga.components.MangaCover
|
||||||
|
import eu.kanade.presentation.manga.components.RatioSwitchToPanorama
|
||||||
import eu.kanade.presentation.util.animateItemFastScroll
|
import eu.kanade.presentation.util.animateItemFastScroll
|
||||||
import eu.kanade.presentation.util.relativeTimeSpanString
|
import eu.kanade.presentation.util.relativeTimeSpanString
|
||||||
import eu.kanade.tachiyomi.data.download.model.Download
|
import eu.kanade.tachiyomi.data.download.model.Download
|
||||||
import eu.kanade.tachiyomi.ui.updates.UpdatesItem
|
import eu.kanade.tachiyomi.ui.updates.UpdatesItem
|
||||||
|
import eu.kanade.tachiyomi.ui.updates.groupByDateAndManga
|
||||||
import tachiyomi.domain.updates.model.UpdatesWithRelations
|
import tachiyomi.domain.updates.model.UpdatesWithRelations
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
import tachiyomi.presentation.core.components.ListGroupHeader
|
import tachiyomi.presentation.core.components.ListGroupHeader
|
||||||
|
|
@ -69,6 +76,11 @@ internal fun LazyListScope.updatesLastUpdatedItem(
|
||||||
|
|
||||||
internal fun LazyListScope.updatesUiItems(
|
internal fun LazyListScope.updatesUiItems(
|
||||||
uiModels: List<UpdatesUiModel>,
|
uiModels: List<UpdatesUiModel>,
|
||||||
|
// KMK -->
|
||||||
|
expandedState: Set<String>,
|
||||||
|
collapseToggle: (key: String) -> Unit,
|
||||||
|
usePanoramaCover: Boolean,
|
||||||
|
// KMK <--
|
||||||
selectionMode: Boolean,
|
selectionMode: Boolean,
|
||||||
// SY -->
|
// SY -->
|
||||||
preserveReadingPosition: Boolean,
|
preserveReadingPosition: Boolean,
|
||||||
|
|
@ -96,7 +108,10 @@ internal fun LazyListScope.updatesUiItems(
|
||||||
when (item) {
|
when (item) {
|
||||||
is UpdatesUiModel.Header -> {
|
is UpdatesUiModel.Header -> {
|
||||||
ListGroupHeader(
|
ListGroupHeader(
|
||||||
modifier = Modifier.animateItemFastScroll(),
|
modifier = Modifier.animateItemFastScroll()
|
||||||
|
// KMK -->
|
||||||
|
.padding(top = MaterialTheme.padding.extraSmall),
|
||||||
|
// KMK <--
|
||||||
text = relativeDateText(item.date),
|
text = relativeDateText(item.date),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -135,6 +150,13 @@ internal fun LazyListScope.updatesUiItems(
|
||||||
}.takeIf { !selectionMode },
|
}.takeIf { !selectionMode },
|
||||||
downloadStateProvider = updatesItem.downloadStateProvider,
|
downloadStateProvider = updatesItem.downloadStateProvider,
|
||||||
downloadProgressProvider = updatesItem.downloadProgressProvider,
|
downloadProgressProvider = updatesItem.downloadProgressProvider,
|
||||||
|
// KMK -->
|
||||||
|
isLeader = item is UpdatesUiModel.Leader,
|
||||||
|
isExpandable = item.isExpandable,
|
||||||
|
expanded = expandedState.contains(updatesItem.update.groupByDateAndManga()),
|
||||||
|
collapseToggle = collapseToggle,
|
||||||
|
usePanoramaCover = usePanoramaCover,
|
||||||
|
// KMK <--
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -153,6 +175,14 @@ private fun UpdatesUiItem(
|
||||||
// Download Indicator
|
// Download Indicator
|
||||||
downloadStateProvider: () -> Download.State,
|
downloadStateProvider: () -> Download.State,
|
||||||
downloadProgressProvider: () -> Int,
|
downloadProgressProvider: () -> Int,
|
||||||
|
// KMK -->
|
||||||
|
isLeader: Boolean,
|
||||||
|
isExpandable: Boolean,
|
||||||
|
expanded: Boolean,
|
||||||
|
collapseToggle: (key: String) -> Unit,
|
||||||
|
usePanoramaCover: Boolean,
|
||||||
|
coverRatio: MutableFloatState = remember { mutableFloatStateOf(1f) },
|
||||||
|
// KMK <--
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val haptic = LocalHapticFeedback.current
|
val haptic = LocalHapticFeedback.current
|
||||||
|
|
@ -168,27 +198,64 @@ private fun UpdatesUiItem(
|
||||||
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
|
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.height(56.dp)
|
.padding(
|
||||||
.padding(horizontal = MaterialTheme.padding.medium),
|
// KMK -->
|
||||||
|
vertical = MaterialTheme.padding.extraSmall,
|
||||||
|
// KMK <--
|
||||||
|
horizontal = MaterialTheme.padding.medium,
|
||||||
|
),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
// KMK -->
|
// KMK -->
|
||||||
val mangaCover = update.coverData
|
val mangaCover = update.coverData
|
||||||
|
val coverIsWide = coverRatio.floatValue <= RatioSwitchToPanorama
|
||||||
val bgColor = mangaCover.dominantCoverColors?.first?.let { Color(it) }
|
val bgColor = mangaCover.dominantCoverColors?.first?.let { Color(it) }
|
||||||
val onBgColor = mangaCover.dominantCoverColors?.second
|
val onBgColor = mangaCover.dominantCoverColors?.second
|
||||||
// KMK <--
|
if (isLeader) {
|
||||||
MangaCover.Square(
|
if (usePanoramaCover && coverIsWide) {
|
||||||
modifier = Modifier
|
MangaCover.Panorama(
|
||||||
.padding(vertical = 6.dp)
|
modifier = Modifier
|
||||||
.fillMaxHeight(),
|
.padding(top = MaterialTheme.padding.small)
|
||||||
data = mangaCover,
|
.width(UpdateItemPanoramaWidth),
|
||||||
onClick = onClickCover,
|
data = mangaCover,
|
||||||
// KMK -->
|
onClick = onClickCover,
|
||||||
bgColor = bgColor,
|
// KMK -->
|
||||||
tint = onBgColor,
|
bgColor = bgColor,
|
||||||
size = MangaCover.Size.Big,
|
tint = onBgColor,
|
||||||
|
size = MangaCover.Size.Medium,
|
||||||
|
onCoverLoaded = { _, result ->
|
||||||
|
val image = result.result.image
|
||||||
|
coverRatio.floatValue = image.height.toFloat() / image.width
|
||||||
|
},
|
||||||
|
// KMK <--
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
// KMK <--
|
||||||
|
MangaCover.Book(
|
||||||
|
modifier = Modifier
|
||||||
|
// KMK -->
|
||||||
|
.padding(top = MaterialTheme.padding.small)
|
||||||
|
.width(UpdateItemWidth),
|
||||||
|
// KMK <--
|
||||||
|
data = mangaCover,
|
||||||
|
onClick = onClickCover,
|
||||||
|
// KMK -->
|
||||||
|
bgColor = bgColor,
|
||||||
|
tint = onBgColor,
|
||||||
|
size = MangaCover.Size.Medium,
|
||||||
|
onCoverLoaded = { _, result ->
|
||||||
|
val image = result.result.image
|
||||||
|
coverRatio.floatValue = image.height.toFloat() / image.width
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.width(if (usePanoramaCover && coverIsWide) UpdateItemPanoramaWidth else UpdateItemWidth),
|
||||||
|
)
|
||||||
// KMK <--
|
// KMK <--
|
||||||
)
|
}
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
@ -247,6 +314,15 @@ private fun UpdatesUiItem(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// KMK -->
|
||||||
|
if (isLeader && isExpandable) {
|
||||||
|
CollapseButton(
|
||||||
|
expanded = expanded,
|
||||||
|
collapseToggle = { collapseToggle(update.groupByDateAndManga()) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// KMK <--
|
||||||
|
|
||||||
ChapterDownloadIndicator(
|
ChapterDownloadIndicator(
|
||||||
enabled = onDownloadChapter != null,
|
enabled = onDownloadChapter != null,
|
||||||
modifier = Modifier.padding(start = 4.dp),
|
modifier = Modifier.padding(start = 4.dp),
|
||||||
|
|
@ -256,3 +332,30 @@ private fun UpdatesUiItem(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// KMK -->
|
||||||
|
@Composable
|
||||||
|
fun CollapseButton(
|
||||||
|
expanded: Boolean,
|
||||||
|
collapseToggle: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = modifier
|
||||||
|
.size(IndicatorSize),
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
|
) {
|
||||||
|
IconButton(onClick = { collapseToggle() }) {
|
||||||
|
Icon(
|
||||||
|
imageVector = if (expanded) Icons.Default.KeyboardArrowUp else Icons.Default.KeyboardArrowDown,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = MaterialTheme.colorScheme.primary,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val IndicatorSize = 18.dp
|
||||||
|
private val UpdateItemPanoramaWidth = 126.dp
|
||||||
|
private val UpdateItemWidth = 56.dp
|
||||||
|
// KMK <--
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import exh.source.EXH_SOURCE_ID
|
||||||
import kotlinx.collections.immutable.PersistentList
|
import kotlinx.collections.immutable.PersistentList
|
||||||
import kotlinx.collections.immutable.mutate
|
import kotlinx.collections.immutable.mutate
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
import kotlinx.collections.immutable.persistentSetOf
|
||||||
import kotlinx.collections.immutable.toPersistentList
|
import kotlinx.collections.immutable.toPersistentList
|
||||||
import kotlinx.coroutines.channels.Channel
|
import kotlinx.coroutines.channels.Channel
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
@ -379,18 +380,43 @@ class UpdatesScreenModel(
|
||||||
libraryPreferences.newUpdatesCount().set(0)
|
libraryPreferences.newUpdatesCount().set(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// KMK -->
|
||||||
|
fun toggleExpandedState(key: String) {
|
||||||
|
mutableState.update {
|
||||||
|
it.copy(
|
||||||
|
expandedState = it.expandedState.toMutableSet().apply {
|
||||||
|
if (it.expandedState.contains(key)) remove(key) else add(key)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// KMK <--
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
data class State(
|
data class State(
|
||||||
val isLoading: Boolean = true,
|
val isLoading: Boolean = true,
|
||||||
val items: PersistentList<UpdatesItem> = persistentListOf(),
|
val items: PersistentList<UpdatesItem> = persistentListOf(),
|
||||||
|
// KMK -->
|
||||||
|
val expandedState: Set<String> = persistentSetOf(),
|
||||||
|
// KMK <--
|
||||||
val dialog: Dialog? = null,
|
val dialog: Dialog? = null,
|
||||||
) {
|
) {
|
||||||
val selected = items.filter { it.selected }
|
val selected = items.filter { it.selected }
|
||||||
val selectionMode = selected.isNotEmpty()
|
val selectionMode = selected.isNotEmpty()
|
||||||
|
|
||||||
fun getUiModel(): List<UpdatesUiModel> {
|
fun getUiModel(): List<UpdatesUiModel> {
|
||||||
return items
|
// KMK -->
|
||||||
.map { UpdatesUiModel.Item(it) }
|
var lastMangaId = -1L
|
||||||
|
// KMK <--
|
||||||
|
return items.groupBy { it.update.dateFetch.toLocalDate() }
|
||||||
|
.flatMap { groupDate ->
|
||||||
|
groupDate.value.groupBy { it.update.mangaId }
|
||||||
|
.flatMap { groupManga ->
|
||||||
|
val list = groupManga.value
|
||||||
|
list.sortedBy { it.update.dateFetch }
|
||||||
|
.map { UpdatesUiModel.Item(it, list.size > 1) }
|
||||||
|
}
|
||||||
|
}
|
||||||
.insertSeparators { before, after ->
|
.insertSeparators { before, after ->
|
||||||
val beforeDate = before?.item?.update?.dateFetch?.toLocalDate()
|
val beforeDate = before?.item?.update?.dateFetch?.toLocalDate()
|
||||||
val afterDate = after?.item?.update?.dateFetch?.toLocalDate()
|
val afterDate = after?.item?.update?.dateFetch?.toLocalDate()
|
||||||
|
|
@ -400,6 +426,21 @@ class UpdatesScreenModel(
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// KMK -->
|
||||||
|
.map {
|
||||||
|
if (it is UpdatesUiModel.Header) {
|
||||||
|
lastMangaId = -1L
|
||||||
|
it
|
||||||
|
} else {
|
||||||
|
if ((it as UpdatesUiModel.Item).item.update.mangaId != lastMangaId) {
|
||||||
|
lastMangaId = it.item.update.mangaId
|
||||||
|
UpdatesUiModel.Leader(it.item, it.isExpandable)
|
||||||
|
} else {
|
||||||
|
it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// KMK <--
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -426,3 +467,7 @@ data class UpdatesItem(
|
||||||
}
|
}
|
||||||
// SY <--
|
// SY <--
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// KMK -->
|
||||||
|
fun UpdatesWithRelations.groupByDateAndManga() = "${dateFetch.toLocalDate().toEpochDay()}-$mangaId"
|
||||||
|
// KMK <--
|
||||||
|
|
|
||||||
|
|
@ -94,6 +94,9 @@ data object UpdatesTab : Tab {
|
||||||
context.startActivity(intent)
|
context.startActivity(intent)
|
||||||
},
|
},
|
||||||
onCalendarClicked = { navigator.push(UpcomingScreen()) },
|
onCalendarClicked = { navigator.push(UpcomingScreen()) },
|
||||||
|
// KMK -->
|
||||||
|
collapseToggle = screenModel::toggleExpandedState,
|
||||||
|
// KMK <--
|
||||||
)
|
)
|
||||||
|
|
||||||
val onDismissDialog = { screenModel.setDialog(null) }
|
val onDismissDialog = { screenModel.setDialog(null) }
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue