From c52de8c4729081c8526fbc6aa456d3d866b0fac5 Mon Sep 17 00:00:00 2001 From: Cuong-Tran Date: Fri, 16 May 2025 14:05:49 +0700 Subject: [PATCH] feat(updates-screen): Swipe actions for chapter updates (#915) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat(updates-screen): Swipe actions for chapter updates * Smaller cover size & add option to hide cover images (debug menu) * Correctly apply passed-in modifier * Memoize `getSwipeAction(...)`` invocations with Compose’s `remember` (keyed on the action and state) to avoid recreating those objects on every recomposition --- .../history/components/HistoryItem.kt | 59 ++-- .../components/ChapterDownloadIndicator.kt | 2 +- .../manga/components/MangaChapterListItem.kt | 54 +-- .../manga/components/MangaCover.kt | 13 +- .../presentation/updates/UpdatesScreen.kt | 11 + .../presentation/updates/UpdatesUiItem.kt | 325 +++++++++++------- .../ui/updates/UpdatesScreenModel.kt | 46 +++ .../kanade/tachiyomi/ui/updates/UpdatesTab.kt | 5 + 8 files changed, 336 insertions(+), 179 deletions(-) diff --git a/app/src/main/java/eu/kanade/presentation/history/components/HistoryItem.kt b/app/src/main/java/eu/kanade/presentation/history/components/HistoryItem.kt index 32e88debc..9b69517b5 100644 --- a/app/src/main/java/eu/kanade/presentation/history/components/HistoryItem.kt +++ b/app/src/main/java/eu/kanade/presentation/history/components/HistoryItem.kt @@ -27,10 +27,12 @@ import androidx.compose.ui.tooling.preview.PreviewLightDark import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import eu.kanade.presentation.manga.components.MangaCover +import eu.kanade.presentation.manga.components.MangaCoverHide import eu.kanade.presentation.manga.components.RatioSwitchToPanorama import eu.kanade.presentation.theme.TachiyomiPreviewTheme import eu.kanade.presentation.util.formatChapterNumber import eu.kanade.tachiyomi.util.lang.toTimestampString +import exh.debug.DebugToggles import tachiyomi.domain.history.model.HistoryWithRelations import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.material.padding @@ -63,37 +65,46 @@ fun HistoryItem( val coverIsWide = coverRatio.floatValue <= RatioSwitchToPanorama val bgColor = mangaCover.dominantCoverColors?.first?.let { Color(it) } val onBgColor = mangaCover.dominantCoverColors?.second - if (usePanoramaCover && coverIsWide) { - MangaCover.Panorama( + if (DebugToggles.HIDE_COVER_IMAGE_ONLY_SHOW_COLOR.enabled) { + MangaCoverHide.Book( modifier = Modifier.fillMaxHeight(), - 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.fillMaxHeight(), - 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 - }, + if (usePanoramaCover && coverIsWide) { + MangaCover.Panorama( + modifier = Modifier.fillMaxHeight(), + 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.fillMaxHeight(), + 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 <-- + ) + } } Column( modifier = Modifier diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/ChapterDownloadIndicator.kt b/app/src/main/java/eu/kanade/presentation/manga/components/ChapterDownloadIndicator.kt index 9878ef3a4..e06ec1908 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/ChapterDownloadIndicator.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/ChapterDownloadIndicator.kt @@ -268,7 +268,7 @@ private fun Modifier.commonClickable( ), ) -private val IndicatorSize = 26.dp +internal val IndicatorSize = 26.dp private val IndicatorPadding = 2.dp // To match composable parameter name when used later diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/MangaChapterListItem.kt b/app/src/main/java/eu/kanade/presentation/manga/components/MangaChapterListItem.kt index 55e61e529..f89594d8b 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/MangaChapterListItem.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/MangaChapterListItem.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row 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.material.icons.Icons import androidx.compose.material.icons.filled.Bookmark @@ -68,27 +69,36 @@ fun MangaChapterListItem( onChapterSwipe: (LibraryPreferences.ChapterSwipeAction) -> Unit, modifier: Modifier = Modifier, ) { - val start = getSwipeAction( - action = chapterSwipeStartAction, - read = read, - bookmark = bookmark, - downloadState = downloadStateProvider(), - background = MaterialTheme.colorScheme.primaryContainer, - onSwipe = { onChapterSwipe(chapterSwipeStartAction) }, - ) - val end = getSwipeAction( - action = chapterSwipeEndAction, - read = read, - bookmark = bookmark, - downloadState = downloadStateProvider(), - background = MaterialTheme.colorScheme.primaryContainer, - onSwipe = { onChapterSwipe(chapterSwipeEndAction) }, - ) + // KMK --> + val swipeBackground = MaterialTheme.colorScheme.primaryContainer + val swipeStart = remember(chapterSwipeStartAction, read, bookmark, downloadStateProvider()) { + // KMK <-- + getSwipeAction( + action = chapterSwipeStartAction, + read = read, + bookmark = bookmark, + downloadState = downloadStateProvider(), + background = swipeBackground, + onSwipe = { onChapterSwipe(chapterSwipeStartAction) }, + ) + } + // KMK --> + val swipeEnd = remember(chapterSwipeEndAction, read, bookmark, downloadStateProvider()) { + // KMK <-- + getSwipeAction( + action = chapterSwipeEndAction, + read = read, + bookmark = bookmark, + downloadState = downloadStateProvider(), + background = swipeBackground, + onSwipe = { onChapterSwipe(chapterSwipeEndAction) }, + ) + } SwipeableActionsBox( modifier = Modifier.clipToBounds(), - startActions = listOfNotNull(start), - endActions = listOfNotNull(end), + startActions = listOfNotNull(swipeStart), + endActions = listOfNotNull(swipeEnd), swipeThreshold = swipeActionThreshold, backgroundUntilSwipeThreshold = MaterialTheme.colorScheme.surfaceContainerLowest, ) { @@ -200,7 +210,7 @@ fun MangaChapterListItem( } } -private fun getSwipeAction( +internal fun getSwipeAction( action: LibraryPreferences.ChapterSwipeAction, read: Boolean, bookmark: Boolean, @@ -243,7 +253,9 @@ private fun swipeAction( return me.saket.swipe.SwipeAction( icon = { Icon( - modifier = Modifier.padding(16.dp), + modifier = Modifier + .padding(horizontal = 16.dp) + .size(IndicatorSize), imageVector = icon, tint = contentColorFor(background), contentDescription = null, @@ -255,4 +267,4 @@ private fun swipeAction( ) } -private val swipeActionThreshold = 56.dp +internal val swipeActionThreshold = 56.dp diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/MangaCover.kt b/app/src/main/java/eu/kanade/presentation/manga/components/MangaCover.kt index 8ac2e16fe..287f4a849 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/components/MangaCover.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/components/MangaCover.kt @@ -30,6 +30,10 @@ import androidx.compose.ui.semantics.Role import androidx.compose.ui.unit.dp import coil3.compose.AsyncImagePainter import coil3.compose.SubcomposeAsyncImage +import eu.kanade.presentation.manga.components.MangaCover.Companion.COVER_TEMPLATE_SIZE_BIG +import eu.kanade.presentation.manga.components.MangaCover.Companion.COVER_TEMPLATE_SIZE_MEDIUM +import eu.kanade.presentation.manga.components.MangaCover.Companion.COVER_TEMPLATE_SIZE_NORMAL +import eu.kanade.presentation.manga.components.MangaCover.Size import eu.kanade.tachiyomi.R import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.asMangaCover @@ -175,6 +179,7 @@ enum class MangaCoverHide(private val ratio: Float) { bgColor: Color? = CoverPlaceholderColor, /** onBackground color, which used for loading/error indicator */ @ColorInt tint: Int? = null, + size: Size = Size.Normal, ) { val modifierColored = modifier .aspectRatio(ratio) @@ -198,7 +203,13 @@ enum class MangaCoverHide(private val ratio: Float) { imageVector = ImageVector.vectorResource(R.drawable.ic_baseline_menu_book_24), contentDescription = contentDescription, modifier = Modifier - .size(32.dp) + .size( + when (size) { + Size.Big -> COVER_TEMPLATE_SIZE_BIG + Size.Medium -> COVER_TEMPLATE_SIZE_MEDIUM + else -> COVER_TEMPLATE_SIZE_NORMAL + }, + ) .align(Alignment.Center), colorFilter = ColorFilter.tint( tint?.let { Color(it) } ?: CoverPlaceholderOnBgColor, diff --git a/app/src/main/java/eu/kanade/presentation/updates/UpdatesScreen.kt b/app/src/main/java/eu/kanade/presentation/updates/UpdatesScreen.kt index 939052464..3ab60d436 100644 --- a/app/src/main/java/eu/kanade/presentation/updates/UpdatesScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/updates/UpdatesScreen.kt @@ -32,6 +32,7 @@ import eu.kanade.tachiyomi.ui.updates.UpdatesScreenModel import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.delay import kotlinx.coroutines.launch +import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.i18n.MR import tachiyomi.i18n.kmk.KMR import tachiyomi.presentation.core.components.FastScrollLazyColumn @@ -60,6 +61,11 @@ fun UpdateScreen( onMultiBookmarkClicked: (List, bookmark: Boolean) -> Unit, onMultiMarkAsReadClicked: (List, read: Boolean) -> Unit, onMultiDeleteClicked: (List) -> Unit, + // KMK --> + updateSwipeStartAction: LibraryPreferences.ChapterSwipeAction, + updateSwipeEndAction: LibraryPreferences.ChapterSwipeAction, + onUpdateSwipe: (UpdatesItem, LibraryPreferences.ChapterSwipeAction) -> Unit, + // KMK <-- onUpdateSelected: (UpdatesItem, /* KMK --> */ UpdatesScreenModel.UpdateSelectionOptions /* KMK <-- */) -> Unit, onOpenChapter: (UpdatesItem) -> Unit, // KMK --> @@ -143,6 +149,11 @@ fun UpdateScreen( onClickCover = onClickCover, onClickUpdate = onOpenChapter, onDownloadChapter = onDownloadChapter, + // KMK --> + updateSwipeStartAction = updateSwipeStartAction, + updateSwipeEndAction = updateSwipeEndAction, + onUpdateSwipe = onUpdateSwipe, + // KMK <-- ) } } diff --git a/app/src/main/java/eu/kanade/presentation/updates/UpdatesUiItem.kt b/app/src/main/java/eu/kanade/presentation/updates/UpdatesUiItem.kt index 3c2d1845d..9e8033f30 100644 --- a/app/src/main/java/eu/kanade/presentation/updates/UpdatesUiItem.kt +++ b/app/src/main/java/eu/kanade/presentation/updates/UpdatesUiItem.kt @@ -37,6 +37,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clipToBounds import androidx.compose.ui.graphics.Color import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.platform.LocalDensity @@ -48,7 +49,10 @@ 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.MangaCoverHide import eu.kanade.presentation.manga.components.RatioSwitchToPanorama +import eu.kanade.presentation.manga.components.getSwipeAction +import eu.kanade.presentation.manga.components.swipeActionThreshold import eu.kanade.presentation.util.animateItemFastScroll import eu.kanade.presentation.util.relativeTimeSpanString import eu.kanade.tachiyomi.R @@ -56,7 +60,10 @@ import eu.kanade.tachiyomi.data.download.model.Download import eu.kanade.tachiyomi.ui.updates.UpdatesItem import eu.kanade.tachiyomi.ui.updates.UpdatesScreenModel.UpdateSelectionOptions import eu.kanade.tachiyomi.ui.updates.groupByDateAndManga +import exh.debug.DebugToggles +import me.saket.swipe.SwipeableActionsBox import mihon.feature.upcoming.DateHeading +import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.domain.updates.model.UpdatesWithRelations import tachiyomi.i18n.MR import tachiyomi.presentation.core.components.material.DISABLED_ALPHA @@ -96,6 +103,11 @@ internal fun LazyListScope.updatesUiItems( onClickCover: (UpdatesItem) -> Unit, onClickUpdate: (UpdatesItem) -> Unit, onDownloadChapter: (List, ChapterDownloadAction) -> Unit, + // KMK --> + updateSwipeStartAction: LibraryPreferences.ChapterSwipeAction, + updateSwipeEndAction: LibraryPreferences.ChapterSwipeAction, + onUpdateSwipe: (UpdatesItem, LibraryPreferences.ChapterSwipeAction) -> Unit, + // KMK <-- ) { items( items = uiModels, @@ -191,6 +203,11 @@ internal fun LazyListScope.updatesUiItems( downloadStateProvider = updatesItem.downloadStateProvider, downloadProgressProvider = updatesItem.downloadProgressProvider, // KMK --> + updateSwipeStartAction = updateSwipeStartAction, + updateSwipeEndAction = updateSwipeEndAction, + onUpdateSwipe = { + onUpdateSwipe(updatesItem, it) + }, isLeader = isLeader, isExpandable = item.isExpandable, expanded = isExpanded, @@ -217,6 +234,9 @@ private fun UpdatesUiItem( downloadStateProvider: () -> Download.State, downloadProgressProvider: () -> Int, // KMK --> + updateSwipeStartAction: LibraryPreferences.ChapterSwipeAction, + updateSwipeEndAction: LibraryPreferences.ChapterSwipeAction, + onUpdateSwipe: (LibraryPreferences.ChapterSwipeAction) -> Unit, isLeader: Boolean, isExpandable: Boolean, expanded: Boolean, @@ -231,152 +251,193 @@ private fun UpdatesUiItem( val haptic = LocalHapticFeedback.current val textAlpha = if (update.read) DISABLED_ALPHA else 1f - Row( - modifier = modifier - .selectedBackground(selected) - .combinedClickable( - onClick = onClick, - onLongClick = { - onLongClick() - haptic.performHapticFeedback(HapticFeedbackType.LongPress) - }, - ) - .padding( - // KMK --> - vertical = if (isLeader) MaterialTheme.padding.extraSmall else 0.dp, - // 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 - 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 <-- - } + // KMK --> + val swipeBackground = MaterialTheme.colorScheme.primaryContainer + val swipeStart = remember(updateSwipeStartAction, update.read, update.bookmark, downloadStateProvider()) { + getSwipeAction( + action = updateSwipeStartAction, + read = update.read, + bookmark = update.bookmark, + downloadState = downloadStateProvider(), + background = swipeBackground, + onSwipe = { onUpdateSwipe(updateSwipeStartAction) }, + ) + } + val swipeEnd = remember(updateSwipeEndAction, update.read, update.bookmark, downloadStateProvider()) { + getSwipeAction( + action = updateSwipeEndAction, + read = update.read, + bookmark = update.bookmark, + downloadState = downloadStateProvider(), + background = swipeBackground, + onSwipe = { onUpdateSwipe(updateSwipeEndAction) }, + ) + } - Column( + SwipeableActionsBox( + modifier = modifier.clipToBounds(), + startActions = listOfNotNull(swipeStart), + endActions = listOfNotNull(swipeEnd), + swipeThreshold = swipeActionThreshold, + backgroundUntilSwipeThreshold = MaterialTheme.colorScheme.surfaceContainerLowest, + ) { + // KMK <-- + Row( modifier = Modifier - .padding(horizontal = MaterialTheme.padding.medium) - .weight(1f), + .selectedBackground(selected) + .combinedClickable( + onClick = onClick, + onLongClick = { + onLongClick() + haptic.performHapticFeedback(HapticFeedbackType.LongPress) + }, + ) + .padding(top = if (isLeader) MaterialTheme.padding.small else 0.dp) + .padding( + // KMK --> + vertical = if (isLeader) MaterialTheme.padding.extraSmall else 0.dp, + // 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 if (isLeader) { - // KMK <-- - Text( - text = update.mangaTitle, - maxLines = 1, - style = MaterialTheme.typography.bodyMedium, - color = LocalContentColor.current.copy(alpha = textAlpha), - overflow = TextOverflow.Ellipsis, + if (DebugToggles.HIDE_COVER_IMAGE_ONLY_SHOW_COLOR.enabled) { + MangaCoverHide.Book( + modifier = Modifier + .width(UpdateItemWidth), + bgColor = bgColor ?: (MaterialTheme.colorScheme.surface.takeIf { selected }), + tint = onBgColor, + size = MangaCover.Size.Medium, + ) + } else { + if (usePanoramaCover && coverIsWide) { + MangaCover.Panorama( + modifier = Modifier + .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 --> + .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 <-- } - Row(verticalAlignment = Alignment.CenterVertically) { - var textHeight by remember { mutableIntStateOf(0) } - if (!update.read) { - Icon( - imageVector = Icons.Filled.Circle, - contentDescription = stringResource(MR.strings.unread), - modifier = Modifier - .height(8.dp) - .padding(end = 4.dp), - tint = MaterialTheme.colorScheme.primary, - ) - } - if (update.bookmark) { - Icon( - imageVector = Icons.Filled.Bookmark, - contentDescription = stringResource(MR.strings.action_filter_bookmarked), - modifier = Modifier - .sizeIn(maxHeight = with(LocalDensity.current) { textHeight.toDp() - 2.dp }), - tint = MaterialTheme.colorScheme.primary, - ) - Spacer(modifier = Modifier.width(2.dp)) - } - Text( - text = update.chapterName, - maxLines = 1, - style = MaterialTheme.typography.bodySmall, - color = LocalContentColor.current.copy(alpha = textAlpha), - overflow = TextOverflow.Ellipsis, - onTextLayout = { textHeight = it.size.height }, - modifier = Modifier - .weight(weight = 1f, fill = false), - ) - if (readProgress != null) { - DotSeparatorText() + Column( + modifier = Modifier + .padding(horizontal = MaterialTheme.padding.medium) + .weight(1f), + ) { + // KMK --> + if (isLeader) { + // KMK <-- Text( - text = readProgress, + text = update.mangaTitle, maxLines = 1, - color = LocalContentColor.current.copy(alpha = DISABLED_ALPHA), + style = MaterialTheme.typography.bodyMedium, + color = LocalContentColor.current.copy(alpha = textAlpha), overflow = TextOverflow.Ellipsis, ) } - } - } - // KMK --> - if (isLeader && isExpandable) { - CollapseButton( - expanded = expanded, - collapseToggle = { collapseToggle(update.groupByDateAndManga()) }, + Row(verticalAlignment = Alignment.CenterVertically) { + var textHeight by remember { mutableIntStateOf(0) } + if (!update.read) { + Icon( + imageVector = Icons.Filled.Circle, + contentDescription = stringResource(MR.strings.unread), + modifier = Modifier + .height(8.dp) + .padding(end = 4.dp), + tint = MaterialTheme.colorScheme.primary, + ) + } + if (update.bookmark) { + Icon( + imageVector = Icons.Filled.Bookmark, + contentDescription = stringResource(MR.strings.action_filter_bookmarked), + modifier = Modifier + .sizeIn(maxHeight = with(LocalDensity.current) { textHeight.toDp() - 2.dp }), + tint = MaterialTheme.colorScheme.primary, + ) + Spacer(modifier = Modifier.width(2.dp)) + } + Text( + text = update.chapterName, + maxLines = 1, + style = MaterialTheme.typography.bodySmall, + color = LocalContentColor.current.copy(alpha = textAlpha), + overflow = TextOverflow.Ellipsis, + onTextLayout = { textHeight = it.size.height }, + modifier = Modifier + .weight(weight = 1f, fill = false), + ) + if (readProgress != null) { + DotSeparatorText() + Text( + text = readProgress, + maxLines = 1, + color = LocalContentColor.current.copy(alpha = DISABLED_ALPHA), + overflow = TextOverflow.Ellipsis, + ) + } + } + } + + // KMK --> + if (isLeader && isExpandable) { + CollapseButton( + expanded = expanded, + collapseToggle = { collapseToggle(update.groupByDateAndManga()) }, + ) + } + // KMK <-- + + ChapterDownloadIndicator( + enabled = onDownloadChapter != null, + modifier = Modifier.padding(start = 4.dp), + downloadStateProvider = downloadStateProvider, + downloadProgressProvider = downloadProgressProvider, + onClick = { onDownloadChapter?.invoke(it) }, ) } - // KMK <-- - - ChapterDownloadIndicator( - enabled = onDownloadChapter != null, - modifier = Modifier.padding(start = 4.dp), - downloadStateProvider = downloadStateProvider, - downloadProgressProvider = downloadProgressProvider, - onClick = { onDownloadChapter?.invoke(it) }, - ) } } @@ -412,6 +473,6 @@ fun CollapseButton( private val IndicatorSize = MaterialTheme.padding.large -private val UpdateItemPanoramaWidth = 126.dp // Book cover -private val UpdateItemWidth = 56.dp +private val UpdateItemPanoramaWidth = 108.dp // Book cover +private val UpdateItemWidth = 48.dp // KMK <-- diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt index df5018788..f389134f8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt @@ -432,6 +432,52 @@ class UpdatesScreenModel( ) } } + + val chapterSwipeStartAction by libraryPreferences.swipeToEndAction().asState(screenModelScope) + val chapterSwipeEndAction by libraryPreferences.swipeToStartAction().asState(screenModelScope) + + /** + * @throws IllegalStateException if the swipe action is [LibraryPreferences.ChapterSwipeAction.Disabled] + */ + fun updateSwipe(updateItem: UpdatesItem, swipeAction: LibraryPreferences.ChapterSwipeAction) { + screenModelScope.launch { + executeUpdateSwipeAction(updateItem, swipeAction) + } + } + + /** + * @throws IllegalStateException if the swipe action is [LibraryPreferences.ChapterSwipeAction.Disabled] + */ + private fun executeUpdateSwipeAction( + updateItem: UpdatesItem, + swipeAction: LibraryPreferences.ChapterSwipeAction, + ) { + val update = updateItem.update + when (swipeAction) { + LibraryPreferences.ChapterSwipeAction.ToggleRead -> { + markUpdatesRead(listOf(updateItem), !update.read) + } + LibraryPreferences.ChapterSwipeAction.ToggleBookmark -> { + bookmarkUpdates(listOf(updateItem), !update.bookmark) + } + LibraryPreferences.ChapterSwipeAction.Download -> { + val downloadAction = when (updateItem.downloadStateProvider()) { + Download.State.ERROR, + Download.State.NOT_DOWNLOADED, + -> ChapterDownloadAction.START_NOW + Download.State.QUEUE, + Download.State.DOWNLOADING, + -> ChapterDownloadAction.CANCEL + Download.State.DOWNLOADED -> ChapterDownloadAction.DELETE + } + downloadChapters( + items = listOf(updateItem), + action = downloadAction, + ) + } + LibraryPreferences.ChapterSwipeAction.Disabled -> throw IllegalStateException() + } + } // KMK <-- @Immutable diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesTab.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesTab.kt index 99592ed30..a8294f36c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesTab.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesTab.kt @@ -88,6 +88,11 @@ data object UpdatesTab : Tab { onMultiBookmarkClicked = screenModel::bookmarkUpdates, onMultiMarkAsReadClicked = screenModel::markUpdatesRead, onMultiDeleteClicked = screenModel::showConfirmDeleteChapters, + // KMK --> + updateSwipeStartAction = screenModel.chapterSwipeStartAction, + updateSwipeEndAction = screenModel.chapterSwipeEndAction, + onUpdateSwipe = screenModel::updateSwipe, + // KMK <-- onUpdateSelected = screenModel::toggleSelection, onOpenChapter = { val intent = ReaderActivity.newIntent(context, it.update.mangaId, it.update.chapterId)