Skip to content

Commit

Permalink
Group update entries together (#610)
Browse files Browse the repository at this point in the history
* collapsible grouping Updates

* - Reverse chapter order

- Only show cover & marker on the first one

* - Bigger cover & support panorama view
  • Loading branch information
cuong-tran authored Dec 31, 2024
1 parent a6f3a2c commit 1f3d2cb
Show file tree
Hide file tree
Showing 5 changed files with 226 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -99,9 +99,9 @@ enum class MangaCover(val ratio: Float) {
modifier = Modifier
.size(
when (size) {
Size.Big -> 16.dp
Size.Medium -> 24.dp
else -> 32.dp
Size.Big -> COVER_TEMPLATE_SIZE_BIG
Size.Medium -> COVER_TEMPLATE_SIZE_MEDIUM
else -> COVER_TEMPLATE_SIZE_NORMAL
},
)
.align(Alignment.Center),
Expand All @@ -122,9 +122,9 @@ enum class MangaCover(val ratio: Float) {
modifier = Modifier
.size(
when (size) {
Size.Big -> 16.dp
Size.Medium -> 24.dp
else -> 32.dp
Size.Big -> COVER_TEMPLATE_SIZE_BIG
Size.Medium -> COVER_TEMPLATE_SIZE_MEDIUM
else -> COVER_TEMPLATE_SIZE_NORMAL
},
)
.align(Alignment.Center),
Expand All @@ -149,6 +149,12 @@ enum class MangaCover(val ratio: Float) {
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) {
Expand Down Expand Up @@ -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 CoverPlaceholderOnBgColor = Color(0x8F888888)
46 changes: 44 additions & 2 deletions app/src/main/java/eu/kanade/presentation/updates/UpdatesScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.CalendarMonth
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.SelectAll
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.TopAppBarScrollBehavior
Expand All @@ -27,10 +29,12 @@ import eu.kanade.presentation.manga.components.MangaBottomActionMenu
import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.ui.updates.UpdatesItem
import eu.kanade.tachiyomi.ui.updates.UpdatesScreenModel
import eu.kanade.tachiyomi.ui.updates.groupByDateAndManga
import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import tachiyomi.i18n.MR
import tachiyomi.i18n.kmk.KMR
import tachiyomi.presentation.core.components.FastScrollLazyColumn
import tachiyomi.presentation.core.components.material.PullRefresh
import tachiyomi.presentation.core.components.material.Scaffold
Expand Down Expand Up @@ -59,7 +63,13 @@ fun UpdateScreen(
onMultiDeleteClicked: (List<UpdatesItem>) -> Unit,
onUpdateSelected: (UpdatesItem, Boolean, Boolean, Boolean) -> Unit,
onOpenChapter: (UpdatesItem) -> Unit,
// KMK -->
collapseToggle: (key: String) -> Unit,
// KMK <--
) {
// KMK -->
val usePanoramaCover = remember { mutableStateOf(false) }
// KMK <--
BackHandler(enabled = state.selectionMode, onBack = { onSelectAll(false) })

Scaffold(
Expand All @@ -72,6 +82,10 @@ fun UpdateScreen(
onInvertSelection = { onInvertSelection() },
onCancelActionMode = { onSelectAll(false) },
scrollBehavior = scrollBehavior,
// KMK -->
usePanoramaCover = usePanoramaCover.value,
usePanoramaCoverClick = { usePanoramaCover.value = !usePanoramaCover.value },
// KMK
)
},
bottomBar = {
Expand Down Expand Up @@ -116,7 +130,19 @@ fun UpdateScreen(
updatesLastUpdatedItem(lastUpdated)

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,
// SY -->
preserveReadingPosition = preserveReadingPosition,
Expand All @@ -143,6 +169,10 @@ private fun UpdatesAppBar(
onInvertSelection: () -> Unit,
onCancelActionMode: () -> Unit,
scrollBehavior: TopAppBarScrollBehavior,
// KMK -->
usePanoramaCover: Boolean,
usePanoramaCoverClick: () -> Unit,
// KMK <--
modifier: Modifier = Modifier,
) {
AppBar(
Expand All @@ -151,6 +181,14 @@ private fun UpdatesAppBar(
actions = {
AppBarActions(
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(
title = stringResource(MR.strings.action_view_upcoming),
icon = Icons.Outlined.CalendarMonth,
Expand Down Expand Up @@ -222,5 +260,9 @@ private fun UpdatesBottomBar(

sealed interface 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 <--
}
135 changes: 119 additions & 16 deletions app/src/main/java/eu/kanade/presentation/updates/UpdatesUiItem.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,27 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.sizeIn
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Bookmark
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.IconButton
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableFloatState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
Expand All @@ -38,10 +43,12 @@ import eu.kanade.presentation.manga.components.ChapterDownloadAction
import eu.kanade.presentation.manga.components.ChapterDownloadIndicator
import eu.kanade.presentation.manga.components.DotSeparatorText
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.relativeTimeSpanString
import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.ui.updates.UpdatesItem
import eu.kanade.tachiyomi.ui.updates.groupByDateAndManga
import tachiyomi.domain.updates.model.UpdatesWithRelations
import tachiyomi.i18n.MR
import tachiyomi.presentation.core.components.ListGroupHeader
Expand Down Expand Up @@ -69,6 +76,11 @@ internal fun LazyListScope.updatesLastUpdatedItem(

internal fun LazyListScope.updatesUiItems(
uiModels: List<UpdatesUiModel>,
// KMK -->
expandedState: Set<String>,
collapseToggle: (key: String) -> Unit,
usePanoramaCover: Boolean,
// KMK <--
selectionMode: Boolean,
// SY -->
preserveReadingPosition: Boolean,
Expand Down Expand Up @@ -96,7 +108,10 @@ internal fun LazyListScope.updatesUiItems(
when (item) {
is UpdatesUiModel.Header -> {
ListGroupHeader(
modifier = Modifier.animateItemFastScroll(),
modifier = Modifier.animateItemFastScroll()
// KMK -->
.padding(top = MaterialTheme.padding.extraSmall),
// KMK <--
text = relativeDateText(item.date),
)
}
Expand Down Expand Up @@ -135,6 +150,13 @@ internal fun LazyListScope.updatesUiItems(
}.takeIf { !selectionMode },
downloadStateProvider = updatesItem.downloadStateProvider,
downloadProgressProvider = updatesItem.downloadProgressProvider,
// KMK -->
isLeader = item is UpdatesUiModel.Leader,
isExpandable = item.isExpandable,
expanded = expandedState.contains(updatesItem.update.groupByDateAndManga()),
collapseToggle = collapseToggle,
usePanoramaCover = usePanoramaCover,
// KMK <--
)
}
}
Expand All @@ -153,6 +175,14 @@ private fun UpdatesUiItem(
// Download Indicator
downloadStateProvider: () -> Download.State,
downloadProgressProvider: () -> Int,
// KMK -->
isLeader: Boolean,
isExpandable: Boolean,
expanded: Boolean,
collapseToggle: (key: String) -> Unit,
usePanoramaCover: Boolean,
coverRatio: MutableFloatState = remember { mutableFloatStateOf(1f) },
// KMK <--
modifier: Modifier = Modifier,
) {
val haptic = LocalHapticFeedback.current
Expand All @@ -168,27 +198,64 @@ private fun UpdatesUiItem(
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
},
)
.height(56.dp)
.padding(horizontal = MaterialTheme.padding.medium),
.padding(
// KMK -->
vertical = MaterialTheme.padding.extraSmall,
// KMK <--
horizontal = MaterialTheme.padding.medium,
),
verticalAlignment = Alignment.CenterVertically,
) {
// KMK -->
val mangaCover = update.coverData
val coverIsWide = coverRatio.floatValue <= RatioSwitchToPanorama
val bgColor = mangaCover.dominantCoverColors?.first?.let { Color(it) }
val onBgColor = mangaCover.dominantCoverColors?.second
// KMK <--
MangaCover.Square(
modifier = Modifier
.padding(vertical = 6.dp)
.fillMaxHeight(),
data = mangaCover,
onClick = onClickCover,
// KMK -->
bgColor = bgColor,
tint = onBgColor,
size = MangaCover.Size.Big,
if (isLeader) {
if (usePanoramaCover && coverIsWide) {
MangaCover.Panorama(
modifier = Modifier
.padding(top = MaterialTheme.padding.small)
.width(UpdateItemPanoramaWidth),
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
},
// 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 <--
)
}

Column(
modifier = Modifier
Expand Down Expand Up @@ -247,6 +314,15 @@ private fun UpdatesUiItem(
}
}

// KMK -->
if (isLeader && isExpandable) {
CollapseButton(
expanded = expanded,
collapseToggle = { collapseToggle(update.groupByDateAndManga()) },
)
}
// KMK <--

ChapterDownloadIndicator(
enabled = onDownloadChapter != null,
modifier = Modifier.padding(start = 4.dp),
Expand All @@ -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 <--
Loading

0 comments on commit 1f3d2cb

Please sign in to comment.