Add swipe actions for chapters (#9304)
* added chapter swipe * Rework corner animtion * Update i18n/src/main/res/values/strings.xml Co-authored-by: arkon <arkon@users.noreply.github.com> * Replace LTR/RTL with Start/End layout * Added label to the animation so the warning will go away * Getting rid of the swipe threshold setting * adding disabled option, renaming stuff, other stuff? * Getting rid of the snackbar * Getting rid of unecessary strings * changing enum names as requested * Renaming Raio to Ratio (I need a better keyboard as well -__-) * Replacing error with download icon and action * backup * minor cleanup * fixing an nasty edge case * fixing mistakes in the previous conflict * space * fixing bug fixed bug where the user could dismiss already dismissed item leading to item getting stuck * fixing lint errors * fixing lints (hopefully) * Added "swipe disabled" to the list of actions * Replacing string value and moving value as requested * replacing rest of the strings with generic ones --------- Co-authored-by: arkon <arkon@users.noreply.github.com>
This commit is contained in:
parent
ef3d2c14b4
commit
a8f17a3fab
7 changed files with 419 additions and 89 deletions
|
@ -66,6 +66,7 @@ import eu.kanade.tachiyomi.util.lang.toRelativeString
|
||||||
import eu.kanade.tachiyomi.util.system.copyToClipboard
|
import eu.kanade.tachiyomi.util.system.copyToClipboard
|
||||||
import tachiyomi.domain.chapter.model.Chapter
|
import tachiyomi.domain.chapter.model.Chapter
|
||||||
import tachiyomi.domain.chapter.service.missingChaptersCount
|
import tachiyomi.domain.chapter.service.missingChaptersCount
|
||||||
|
import tachiyomi.domain.library.service.LibraryPreferences
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
import tachiyomi.domain.source.model.StubSource
|
import tachiyomi.domain.source.model.StubSource
|
||||||
import tachiyomi.presentation.core.components.LazyColumn
|
import tachiyomi.presentation.core.components.LazyColumn
|
||||||
|
@ -86,6 +87,8 @@ fun MangaScreen(
|
||||||
dateRelativeTime: Int,
|
dateRelativeTime: Int,
|
||||||
dateFormat: DateFormat,
|
dateFormat: DateFormat,
|
||||||
isTabletUi: Boolean,
|
isTabletUi: Boolean,
|
||||||
|
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
|
||||||
|
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
|
||||||
onBackClicked: () -> Unit,
|
onBackClicked: () -> Unit,
|
||||||
onChapterClicked: (Chapter) -> Unit,
|
onChapterClicked: (Chapter) -> Unit,
|
||||||
onDownloadChapter: ((List<ChapterItem>, ChapterDownloadAction) -> Unit)?,
|
onDownloadChapter: ((List<ChapterItem>, ChapterDownloadAction) -> Unit)?,
|
||||||
|
@ -117,6 +120,9 @@ fun MangaScreen(
|
||||||
onMarkPreviousAsReadClicked: (Chapter) -> Unit,
|
onMarkPreviousAsReadClicked: (Chapter) -> Unit,
|
||||||
onMultiDeleteClicked: (List<Chapter>) -> Unit,
|
onMultiDeleteClicked: (List<Chapter>) -> Unit,
|
||||||
|
|
||||||
|
// For chapter swipe
|
||||||
|
onChapterSwipe: (ChapterItem, LibraryPreferences.ChapterSwipeAction) -> Unit,
|
||||||
|
|
||||||
// Chapter selection
|
// Chapter selection
|
||||||
onChapterSelected: (ChapterItem, Boolean, Boolean, Boolean) -> Unit,
|
onChapterSelected: (ChapterItem, Boolean, Boolean, Boolean) -> Unit,
|
||||||
onAllChapterSelected: (Boolean) -> Unit,
|
onAllChapterSelected: (Boolean) -> Unit,
|
||||||
|
@ -135,6 +141,8 @@ fun MangaScreen(
|
||||||
snackbarHostState = snackbarHostState,
|
snackbarHostState = snackbarHostState,
|
||||||
dateRelativeTime = dateRelativeTime,
|
dateRelativeTime = dateRelativeTime,
|
||||||
dateFormat = dateFormat,
|
dateFormat = dateFormat,
|
||||||
|
chapterSwipeEndAction = chapterSwipeEndAction,
|
||||||
|
chapterSwipeStartAction = chapterSwipeStartAction,
|
||||||
onBackClicked = onBackClicked,
|
onBackClicked = onBackClicked,
|
||||||
onChapterClicked = onChapterClicked,
|
onChapterClicked = onChapterClicked,
|
||||||
onDownloadChapter = onDownloadChapter,
|
onDownloadChapter = onDownloadChapter,
|
||||||
|
@ -157,6 +165,7 @@ fun MangaScreen(
|
||||||
onMultiMarkAsReadClicked = onMultiMarkAsReadClicked,
|
onMultiMarkAsReadClicked = onMultiMarkAsReadClicked,
|
||||||
onMarkPreviousAsReadClicked = onMarkPreviousAsReadClicked,
|
onMarkPreviousAsReadClicked = onMarkPreviousAsReadClicked,
|
||||||
onMultiDeleteClicked = onMultiDeleteClicked,
|
onMultiDeleteClicked = onMultiDeleteClicked,
|
||||||
|
onChapterSwipe = onChapterSwipe,
|
||||||
onChapterSelected = onChapterSelected,
|
onChapterSelected = onChapterSelected,
|
||||||
onAllChapterSelected = onAllChapterSelected,
|
onAllChapterSelected = onAllChapterSelected,
|
||||||
onInvertSelection = onInvertSelection,
|
onInvertSelection = onInvertSelection,
|
||||||
|
@ -166,6 +175,8 @@ fun MangaScreen(
|
||||||
state = state,
|
state = state,
|
||||||
snackbarHostState = snackbarHostState,
|
snackbarHostState = snackbarHostState,
|
||||||
dateRelativeTime = dateRelativeTime,
|
dateRelativeTime = dateRelativeTime,
|
||||||
|
chapterSwipeEndAction = chapterSwipeEndAction,
|
||||||
|
chapterSwipeStartAction = chapterSwipeStartAction,
|
||||||
dateFormat = dateFormat,
|
dateFormat = dateFormat,
|
||||||
onBackClicked = onBackClicked,
|
onBackClicked = onBackClicked,
|
||||||
onChapterClicked = onChapterClicked,
|
onChapterClicked = onChapterClicked,
|
||||||
|
@ -189,6 +200,7 @@ fun MangaScreen(
|
||||||
onMultiMarkAsReadClicked = onMultiMarkAsReadClicked,
|
onMultiMarkAsReadClicked = onMultiMarkAsReadClicked,
|
||||||
onMarkPreviousAsReadClicked = onMarkPreviousAsReadClicked,
|
onMarkPreviousAsReadClicked = onMarkPreviousAsReadClicked,
|
||||||
onMultiDeleteClicked = onMultiDeleteClicked,
|
onMultiDeleteClicked = onMultiDeleteClicked,
|
||||||
|
onChapterSwipe = onChapterSwipe,
|
||||||
onChapterSelected = onChapterSelected,
|
onChapterSelected = onChapterSelected,
|
||||||
onAllChapterSelected = onAllChapterSelected,
|
onAllChapterSelected = onAllChapterSelected,
|
||||||
onInvertSelection = onInvertSelection,
|
onInvertSelection = onInvertSelection,
|
||||||
|
@ -202,6 +214,8 @@ private fun MangaScreenSmallImpl(
|
||||||
snackbarHostState: SnackbarHostState,
|
snackbarHostState: SnackbarHostState,
|
||||||
dateRelativeTime: Int,
|
dateRelativeTime: Int,
|
||||||
dateFormat: DateFormat,
|
dateFormat: DateFormat,
|
||||||
|
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
|
||||||
|
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
|
||||||
onBackClicked: () -> Unit,
|
onBackClicked: () -> Unit,
|
||||||
onChapterClicked: (Chapter) -> Unit,
|
onChapterClicked: (Chapter) -> Unit,
|
||||||
onDownloadChapter: ((List<ChapterItem>, ChapterDownloadAction) -> Unit)?,
|
onDownloadChapter: ((List<ChapterItem>, ChapterDownloadAction) -> Unit)?,
|
||||||
|
@ -234,6 +248,9 @@ private fun MangaScreenSmallImpl(
|
||||||
onMarkPreviousAsReadClicked: (Chapter) -> Unit,
|
onMarkPreviousAsReadClicked: (Chapter) -> Unit,
|
||||||
onMultiDeleteClicked: (List<Chapter>) -> Unit,
|
onMultiDeleteClicked: (List<Chapter>) -> Unit,
|
||||||
|
|
||||||
|
// For chapter swipe
|
||||||
|
onChapterSwipe: (ChapterItem, LibraryPreferences.ChapterSwipeAction) -> Unit,
|
||||||
|
|
||||||
// Chapter selection
|
// Chapter selection
|
||||||
onChapterSelected: (ChapterItem, Boolean, Boolean, Boolean) -> Unit,
|
onChapterSelected: (ChapterItem, Boolean, Boolean, Boolean) -> Unit,
|
||||||
onAllChapterSelected: (Boolean) -> Unit,
|
onAllChapterSelected: (Boolean) -> Unit,
|
||||||
|
@ -404,9 +421,12 @@ private fun MangaScreenSmallImpl(
|
||||||
chapters = chapters,
|
chapters = chapters,
|
||||||
dateRelativeTime = dateRelativeTime,
|
dateRelativeTime = dateRelativeTime,
|
||||||
dateFormat = dateFormat,
|
dateFormat = dateFormat,
|
||||||
|
chapterSwipeEndAction = chapterSwipeEndAction,
|
||||||
|
chapterSwipeStartAction = chapterSwipeStartAction,
|
||||||
onChapterClicked = onChapterClicked,
|
onChapterClicked = onChapterClicked,
|
||||||
onDownloadChapter = onDownloadChapter,
|
onDownloadChapter = onDownloadChapter,
|
||||||
onChapterSelected = onChapterSelected,
|
onChapterSelected = onChapterSelected,
|
||||||
|
onChapterSwipe = onChapterSwipe,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -420,6 +440,8 @@ fun MangaScreenLargeImpl(
|
||||||
snackbarHostState: SnackbarHostState,
|
snackbarHostState: SnackbarHostState,
|
||||||
dateRelativeTime: Int,
|
dateRelativeTime: Int,
|
||||||
dateFormat: DateFormat,
|
dateFormat: DateFormat,
|
||||||
|
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
|
||||||
|
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
|
||||||
onBackClicked: () -> Unit,
|
onBackClicked: () -> Unit,
|
||||||
onChapterClicked: (Chapter) -> Unit,
|
onChapterClicked: (Chapter) -> Unit,
|
||||||
onDownloadChapter: ((List<ChapterItem>, ChapterDownloadAction) -> Unit)?,
|
onDownloadChapter: ((List<ChapterItem>, ChapterDownloadAction) -> Unit)?,
|
||||||
|
@ -452,6 +474,9 @@ fun MangaScreenLargeImpl(
|
||||||
onMarkPreviousAsReadClicked: (Chapter) -> Unit,
|
onMarkPreviousAsReadClicked: (Chapter) -> Unit,
|
||||||
onMultiDeleteClicked: (List<Chapter>) -> Unit,
|
onMultiDeleteClicked: (List<Chapter>) -> Unit,
|
||||||
|
|
||||||
|
// For swipe actions
|
||||||
|
onChapterSwipe: (ChapterItem, LibraryPreferences.ChapterSwipeAction) -> Unit,
|
||||||
|
|
||||||
// Chapter selection
|
// Chapter selection
|
||||||
onChapterSelected: (ChapterItem, Boolean, Boolean, Boolean) -> Unit,
|
onChapterSelected: (ChapterItem, Boolean, Boolean, Boolean) -> Unit,
|
||||||
onAllChapterSelected: (Boolean) -> Unit,
|
onAllChapterSelected: (Boolean) -> Unit,
|
||||||
|
@ -616,9 +641,12 @@ fun MangaScreenLargeImpl(
|
||||||
chapters = chapters,
|
chapters = chapters,
|
||||||
dateRelativeTime = dateRelativeTime,
|
dateRelativeTime = dateRelativeTime,
|
||||||
dateFormat = dateFormat,
|
dateFormat = dateFormat,
|
||||||
|
chapterSwipeEndAction = chapterSwipeEndAction,
|
||||||
|
chapterSwipeStartAction = chapterSwipeStartAction,
|
||||||
onChapterClicked = onChapterClicked,
|
onChapterClicked = onChapterClicked,
|
||||||
onDownloadChapter = onDownloadChapter,
|
onDownloadChapter = onDownloadChapter,
|
||||||
onChapterSelected = onChapterSelected,
|
onChapterSelected = onChapterSelected,
|
||||||
|
onChapterSwipe = onChapterSwipe,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -675,9 +703,12 @@ private fun LazyListScope.sharedChapterItems(
|
||||||
chapters: List<ChapterItem>,
|
chapters: List<ChapterItem>,
|
||||||
dateRelativeTime: Int,
|
dateRelativeTime: Int,
|
||||||
dateFormat: DateFormat,
|
dateFormat: DateFormat,
|
||||||
|
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
|
||||||
|
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
|
||||||
onChapterClicked: (Chapter) -> Unit,
|
onChapterClicked: (Chapter) -> Unit,
|
||||||
onDownloadChapter: ((List<ChapterItem>, ChapterDownloadAction) -> Unit)?,
|
onDownloadChapter: ((List<ChapterItem>, ChapterDownloadAction) -> Unit)?,
|
||||||
onChapterSelected: (ChapterItem, Boolean, Boolean, Boolean) -> Unit,
|
onChapterSelected: (ChapterItem, Boolean, Boolean, Boolean) -> Unit,
|
||||||
|
onChapterSwipe: (ChapterItem, LibraryPreferences.ChapterSwipeAction) -> Unit,
|
||||||
) {
|
) {
|
||||||
items(
|
items(
|
||||||
items = chapters,
|
items = chapters,
|
||||||
|
@ -720,6 +751,8 @@ private fun LazyListScope.sharedChapterItems(
|
||||||
downloadIndicatorEnabled = chapters.fastAll { !it.selected },
|
downloadIndicatorEnabled = chapters.fastAll { !it.selected },
|
||||||
downloadStateProvider = { chapterItem.downloadState },
|
downloadStateProvider = { chapterItem.downloadState },
|
||||||
downloadProgressProvider = { chapterItem.downloadProgress },
|
downloadProgressProvider = { chapterItem.downloadProgress },
|
||||||
|
chapterSwipeEndAction = chapterSwipeEndAction,
|
||||||
|
chapterSwipeStartAction = chapterSwipeStartAction,
|
||||||
onLongClick = {
|
onLongClick = {
|
||||||
onChapterSelected(chapterItem, !chapterItem.selected, true, true)
|
onChapterSelected(chapterItem, !chapterItem.selected, true, true)
|
||||||
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
|
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
|
||||||
|
@ -737,6 +770,9 @@ private fun LazyListScope.sharedChapterItems(
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
},
|
},
|
||||||
|
onChapterSwipe = {
|
||||||
|
onChapterSwipe(chapterItem, it)
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,41 @@
|
||||||
package eu.kanade.presentation.manga.components
|
package eu.kanade.presentation.manga.components
|
||||||
|
|
||||||
|
import androidx.compose.animation.core.animateFloatAsState
|
||||||
|
import androidx.compose.animation.core.tween
|
||||||
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.combinedClickable
|
import androidx.compose.foundation.combinedClickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
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.fillMaxSize
|
||||||
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.sizeIn
|
import androidx.compose.foundation.layout.sizeIn
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material.DismissDirection
|
||||||
|
import androidx.compose.material.DismissValue
|
||||||
|
import androidx.compose.material.SwipeToDismiss
|
||||||
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.BookmarkRemove
|
||||||
import androidx.compose.material.icons.filled.Circle
|
import androidx.compose.material.icons.filled.Circle
|
||||||
|
import androidx.compose.material.icons.filled.Delete
|
||||||
|
import androidx.compose.material.icons.filled.Download
|
||||||
|
import androidx.compose.material.icons.filled.FileDownloadOff
|
||||||
|
import androidx.compose.material.icons.filled.Visibility
|
||||||
|
import androidx.compose.material.icons.filled.VisibilityOff
|
||||||
|
import androidx.compose.material.rememberDismissState
|
||||||
|
import androidx.compose.material3.Card
|
||||||
|
import androidx.compose.material3.CardDefaults
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.LocalContentColor
|
import androidx.compose.material3.LocalContentColor
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.ProvideTextStyle
|
import androidx.compose.material3.ProvideTextStyle
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.contentColorFor
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
@ -23,6 +43,7 @@ import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.alpha
|
import androidx.compose.ui.draw.alpha
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.LocalDensity
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
@ -30,9 +51,11 @@ import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import eu.kanade.tachiyomi.R
|
import eu.kanade.tachiyomi.R
|
||||||
import eu.kanade.tachiyomi.data.download.model.Download
|
import eu.kanade.tachiyomi.data.download.model.Download
|
||||||
|
import tachiyomi.domain.library.service.LibraryPreferences
|
||||||
import tachiyomi.presentation.core.components.material.ReadItemAlpha
|
import tachiyomi.presentation.core.components.material.ReadItemAlpha
|
||||||
import tachiyomi.presentation.core.components.material.SecondaryItemAlpha
|
import tachiyomi.presentation.core.components.material.SecondaryItemAlpha
|
||||||
import tachiyomi.presentation.core.util.selectedBackground
|
import tachiyomi.presentation.core.util.selectedBackground
|
||||||
|
import kotlin.math.min
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MangaChapterListItem(
|
fun MangaChapterListItem(
|
||||||
|
@ -47,16 +70,132 @@ fun MangaChapterListItem(
|
||||||
downloadIndicatorEnabled: Boolean,
|
downloadIndicatorEnabled: Boolean,
|
||||||
downloadStateProvider: () -> Download.State,
|
downloadStateProvider: () -> Download.State,
|
||||||
downloadProgressProvider: () -> Int,
|
downloadProgressProvider: () -> Int,
|
||||||
|
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
|
||||||
|
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
|
||||||
onLongClick: () -> Unit,
|
onLongClick: () -> Unit,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
onDownloadClick: ((ChapterDownloadAction) -> Unit)?,
|
onDownloadClick: ((ChapterDownloadAction) -> Unit)?,
|
||||||
|
onChapterSwipe: (LibraryPreferences.ChapterSwipeAction) -> Unit,
|
||||||
) {
|
) {
|
||||||
val textAlpha = if (read) ReadItemAlpha else 1f
|
val textAlpha = if (read) ReadItemAlpha else 1f
|
||||||
val textSubtitleAlpha = if (read) ReadItemAlpha else SecondaryItemAlpha
|
val textSubtitleAlpha = if (read) ReadItemAlpha else SecondaryItemAlpha
|
||||||
|
|
||||||
|
val chapterSwipeStartEnabled = chapterSwipeStartAction != LibraryPreferences.ChapterSwipeAction.Disabled
|
||||||
|
val chapterSwipeEndEnabled = chapterSwipeEndAction != LibraryPreferences.ChapterSwipeAction.Disabled
|
||||||
|
|
||||||
|
val dismissState = rememberDismissState()
|
||||||
|
val dismissDirections = remember { mutableSetOf<DismissDirection>() }
|
||||||
|
var lastDismissDirection: DismissDirection? by remember { mutableStateOf(null) }
|
||||||
|
if (lastDismissDirection == null) {
|
||||||
|
if (chapterSwipeStartEnabled) {
|
||||||
|
dismissDirections.add(DismissDirection.EndToStart)
|
||||||
|
}
|
||||||
|
if (chapterSwipeEndEnabled) {
|
||||||
|
dismissDirections.add(DismissDirection.StartToEnd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val animateDismissContentAlpha by animateFloatAsState(
|
||||||
|
label = "animateDismissContentAlpha",
|
||||||
|
targetValue = if (lastDismissDirection != null) 1f else 0f,
|
||||||
|
animationSpec = tween(durationMillis = if (lastDismissDirection != null) 500 else 0),
|
||||||
|
finishedListener = {
|
||||||
|
lastDismissDirection = null
|
||||||
|
},
|
||||||
|
)
|
||||||
|
LaunchedEffect(dismissState.currentValue) {
|
||||||
|
when (dismissState.currentValue) {
|
||||||
|
DismissValue.DismissedToEnd -> {
|
||||||
|
lastDismissDirection = DismissDirection.StartToEnd
|
||||||
|
val dismissDirectionsCopy = dismissDirections.toSet()
|
||||||
|
dismissDirections.clear()
|
||||||
|
onChapterSwipe(chapterSwipeEndAction)
|
||||||
|
dismissState.snapTo(DismissValue.Default)
|
||||||
|
dismissDirections.addAll(dismissDirectionsCopy)
|
||||||
|
}
|
||||||
|
DismissValue.DismissedToStart -> {
|
||||||
|
lastDismissDirection = DismissDirection.EndToStart
|
||||||
|
val dismissDirectionsCopy = dismissDirections.toSet()
|
||||||
|
dismissDirections.clear()
|
||||||
|
onChapterSwipe(chapterSwipeStartAction)
|
||||||
|
dismissState.snapTo(DismissValue.Default)
|
||||||
|
dismissDirections.addAll(dismissDirectionsCopy)
|
||||||
|
}
|
||||||
|
DismissValue.Default -> { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SwipeToDismiss(
|
||||||
|
state = dismissState,
|
||||||
|
directions = dismissDirections,
|
||||||
|
background = {
|
||||||
|
val backgroundColor = if (chapterSwipeEndEnabled && (dismissState.dismissDirection == DismissDirection.StartToEnd || lastDismissDirection == DismissDirection.StartToEnd)) {
|
||||||
|
MaterialTheme.colorScheme.primary
|
||||||
|
} else if (chapterSwipeStartEnabled && (dismissState.dismissDirection == DismissDirection.EndToStart || lastDismissDirection == DismissDirection.EndToStart)) {
|
||||||
|
MaterialTheme.colorScheme.primary
|
||||||
|
} else {
|
||||||
|
Color.Unspecified
|
||||||
|
}
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.background(backgroundColor),
|
||||||
|
) {
|
||||||
|
if (dismissState.dismissDirection in dismissDirections) {
|
||||||
|
val downloadState = downloadStateProvider()
|
||||||
|
SwipeBackgroundIcon(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(start = 16.dp)
|
||||||
|
.align(Alignment.CenterStart)
|
||||||
|
.alpha(
|
||||||
|
if (dismissState.dismissDirection == DismissDirection.StartToEnd) 1f else 0f,
|
||||||
|
),
|
||||||
|
tint = contentColorFor(backgroundColor),
|
||||||
|
swipeAction = chapterSwipeEndAction,
|
||||||
|
read = read,
|
||||||
|
bookmark = bookmark,
|
||||||
|
downloadState = downloadState,
|
||||||
|
)
|
||||||
|
SwipeBackgroundIcon(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(end = 16.dp)
|
||||||
|
.align(Alignment.CenterEnd)
|
||||||
|
.alpha(
|
||||||
|
if (dismissState.dismissDirection == DismissDirection.EndToStart) 1f else 0f,
|
||||||
|
),
|
||||||
|
tint = contentColorFor(backgroundColor),
|
||||||
|
swipeAction = chapterSwipeStartAction,
|
||||||
|
read = read,
|
||||||
|
bookmark = bookmark,
|
||||||
|
downloadState = downloadState,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissContent = {
|
||||||
|
val animateCornerRatio = if (dismissState.offset.value != 0f) {
|
||||||
|
min(
|
||||||
|
dismissState.progress.fraction / .075f,
|
||||||
|
1f,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
0f
|
||||||
|
}
|
||||||
|
val animateCornerShape = (8f * animateCornerRatio).dp
|
||||||
|
val dismissContentAlpha =
|
||||||
|
if (lastDismissDirection != null) animateDismissContentAlpha else 1f
|
||||||
|
Card(
|
||||||
|
modifier = modifier,
|
||||||
|
colors = CardDefaults.elevatedCardColors(
|
||||||
|
containerColor = Color.Transparent,
|
||||||
|
),
|
||||||
|
shape = RoundedCornerShape(animateCornerShape),
|
||||||
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = modifier
|
modifier = Modifier
|
||||||
|
.background(
|
||||||
|
MaterialTheme.colorScheme.background.copy(dismissContentAlpha),
|
||||||
|
)
|
||||||
.selectedBackground(selected)
|
.selectedBackground(selected)
|
||||||
|
.alpha(dismissContentAlpha)
|
||||||
.combinedClickable(
|
.combinedClickable(
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
onLongClick = onLongClick,
|
onLongClick = onLongClick,
|
||||||
|
@ -146,4 +285,56 @@ fun MangaChapterListItem(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun SwipeBackgroundIcon(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
tint: Color,
|
||||||
|
swipeAction: LibraryPreferences.ChapterSwipeAction,
|
||||||
|
read: Boolean,
|
||||||
|
bookmark: Boolean,
|
||||||
|
downloadState: Download.State,
|
||||||
|
) {
|
||||||
|
val imageVector = when (swipeAction) {
|
||||||
|
LibraryPreferences.ChapterSwipeAction.ToggleRead -> {
|
||||||
|
if (!read) {
|
||||||
|
Icons.Default.Visibility
|
||||||
|
} else {
|
||||||
|
Icons.Default.VisibilityOff
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LibraryPreferences.ChapterSwipeAction.ToggleBookmark -> {
|
||||||
|
if (!bookmark) {
|
||||||
|
Icons.Default.Bookmark
|
||||||
|
} else {
|
||||||
|
Icons.Default.BookmarkRemove
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LibraryPreferences.ChapterSwipeAction.Download -> {
|
||||||
|
when (downloadState) {
|
||||||
|
Download.State.NOT_DOWNLOADED,
|
||||||
|
Download.State.ERROR,
|
||||||
|
-> { Icons.Default.Download }
|
||||||
|
Download.State.QUEUE,
|
||||||
|
Download.State.DOWNLOADING,
|
||||||
|
-> { Icons.Default.FileDownloadOff }
|
||||||
|
Download.State.DOWNLOADED -> { Icons.Default.Delete }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LibraryPreferences.ChapterSwipeAction.Disabled -> {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
imageVector?.let {
|
||||||
|
Icon(
|
||||||
|
modifier = modifier,
|
||||||
|
imageVector = imageVector,
|
||||||
|
tint = tint,
|
||||||
|
contentDescription = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,6 +58,7 @@ object SettingsLibraryScreen : SearchableSettings {
|
||||||
return mutableListOf(
|
return mutableListOf(
|
||||||
getCategoriesGroup(LocalNavigator.currentOrThrow, allCategories, libraryPreferences),
|
getCategoriesGroup(LocalNavigator.currentOrThrow, allCategories, libraryPreferences),
|
||||||
getGlobalUpdateGroup(allCategories, libraryPreferences),
|
getGlobalUpdateGroup(allCategories, libraryPreferences),
|
||||||
|
getChapterSwipeActionsGroup(libraryPreferences),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,4 +217,38 @@ object SettingsLibraryScreen : SearchableSettings {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun getChapterSwipeActionsGroup(
|
||||||
|
libraryPreferences: LibraryPreferences,
|
||||||
|
): Preference.PreferenceGroup {
|
||||||
|
val chapterSwipeEndActionPref = libraryPreferences.swipeEndAction()
|
||||||
|
val chapterSwipeStartActionPref = libraryPreferences.swipeStartAction()
|
||||||
|
|
||||||
|
return Preference.PreferenceGroup(
|
||||||
|
title = stringResource(R.string.pref_chapter_swipe),
|
||||||
|
preferenceItems = listOf(
|
||||||
|
Preference.PreferenceItem.ListPreference(
|
||||||
|
pref = chapterSwipeEndActionPref,
|
||||||
|
title = stringResource(R.string.pref_chapter_swipe_end),
|
||||||
|
entries = mapOf(
|
||||||
|
LibraryPreferences.ChapterSwipeAction.Disabled to stringResource(R.string.action_disable),
|
||||||
|
LibraryPreferences.ChapterSwipeAction.ToggleBookmark to stringResource(R.string.action_bookmark),
|
||||||
|
LibraryPreferences.ChapterSwipeAction.ToggleRead to stringResource(R.string.action_mark_as_read),
|
||||||
|
LibraryPreferences.ChapterSwipeAction.Download to stringResource(R.string.action_download),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Preference.PreferenceItem.ListPreference(
|
||||||
|
pref = chapterSwipeStartActionPref,
|
||||||
|
title = stringResource(R.string.pref_chapter_swipe_start),
|
||||||
|
entries = mapOf(
|
||||||
|
LibraryPreferences.ChapterSwipeAction.Disabled to stringResource(R.string.action_disable),
|
||||||
|
LibraryPreferences.ChapterSwipeAction.ToggleBookmark to stringResource(R.string.action_bookmark),
|
||||||
|
LibraryPreferences.ChapterSwipeAction.ToggleRead to stringResource(R.string.action_mark_as_read),
|
||||||
|
LibraryPreferences.ChapterSwipeAction.Download to stringResource(R.string.action_download),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -101,6 +101,8 @@ class MangaScreen(
|
||||||
dateRelativeTime = screenModel.relativeTime,
|
dateRelativeTime = screenModel.relativeTime,
|
||||||
dateFormat = screenModel.dateFormat,
|
dateFormat = screenModel.dateFormat,
|
||||||
isTabletUi = isTabletUi(),
|
isTabletUi = isTabletUi(),
|
||||||
|
chapterSwipeEndAction = screenModel.chapterSwipeEndAction,
|
||||||
|
chapterSwipeStartAction = screenModel.chapterSwipeStartAction,
|
||||||
onBackClicked = navigator::pop,
|
onBackClicked = navigator::pop,
|
||||||
onChapterClicked = { openChapter(context, it) },
|
onChapterClicked = { openChapter(context, it) },
|
||||||
onDownloadChapter = screenModel::runChapterDownloadActions.takeIf { !successState.source.isLocalOrStub() },
|
onDownloadChapter = screenModel::runChapterDownloadActions.takeIf { !successState.source.isLocalOrStub() },
|
||||||
|
@ -125,6 +127,7 @@ class MangaScreen(
|
||||||
onMultiMarkAsReadClicked = screenModel::markChaptersRead,
|
onMultiMarkAsReadClicked = screenModel::markChaptersRead,
|
||||||
onMarkPreviousAsReadClicked = screenModel::markPreviousChapterRead,
|
onMarkPreviousAsReadClicked = screenModel::markPreviousChapterRead,
|
||||||
onMultiDeleteClicked = screenModel::showDeleteChapterDialog,
|
onMultiDeleteClicked = screenModel::showDeleteChapterDialog,
|
||||||
|
onChapterSwipe = screenModel::chapterSwipe,
|
||||||
onChapterSelected = screenModel::toggleSelection,
|
onChapterSelected = screenModel::toggleSelection,
|
||||||
onAllChapterSelected = screenModel::toggleAllSelection,
|
onAllChapterSelected = screenModel::toggleAllSelection,
|
||||||
onInvertSelection = screenModel::invertSelection,
|
onInvertSelection = screenModel::invertSelection,
|
||||||
|
|
|
@ -121,6 +121,9 @@ class MangaInfoScreenModel(
|
||||||
private val filteredChapters: Sequence<ChapterItem>?
|
private val filteredChapters: Sequence<ChapterItem>?
|
||||||
get() = successState?.processedChapters
|
get() = successState?.processedChapters
|
||||||
|
|
||||||
|
val chapterSwipeEndAction = libraryPreferences.swipeEndAction().get()
|
||||||
|
val chapterSwipeStartAction = libraryPreferences.swipeStartAction().get()
|
||||||
|
|
||||||
val relativeTime by uiPreferences.relativeTime().asState(coroutineScope)
|
val relativeTime by uiPreferences.relativeTime().asState(coroutineScope)
|
||||||
val dateFormat by mutableStateOf(UiPreferences.dateFormat(uiPreferences.dateFormat().get()))
|
val dateFormat by mutableStateOf(UiPreferences.dateFormat(uiPreferences.dateFormat().get()))
|
||||||
private val skipFiltered by readerPreferences.skipFiltered().asState(coroutineScope)
|
private val skipFiltered by readerPreferences.skipFiltered().asState(coroutineScope)
|
||||||
|
@ -523,6 +526,49 @@ class MangaInfoScreenModel(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws IllegalStateException if the swipe action is [LibraryPreferences.ChapterSwipeAction.Disabled]
|
||||||
|
*/
|
||||||
|
fun chapterSwipe(chapterItem: ChapterItem, swipeAction: LibraryPreferences.ChapterSwipeAction) {
|
||||||
|
coroutineScope.launch {
|
||||||
|
executeChapterSwipeAction(chapterItem, swipeAction)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws IllegalStateException if the swipe action is [LibraryPreferences.ChapterSwipeAction.Disabled]
|
||||||
|
*/
|
||||||
|
private fun executeChapterSwipeAction(
|
||||||
|
chapterItem: ChapterItem,
|
||||||
|
swipeAction: LibraryPreferences.ChapterSwipeAction,
|
||||||
|
) {
|
||||||
|
val chapter = chapterItem.chapter
|
||||||
|
when (swipeAction) {
|
||||||
|
LibraryPreferences.ChapterSwipeAction.ToggleRead -> {
|
||||||
|
markChaptersRead(listOf(chapter), !chapter.read)
|
||||||
|
}
|
||||||
|
LibraryPreferences.ChapterSwipeAction.ToggleBookmark -> {
|
||||||
|
bookmarkChapters(listOf(chapter), !chapter.bookmark)
|
||||||
|
}
|
||||||
|
LibraryPreferences.ChapterSwipeAction.Download -> {
|
||||||
|
val downloadAction: ChapterDownloadAction = when (chapterItem.downloadState) {
|
||||||
|
Download.State.ERROR,
|
||||||
|
Download.State.NOT_DOWNLOADED,
|
||||||
|
-> ChapterDownloadAction.START_NOW
|
||||||
|
Download.State.QUEUE,
|
||||||
|
Download.State.DOWNLOADING,
|
||||||
|
-> ChapterDownloadAction.CANCEL
|
||||||
|
Download.State.DOWNLOADED -> ChapterDownloadAction.DELETE
|
||||||
|
}
|
||||||
|
runChapterDownloadActions(
|
||||||
|
items = listOf(chapterItem),
|
||||||
|
action = downloadAction,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
LibraryPreferences.ChapterSwipeAction.Disabled -> throw IllegalStateException()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the next unread chapter or null if everything is read.
|
* Returns the next unread chapter or null if everything is read.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -118,6 +118,21 @@ class LibraryPreferences(
|
||||||
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
|
// region Swipe Actions
|
||||||
|
|
||||||
|
fun swipeEndAction() = preferenceStore.getEnum("pref_chapter_swipe_end_action", ChapterSwipeAction.ToggleBookmark)
|
||||||
|
|
||||||
|
fun swipeStartAction() = preferenceStore.getEnum("pref_chapter_swipe_start_action", ChapterSwipeAction.ToggleRead)
|
||||||
|
|
||||||
|
// endregion
|
||||||
|
|
||||||
|
enum class ChapterSwipeAction {
|
||||||
|
ToggleRead,
|
||||||
|
ToggleBookmark,
|
||||||
|
Download,
|
||||||
|
Disabled,
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val DEVICE_ONLY_ON_WIFI = "wifi"
|
const val DEVICE_ONLY_ON_WIFI = "wifi"
|
||||||
const val DEVICE_NETWORK_NOT_METERED = "network_not_metered"
|
const val DEVICE_NETWORK_NOT_METERED = "network_not_metered"
|
||||||
|
|
|
@ -167,7 +167,7 @@
|
||||||
|
|
||||||
<string name="pref_general_summary">App language, notifications</string>
|
<string name="pref_general_summary">App language, notifications</string>
|
||||||
<string name="pref_appearance_summary">Theme, date & time format</string>
|
<string name="pref_appearance_summary">Theme, date & time format</string>
|
||||||
<string name="pref_library_summary">Categories, global update</string>
|
<string name="pref_library_summary">Categories, global update, chapter swipe</string>
|
||||||
<string name="pref_reader_summary">Reading mode, display, navigation</string>
|
<string name="pref_reader_summary">Reading mode, display, navigation</string>
|
||||||
<string name="pref_downloads_summary">Automatic download, download ahead</string>
|
<string name="pref_downloads_summary">Automatic download, download ahead</string>
|
||||||
<string name="pref_tracking_summary">One-way progress sync, enhanced sync</string>
|
<string name="pref_tracking_summary">One-way progress sync, enhanced sync</string>
|
||||||
|
@ -273,6 +273,10 @@
|
||||||
<string name="include">Include: %s</string>
|
<string name="include">Include: %s</string>
|
||||||
<string name="exclude">Exclude: %s</string>
|
<string name="exclude">Exclude: %s</string>
|
||||||
|
|
||||||
|
<string name="pref_chapter_swipe">Chapter swipe</string>
|
||||||
|
<string name="pref_chapter_swipe_end">Swipe to end action</string>
|
||||||
|
<string name="pref_chapter_swipe_start">Swipe to start action</string>
|
||||||
|
|
||||||
<!-- Extension section -->
|
<!-- Extension section -->
|
||||||
<string name="multi_lang">Multi</string>
|
<string name="multi_lang">Multi</string>
|
||||||
<string name="ext_updates_pending">Updates pending</string>
|
<string name="ext_updates_pending">Updates pending</string>
|
||||||
|
|
Reference in a new issue