2022-07-18 08:17:40 +06:00
|
|
|
package eu.kanade.presentation.components
|
2022-06-25 22:03:48 +07:00
|
|
|
|
|
|
|
import androidx.compose.animation.AnimatedVisibility
|
|
|
|
import androidx.compose.animation.core.animateFloatAsState
|
2022-12-03 10:35:30 +07:00
|
|
|
import androidx.compose.animation.core.tween
|
2022-06-25 22:03:48 +07:00
|
|
|
import androidx.compose.animation.expandVertically
|
|
|
|
import androidx.compose.animation.fadeIn
|
|
|
|
import androidx.compose.animation.fadeOut
|
|
|
|
import androidx.compose.animation.shrinkVertically
|
|
|
|
import androidx.compose.foundation.combinedClickable
|
|
|
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
|
|
|
import androidx.compose.foundation.layout.Arrangement
|
|
|
|
import androidx.compose.foundation.layout.Column
|
|
|
|
import androidx.compose.foundation.layout.Row
|
|
|
|
import androidx.compose.foundation.layout.RowScope
|
2022-09-28 09:20:10 +07:00
|
|
|
import androidx.compose.foundation.layout.WindowInsets
|
|
|
|
import androidx.compose.foundation.layout.WindowInsetsSides
|
|
|
|
import androidx.compose.foundation.layout.asPaddingValues
|
|
|
|
import androidx.compose.foundation.layout.navigationBars
|
|
|
|
import androidx.compose.foundation.layout.only
|
2022-06-25 22:03:48 +07:00
|
|
|
import androidx.compose.foundation.layout.padding
|
|
|
|
import androidx.compose.foundation.layout.size
|
2022-12-03 10:35:30 +07:00
|
|
|
import androidx.compose.foundation.layout.windowInsetsPadding
|
2022-07-27 20:12:01 +07:00
|
|
|
import androidx.compose.foundation.shape.ZeroCornerSize
|
2022-06-25 22:03:48 +07:00
|
|
|
import androidx.compose.material.icons.Icons
|
2022-10-29 23:43:51 +08:00
|
|
|
import androidx.compose.material.icons.outlined.BookmarkAdd
|
|
|
|
import androidx.compose.material.icons.outlined.BookmarkRemove
|
2022-06-25 11:20:34 -04:00
|
|
|
import androidx.compose.material.icons.outlined.Delete
|
2022-10-29 23:43:51 +08:00
|
|
|
import androidx.compose.material.icons.outlined.DoneAll
|
2022-06-25 11:20:34 -04:00
|
|
|
import androidx.compose.material.icons.outlined.Download
|
2022-08-06 16:23:13 -04:00
|
|
|
import androidx.compose.material.icons.outlined.Label
|
2022-10-29 23:43:51 +08:00
|
|
|
import androidx.compose.material.icons.outlined.RemoveDone
|
2022-06-25 22:03:48 +07:00
|
|
|
import androidx.compose.material.ripple.rememberRipple
|
|
|
|
import androidx.compose.material3.Icon
|
|
|
|
import androidx.compose.material3.MaterialTheme
|
|
|
|
import androidx.compose.material3.Surface
|
|
|
|
import androidx.compose.material3.Text
|
|
|
|
import androidx.compose.runtime.Composable
|
|
|
|
import androidx.compose.runtime.getValue
|
|
|
|
import androidx.compose.runtime.mutableStateListOf
|
2022-10-31 13:27:48 +11:00
|
|
|
import androidx.compose.runtime.mutableStateOf
|
2022-06-25 22:03:48 +07:00
|
|
|
import androidx.compose.runtime.remember
|
|
|
|
import androidx.compose.runtime.rememberCoroutineScope
|
2022-10-31 13:27:48 +11:00
|
|
|
import androidx.compose.runtime.setValue
|
2022-06-25 22:03:48 +07:00
|
|
|
import androidx.compose.ui.Alignment
|
|
|
|
import androidx.compose.ui.Modifier
|
|
|
|
import androidx.compose.ui.graphics.vector.ImageVector
|
|
|
|
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
|
|
|
import androidx.compose.ui.platform.LocalHapticFeedback
|
|
|
|
import androidx.compose.ui.res.stringResource
|
|
|
|
import androidx.compose.ui.res.vectorResource
|
|
|
|
import androidx.compose.ui.text.style.TextOverflow
|
|
|
|
import androidx.compose.ui.unit.dp
|
2022-10-31 13:27:48 +11:00
|
|
|
import eu.kanade.presentation.manga.DownloadAction
|
2022-06-25 22:03:48 +07:00
|
|
|
import eu.kanade.tachiyomi.R
|
|
|
|
import kotlinx.coroutines.Job
|
|
|
|
import kotlinx.coroutines.delay
|
|
|
|
import kotlinx.coroutines.isActive
|
|
|
|
import kotlinx.coroutines.launch
|
2022-10-22 19:57:55 -04:00
|
|
|
import kotlin.time.Duration.Companion.seconds
|
2022-06-25 22:03:48 +07:00
|
|
|
|
|
|
|
@Composable
|
|
|
|
fun MangaBottomActionMenu(
|
|
|
|
visible: Boolean,
|
|
|
|
modifier: Modifier = Modifier,
|
2022-07-18 08:17:40 +06:00
|
|
|
onBookmarkClicked: (() -> Unit)? = null,
|
|
|
|
onRemoveBookmarkClicked: (() -> Unit)? = null,
|
|
|
|
onMarkAsReadClicked: (() -> Unit)? = null,
|
|
|
|
onMarkAsUnreadClicked: (() -> Unit)? = null,
|
|
|
|
onMarkPreviousAsReadClicked: (() -> Unit)? = null,
|
|
|
|
onDownloadClicked: (() -> Unit)? = null,
|
|
|
|
onDeleteClicked: (() -> Unit)? = null,
|
2022-06-25 22:03:48 +07:00
|
|
|
) {
|
|
|
|
AnimatedVisibility(
|
|
|
|
visible = visible,
|
|
|
|
enter = expandVertically(expandFrom = Alignment.Bottom),
|
|
|
|
exit = shrinkVertically(shrinkTowards = Alignment.Bottom),
|
|
|
|
) {
|
|
|
|
val scope = rememberCoroutineScope()
|
|
|
|
Surface(
|
|
|
|
modifier = modifier,
|
2022-07-27 20:12:01 +07:00
|
|
|
shape = MaterialTheme.shapes.large.copy(bottomEnd = ZeroCornerSize, bottomStart = ZeroCornerSize),
|
2022-06-25 22:03:48 +07:00
|
|
|
tonalElevation = 3.dp,
|
|
|
|
) {
|
|
|
|
val haptic = LocalHapticFeedback.current
|
|
|
|
val confirm = remember { mutableStateListOf(false, false, false, false, false, false, false) }
|
|
|
|
var resetJob: Job? = remember { null }
|
|
|
|
val onLongClickItem: (Int) -> Unit = { toConfirmIndex ->
|
|
|
|
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
|
|
|
|
(0 until 7).forEach { i -> confirm[i] = i == toConfirmIndex }
|
|
|
|
resetJob?.cancel()
|
|
|
|
resetJob = scope.launch {
|
2022-10-22 19:57:55 -04:00
|
|
|
delay(1.seconds)
|
2022-06-25 22:03:48 +07:00
|
|
|
if (isActive) confirm[toConfirmIndex] = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Row(
|
|
|
|
modifier = Modifier
|
2022-12-03 10:35:30 +07:00
|
|
|
.padding(
|
|
|
|
WindowInsets.navigationBars
|
|
|
|
.only(WindowInsetsSides.Bottom)
|
|
|
|
.asPaddingValues(),
|
|
|
|
)
|
2022-06-25 22:03:48 +07:00
|
|
|
.padding(horizontal = 8.dp, vertical = 12.dp),
|
|
|
|
) {
|
|
|
|
if (onBookmarkClicked != null) {
|
|
|
|
Button(
|
2022-06-25 11:20:34 -04:00
|
|
|
title = stringResource(R.string.action_bookmark),
|
2022-10-29 23:43:51 +08:00
|
|
|
icon = Icons.Outlined.BookmarkAdd,
|
2022-06-25 22:03:48 +07:00
|
|
|
toConfirm = confirm[0],
|
|
|
|
onLongClick = { onLongClickItem(0) },
|
|
|
|
onClick = onBookmarkClicked,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
if (onRemoveBookmarkClicked != null) {
|
|
|
|
Button(
|
2022-06-25 11:20:34 -04:00
|
|
|
title = stringResource(R.string.action_remove_bookmark),
|
2022-10-29 23:43:51 +08:00
|
|
|
icon = Icons.Outlined.BookmarkRemove,
|
2022-06-25 22:03:48 +07:00
|
|
|
toConfirm = confirm[1],
|
|
|
|
onLongClick = { onLongClickItem(1) },
|
|
|
|
onClick = onRemoveBookmarkClicked,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
if (onMarkAsReadClicked != null) {
|
|
|
|
Button(
|
2022-06-25 11:20:34 -04:00
|
|
|
title = stringResource(R.string.action_mark_as_read),
|
2022-10-29 23:43:51 +08:00
|
|
|
icon = Icons.Outlined.DoneAll,
|
2022-06-25 22:03:48 +07:00
|
|
|
toConfirm = confirm[2],
|
|
|
|
onLongClick = { onLongClickItem(2) },
|
|
|
|
onClick = onMarkAsReadClicked,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
if (onMarkAsUnreadClicked != null) {
|
|
|
|
Button(
|
2022-06-25 11:20:34 -04:00
|
|
|
title = stringResource(R.string.action_mark_as_unread),
|
2022-10-29 23:43:51 +08:00
|
|
|
icon = Icons.Outlined.RemoveDone,
|
2022-06-25 22:03:48 +07:00
|
|
|
toConfirm = confirm[3],
|
|
|
|
onLongClick = { onLongClickItem(3) },
|
|
|
|
onClick = onMarkAsUnreadClicked,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
if (onMarkPreviousAsReadClicked != null) {
|
|
|
|
Button(
|
2022-06-25 11:20:34 -04:00
|
|
|
title = stringResource(R.string.action_mark_previous_as_read),
|
2022-06-25 22:03:48 +07:00
|
|
|
icon = ImageVector.vectorResource(id = R.drawable.ic_done_prev_24dp),
|
|
|
|
toConfirm = confirm[4],
|
|
|
|
onLongClick = { onLongClickItem(4) },
|
|
|
|
onClick = onMarkPreviousAsReadClicked,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
if (onDownloadClicked != null) {
|
|
|
|
Button(
|
2022-06-25 11:20:34 -04:00
|
|
|
title = stringResource(R.string.action_download),
|
|
|
|
icon = Icons.Outlined.Download,
|
2022-06-25 22:03:48 +07:00
|
|
|
toConfirm = confirm[5],
|
|
|
|
onLongClick = { onLongClickItem(5) },
|
|
|
|
onClick = onDownloadClicked,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
if (onDeleteClicked != null) {
|
|
|
|
Button(
|
2022-06-25 11:20:34 -04:00
|
|
|
title = stringResource(R.string.action_delete),
|
|
|
|
icon = Icons.Outlined.Delete,
|
2022-06-25 22:03:48 +07:00
|
|
|
toConfirm = confirm[6],
|
|
|
|
onLongClick = { onLongClickItem(6) },
|
|
|
|
onClick = onDeleteClicked,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Composable
|
|
|
|
private fun RowScope.Button(
|
|
|
|
title: String,
|
|
|
|
icon: ImageVector,
|
|
|
|
toConfirm: Boolean,
|
|
|
|
onLongClick: () -> Unit,
|
|
|
|
onClick: () -> Unit,
|
2022-10-30 22:56:07 -04:00
|
|
|
content: (@Composable () -> Unit)? = null,
|
2022-06-25 22:03:48 +07:00
|
|
|
) {
|
|
|
|
val animatedWeight by animateFloatAsState(if (toConfirm) 2f else 1f)
|
|
|
|
Column(
|
|
|
|
modifier = Modifier
|
|
|
|
.size(48.dp)
|
|
|
|
.weight(animatedWeight)
|
|
|
|
.combinedClickable(
|
|
|
|
interactionSource = remember { MutableInteractionSource() },
|
|
|
|
indication = rememberRipple(bounded = false),
|
|
|
|
onLongClick = onLongClick,
|
|
|
|
onClick = onClick,
|
|
|
|
),
|
|
|
|
verticalArrangement = Arrangement.Center,
|
|
|
|
horizontalAlignment = Alignment.CenterHorizontally,
|
|
|
|
) {
|
|
|
|
Icon(
|
|
|
|
imageVector = icon,
|
|
|
|
contentDescription = title,
|
|
|
|
)
|
|
|
|
AnimatedVisibility(
|
|
|
|
visible = toConfirm,
|
|
|
|
enter = expandVertically(expandFrom = Alignment.Top) + fadeIn(),
|
|
|
|
exit = shrinkVertically(shrinkTowards = Alignment.Top) + fadeOut(),
|
|
|
|
) {
|
|
|
|
Text(
|
|
|
|
text = title,
|
|
|
|
overflow = TextOverflow.Visible,
|
|
|
|
maxLines = 1,
|
|
|
|
style = MaterialTheme.typography.labelSmall,
|
|
|
|
)
|
|
|
|
}
|
2022-10-30 22:56:07 -04:00
|
|
|
content?.invoke()
|
2022-06-25 22:03:48 +07:00
|
|
|
}
|
|
|
|
}
|
2022-07-23 01:05:50 +02:00
|
|
|
|
|
|
|
@Composable
|
|
|
|
fun LibraryBottomActionMenu(
|
|
|
|
visible: Boolean,
|
|
|
|
modifier: Modifier = Modifier,
|
2022-12-03 10:35:30 +07:00
|
|
|
onChangeCategoryClicked: () -> Unit,
|
|
|
|
onMarkAsReadClicked: () -> Unit,
|
|
|
|
onMarkAsUnreadClicked: () -> Unit,
|
2022-10-31 13:27:48 +11:00
|
|
|
onDownloadClicked: ((DownloadAction) -> Unit)?,
|
2022-12-03 10:35:30 +07:00
|
|
|
onDeleteClicked: () -> Unit,
|
2022-07-23 01:05:50 +02:00
|
|
|
) {
|
|
|
|
AnimatedVisibility(
|
|
|
|
visible = visible,
|
2022-12-03 10:35:30 +07:00
|
|
|
enter = expandVertically(animationSpec = tween(delayMillis = 300)),
|
|
|
|
exit = shrinkVertically(animationSpec = tween()),
|
2022-07-23 01:05:50 +02:00
|
|
|
) {
|
|
|
|
val scope = rememberCoroutineScope()
|
|
|
|
Surface(
|
|
|
|
modifier = modifier,
|
2022-07-27 20:12:01 +07:00
|
|
|
shape = MaterialTheme.shapes.large.copy(bottomEnd = ZeroCornerSize, bottomStart = ZeroCornerSize),
|
2022-07-23 01:05:50 +02:00
|
|
|
tonalElevation = 3.dp,
|
|
|
|
) {
|
|
|
|
val haptic = LocalHapticFeedback.current
|
|
|
|
val confirm = remember { mutableStateListOf(false, false, false, false, false) }
|
|
|
|
var resetJob: Job? = remember { null }
|
|
|
|
val onLongClickItem: (Int) -> Unit = { toConfirmIndex ->
|
|
|
|
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
|
|
|
|
(0 until 5).forEach { i -> confirm[i] = i == toConfirmIndex }
|
|
|
|
resetJob?.cancel()
|
|
|
|
resetJob = scope.launch {
|
2022-10-22 19:57:55 -04:00
|
|
|
delay(1.seconds)
|
2022-07-23 01:05:50 +02:00
|
|
|
if (isActive) confirm[toConfirmIndex] = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Row(
|
|
|
|
modifier = Modifier
|
2022-12-03 10:35:30 +07:00
|
|
|
.windowInsetsPadding(
|
|
|
|
WindowInsets.navigationBars
|
|
|
|
.only(WindowInsetsSides.Bottom),
|
|
|
|
)
|
2022-07-23 01:05:50 +02:00
|
|
|
.padding(horizontal = 8.dp, vertical = 12.dp),
|
|
|
|
) {
|
2022-12-03 10:35:30 +07:00
|
|
|
Button(
|
|
|
|
title = stringResource(R.string.action_move_category),
|
|
|
|
icon = Icons.Outlined.Label,
|
|
|
|
toConfirm = confirm[0],
|
|
|
|
onLongClick = { onLongClickItem(0) },
|
|
|
|
onClick = onChangeCategoryClicked,
|
|
|
|
)
|
|
|
|
Button(
|
|
|
|
title = stringResource(R.string.action_mark_as_read),
|
|
|
|
icon = Icons.Outlined.DoneAll,
|
|
|
|
toConfirm = confirm[1],
|
|
|
|
onLongClick = { onLongClickItem(1) },
|
|
|
|
onClick = onMarkAsReadClicked,
|
|
|
|
)
|
|
|
|
Button(
|
|
|
|
title = stringResource(R.string.action_mark_as_unread),
|
|
|
|
icon = Icons.Outlined.RemoveDone,
|
|
|
|
toConfirm = confirm[2],
|
|
|
|
onLongClick = { onLongClickItem(2) },
|
|
|
|
onClick = onMarkAsUnreadClicked,
|
|
|
|
)
|
2022-07-23 01:05:50 +02:00
|
|
|
if (onDownloadClicked != null) {
|
2022-10-30 22:56:07 -04:00
|
|
|
var downloadExpanded by remember { mutableStateOf(false) }
|
|
|
|
Button(
|
|
|
|
title = stringResource(R.string.action_download),
|
|
|
|
icon = Icons.Outlined.Download,
|
|
|
|
toConfirm = confirm[3],
|
|
|
|
onLongClick = { onLongClickItem(3) },
|
|
|
|
onClick = { downloadExpanded = !downloadExpanded },
|
|
|
|
) {
|
2022-10-31 13:27:48 +11:00
|
|
|
val onDismissRequest = { downloadExpanded = false }
|
|
|
|
DownloadDropdownMenu(
|
|
|
|
expanded = downloadExpanded,
|
|
|
|
onDismissRequest = onDismissRequest,
|
|
|
|
onDownloadClicked = onDownloadClicked,
|
|
|
|
includeDownloadAllOption = false,
|
|
|
|
)
|
|
|
|
}
|
2022-07-23 01:05:50 +02:00
|
|
|
}
|
2022-12-03 10:35:30 +07:00
|
|
|
Button(
|
|
|
|
title = stringResource(R.string.action_delete),
|
|
|
|
icon = Icons.Outlined.Delete,
|
|
|
|
toConfirm = confirm[4],
|
|
|
|
onLongClick = { onLongClickItem(4) },
|
|
|
|
onClick = onDeleteClicked,
|
|
|
|
)
|
2022-07-23 01:05:50 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|