From 57dcb60d60bf2bf9f4fa8961d095bf4253f57b08 Mon Sep 17 00:00:00 2001 From: Syer10 Date: Fri, 11 Jun 2021 19:01:04 -0400 Subject: [PATCH] Move chapter items to a seperate file --- .../kotlin/ca/gosyer/ui/manga/ChapterItem.kt | 302 ++++++++++++++++++ .../kotlin/ca/gosyer/ui/manga/MangaMenu.kt | 276 ---------------- 2 files changed, 302 insertions(+), 276 deletions(-) create mode 100644 src/main/kotlin/ca/gosyer/ui/manga/ChapterItem.kt diff --git a/src/main/kotlin/ca/gosyer/ui/manga/ChapterItem.kt b/src/main/kotlin/ca/gosyer/ui/manga/ChapterItem.kt new file mode 100644 index 00000000..27cf4396 --- /dev/null +++ b/src/main/kotlin/ca/gosyer/ui/manga/ChapterItem.kt @@ -0,0 +1,302 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package ca.gosyer.ui.manga + +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.clickable +import androidx.compose.foundation.gestures.forEachGesture +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxScope +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ColumnScope +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Card +import androidx.compose.material.CircularProgressIndicator +import androidx.compose.material.ContentAlpha +import androidx.compose.material.DropdownMenu +import androidx.compose.material.DropdownMenuItem +import androidx.compose.material.Icon +import androidx.compose.material.IconButton +import androidx.compose.material.LocalContentColor +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Surface +import androidx.compose.material.Text +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowDownward +import androidx.compose.material.icons.filled.Check +import androidx.compose.material.icons.filled.Error +import androidx.compose.material.ripple.rememberRipple +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.unit.DpOffset +import androidx.compose.ui.unit.dp +import ca.gosyer.data.download.model.DownloadChapter +import ca.gosyer.data.download.model.DownloadState +import ca.gosyer.ui.base.components.awaitEventFirstDown +import ca.gosyer.ui.base.components.combinedMouseClickable +import ca.gosyer.util.compose.contextMenu +import java.time.Instant + +@Composable +fun ChapterItem( + viewChapter: MangaMenuViewModel.ViewChapter, + format: (Instant) -> String, + onClick: (Int) -> Unit, + toggleRead: (Int) -> Unit, + toggleBookmarked: (Int) -> Unit, + markPreviousAsRead: (Int) -> Unit, + downloadAChapter: (Int) -> Unit, + deleteDownload: (Int) -> Unit, + stopDownload: (Int) -> Unit +) { + val chapter = viewChapter.chapter + Card( + modifier = Modifier.fillMaxWidth().height(70.dp).padding(4.dp), + elevation = 1.dp, + shape = RoundedCornerShape(4.dp) + ) { + Row( + Modifier.combinedMouseClickable( + onClick = { + onClick(chapter.index) + }, + onRightClick = { + contextMenu( + it + ) { + menuItem("Toggle read") { toggleRead(chapter.index) } + menuItem("Mark previous as read") { markPreviousAsRead(chapter.index) } + separator() + menuItem("Toggle bookmarked") { toggleBookmarked(chapter.index) } + } + } + ), + horizontalArrangement = Arrangement.SpaceBetween + ) { + Column( + Modifier.padding(4.dp) + ) { + Text( + chapter.name, + maxLines = 1, + style = MaterialTheme.typography.h6, + color = LocalContentColor.current.copy( + alpha = if (chapter.read) ContentAlpha.disabled else ContentAlpha.high + ) + ) + val subtitleStr = buildAnnotatedString { + if (chapter.uploadDate > 0) { + append(format(Instant.ofEpochMilli(chapter.uploadDate))) + } + if (!chapter.read && chapter.lastPageRead > 0) { + if (length > 0) append(" • ") + append( + AnnotatedString( + "Page " + (chapter.lastPageRead + 1).toString(), + SpanStyle(color = LocalContentColor.current.copy(alpha = ContentAlpha.disabled)) + ) + ) + } + if (!chapter.scanlator.isNullOrBlank()) { + if (length > 0) append(" • ") + append(chapter.scanlator) + } + } + Text( + subtitleStr, + style = MaterialTheme.typography.body2, + color = LocalContentColor.current.copy( + alpha = if (chapter.read) ContentAlpha.disabled else ContentAlpha.medium + ) + ) + } + val downloadChapter by viewChapter.downloadChapterFlow.collectAsState() + val downloadState by viewChapter.downloadState.collectAsState() + + when (downloadState) { + MangaMenuViewModel.DownloadState.Downloaded -> { + DownloadedIconButton(onClick = { deleteDownload(chapter.index) }) + } + MangaMenuViewModel.DownloadState.Downloading -> { + DownloadingIconButton(downloadChapter, onClick = { stopDownload(chapter.index) }) + } + MangaMenuViewModel.DownloadState.NotDownloaded -> { + DownloadIconButton(onClick = { downloadAChapter(chapter.index) }) + } + } + } + } +} + +@Composable +private fun DownloadIconButton(onClick: () -> Unit) { + IconButton( + onClick = onClick, + modifier = Modifier.fillMaxHeight() + ) { + Surface( + shape = CircleShape, + border = BorderStroke(2.dp, LocalContentColor.current.copy(alpha = ContentAlpha.disabled)), + ) { + Icon( + Icons.Default.ArrowDownward, + null, + Modifier + .size(22.dp) + .padding(2.dp), + LocalContentColor.current.copy(alpha = ContentAlpha.disabled) + ) + } + } +} + +@Composable +private fun DownloadingIconButton(downloadChapter: DownloadChapter?, onClick: () -> Unit) { + DropdownIconButton( + { + DropdownMenuItem(onClick = onClick) { + Text("Cancel") + } + } + ) { + when (downloadChapter?.state) { + null, DownloadState.Queued -> CircularProgressIndicator( + Modifier + .size(26.dp) + .padding(2.dp), + LocalContentColor.current.copy(alpha = ContentAlpha.disabled), + 2.dp + ) + DownloadState.Downloading -> if (downloadChapter.progress != 0.0F) { + CircularProgressIndicator( + downloadChapter.progress, + Modifier + .size(26.dp) + .padding(2.dp), + LocalContentColor.current.copy(alpha = ContentAlpha.disabled), + 2.dp + ) + Icon( + Icons.Default.ArrowDownward, + null, + Modifier + .size(22.dp) + .padding(2.dp), + LocalContentColor.current.copy(alpha = ContentAlpha.disabled) + ) + } else { + CircularProgressIndicator( + Modifier + .size(26.dp) + .padding(2.dp), + LocalContentColor.current.copy(alpha = ContentAlpha.disabled), + 2.dp + ) + } + DownloadState.Error -> Surface(shape = CircleShape, color = LocalContentColor.current) { + Icon( + Icons.Default.Error, + null, + Modifier + .size(22.dp) + .padding(2.dp), + Color.Red + ) + } + DownloadState.Finished -> Surface(shape = CircleShape, color = LocalContentColor.current) { + Icon( + Icons.Default.Check, + null, + Modifier + .size(22.dp) + .padding(2.dp), + MaterialTheme.colors.surface + ) + } + } + } +} + +@Composable +private fun DownloadedIconButton(onClick: () -> Unit) { + DropdownIconButton( + { + DropdownMenuItem(onClick = onClick) { + Text("Delete") + } + } + ) { + Surface(shape = CircleShape, color = LocalContentColor.current) { + Icon( + Icons.Default.Check, + null, + Modifier + .size(22.dp) + .padding(2.dp), + MaterialTheme.colors.surface + ) + } + } +} + +@Composable +fun DropdownIconButton( + dropdownItems: @Composable ColumnScope.() -> Unit, + content: @Composable BoxScope.() -> Unit +) { + var showMenu by remember { mutableStateOf(false) } + var offset by remember { mutableStateOf(DpOffset(0.dp, 0.dp)) } + DropdownMenu( + expanded = showMenu, + onDismissRequest = { showMenu = false }, + offset = offset, + content = dropdownItems + ) + Box( + modifier = Modifier.fillMaxHeight() + .size(48.dp) + .clickable( + remember { MutableInteractionSource() }, + role = Role.Button, + indication = rememberRipple(bounded = false, radius = 24.dp) + ) { + showMenu = true + } + .pointerInput(Unit) { + forEachGesture { + awaitPointerEventScope { + awaitEventFirstDown().mouseEvent?.let { + offset = DpOffset(it.x.dp, it.y.dp) + } + } + } + }, + contentAlignment = Alignment.Center, + content = content + ) +} diff --git a/src/main/kotlin/ca/gosyer/ui/manga/MangaMenu.kt b/src/main/kotlin/ca/gosyer/ui/manga/MangaMenu.kt index 217f97a2..29044b34 100644 --- a/src/main/kotlin/ca/gosyer/ui/manga/MangaMenu.kt +++ b/src/main/kotlin/ca/gosyer/ui/manga/MangaMenu.kt @@ -6,18 +6,12 @@ package ca.gosyer.ui.manga -import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.VerticalScrollbar import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.gestures.forEachGesture -import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.ColumnScope import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.aspectRatio @@ -27,66 +21,35 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.rememberScrollbarAdapter -import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.Button -import androidx.compose.material.Card -import androidx.compose.material.CircularProgressIndicator -import androidx.compose.material.ContentAlpha -import androidx.compose.material.DropdownMenu -import androidx.compose.material.DropdownMenuItem -import androidx.compose.material.Icon -import androidx.compose.material.IconButton -import androidx.compose.material.LocalContentColor import androidx.compose.material.MaterialTheme import androidx.compose.material.Surface import androidx.compose.material.Text -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.ArrowDownward -import androidx.compose.material.icons.filled.Check -import androidx.compose.material.icons.filled.Error -import androidx.compose.material.ripple.rememberRipple import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.input.pointer.pointerInput -import androidx.compose.ui.semantics.Role -import androidx.compose.ui.text.AnnotatedString -import androidx.compose.ui.text.SpanStyle -import androidx.compose.ui.text.buildAnnotatedString import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.DpOffset import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import ca.gosyer.BuildConfig -import ca.gosyer.data.download.model.DownloadChapter -import ca.gosyer.data.download.model.DownloadState import ca.gosyer.data.models.Manga import ca.gosyer.ui.base.components.KtorImage import ca.gosyer.ui.base.components.LoadingScreen import ca.gosyer.ui.base.components.Toolbar -import ca.gosyer.ui.base.components.awaitEventFirstDown -import ca.gosyer.ui.base.components.combinedMouseClickable import ca.gosyer.ui.base.components.mangaAspectRatio import ca.gosyer.ui.base.vm.viewModel import ca.gosyer.ui.main.Route import ca.gosyer.ui.reader.openReaderMenu import ca.gosyer.util.compose.ThemedWindow -import ca.gosyer.util.compose.contextMenu import com.github.zsoltk.compose.router.BackStack -import java.time.Instant fun openMangaMenu(mangaId: Long) { ThemedWindow(BuildConfig.NAME) { @@ -213,242 +176,3 @@ private fun MangaInfo(manga: Manga, modifier: Modifier = Modifier) { } } } - -@Composable -fun ChapterItem( - viewChapter: MangaMenuViewModel.ViewChapter, - format: (Instant) -> String, - onClick: (Int) -> Unit, - toggleRead: (Int) -> Unit, - toggleBookmarked: (Int) -> Unit, - markPreviousAsRead: (Int) -> Unit, - downloadAChapter: (Int) -> Unit, - deleteDownload: (Int) -> Unit, - stopDownload: (Int) -> Unit -) { - val chapter = viewChapter.chapter - Card( - modifier = Modifier.fillMaxWidth().height(70.dp).padding(4.dp), - elevation = 1.dp, - shape = RoundedCornerShape(4.dp) - ) { - Row( - Modifier.combinedMouseClickable( - onClick = { - onClick(chapter.index) - }, - onRightClick = { - contextMenu( - it - ) { - menuItem("Toggle read") { toggleRead(chapter.index) } - menuItem("Mark previous as read") { markPreviousAsRead(chapter.index) } - separator() - menuItem("Toggle bookmarked") { toggleBookmarked(chapter.index) } - } - } - ), - horizontalArrangement = Arrangement.SpaceBetween - ) { - Column( - Modifier.padding(4.dp) - ) { - Text( - chapter.name, - maxLines = 1, - style = MaterialTheme.typography.h6, - color = LocalContentColor.current.copy( - alpha = if (chapter.read) ContentAlpha.disabled else ContentAlpha.high - ) - ) - val subtitleStr = buildAnnotatedString { - if (chapter.uploadDate > 0) { - append(format(Instant.ofEpochMilli(chapter.uploadDate))) - } - if (!chapter.read && chapter.lastPageRead > 0) { - if (length > 0) append(" • ") - append( - AnnotatedString( - "Page " + (chapter.lastPageRead + 1).toString(), - SpanStyle(color = LocalContentColor.current.copy(alpha = ContentAlpha.disabled)) - ) - ) - } - if (!chapter.scanlator.isNullOrBlank()) { - if (length > 0) append(" • ") - append(chapter.scanlator) - } - } - Text( - subtitleStr, - style = MaterialTheme.typography.body2, - color = LocalContentColor.current.copy( - alpha = if (chapter.read) ContentAlpha.disabled else ContentAlpha.medium - ) - ) - } - val downloadChapter by viewChapter.downloadChapterFlow.collectAsState() - val downloadState by viewChapter.downloadState.collectAsState() - - when (downloadState) { - MangaMenuViewModel.DownloadState.Downloaded -> { - DownloadedIconButton(onClick = { deleteDownload(chapter.index) }) - } - MangaMenuViewModel.DownloadState.Downloading -> { - DownloadingIconButton(downloadChapter, onClick = { stopDownload(chapter.index) }) - } - MangaMenuViewModel.DownloadState.NotDownloaded -> { - DownloadIconButton(onClick = { downloadAChapter(chapter.index) }) - } - } - } - } -} - -@Composable -private fun DownloadIconButton(onClick: () -> Unit) { - IconButton( - onClick = onClick, - modifier = Modifier.fillMaxHeight() - ) { - Surface( - shape = CircleShape, - border = BorderStroke(2.dp, LocalContentColor.current.copy(alpha = ContentAlpha.disabled)), - ) { - Icon( - Icons.Default.ArrowDownward, - null, - Modifier - .size(22.dp) - .padding(2.dp), - LocalContentColor.current.copy(alpha = ContentAlpha.disabled) - ) - } - } -} - -@Composable -private fun DownloadingIconButton(downloadChapter: DownloadChapter?, onClick: () -> Unit) { - DropdownIconButton( - { - DropdownMenuItem(onClick = onClick) { - Text("Cancel") - } - } - ) { - when (downloadChapter?.state) { - null, DownloadState.Queued -> CircularProgressIndicator( - Modifier - .size(26.dp) - .padding(2.dp), - LocalContentColor.current.copy(alpha = ContentAlpha.disabled), - 2.dp - ) - DownloadState.Downloading -> if (downloadChapter.progress != 0.0F) { - CircularProgressIndicator( - downloadChapter.progress, - Modifier - .size(26.dp) - .padding(2.dp), - LocalContentColor.current.copy(alpha = ContentAlpha.disabled), - 2.dp - ) - Icon( - Icons.Default.ArrowDownward, - null, - Modifier - .size(22.dp) - .padding(2.dp), - LocalContentColor.current.copy(alpha = ContentAlpha.disabled) - ) - } else { - CircularProgressIndicator( - Modifier - .size(26.dp) - .padding(2.dp), - LocalContentColor.current.copy(alpha = ContentAlpha.disabled), - 2.dp - ) - } - DownloadState.Error -> Surface(shape = CircleShape, color = LocalContentColor.current) { - Icon( - Icons.Default.Error, - null, - Modifier - .size(22.dp) - .padding(2.dp), - Color.Red - ) - } - DownloadState.Finished -> Surface(shape = CircleShape, color = LocalContentColor.current) { - Icon( - Icons.Default.Check, - null, - Modifier - .size(22.dp) - .padding(2.dp), - MaterialTheme.colors.surface - ) - } - } - } -} - -@Composable -private fun DownloadedIconButton(onClick: () -> Unit) { - DropdownIconButton( - { - DropdownMenuItem(onClick = onClick) { - Text("Delete") - } - } - ) { - Surface(shape = CircleShape, color = LocalContentColor.current) { - Icon( - Icons.Default.Check, - null, - Modifier - .size(22.dp) - .padding(2.dp), - MaterialTheme.colors.surface - ) - } - } -} - -@Composable -fun DropdownIconButton( - dropdownItems: @Composable ColumnScope.() -> Unit, - content: @Composable BoxScope.() -> Unit -) { - var showMenu by remember { mutableStateOf(false) } - var offset by remember { mutableStateOf(DpOffset(0.dp, 0.dp)) } - DropdownMenu( - expanded = showMenu, - onDismissRequest = { showMenu = false }, - offset = offset, - content = dropdownItems - ) - Box( - modifier = Modifier.fillMaxHeight() - .size(48.dp) - .clickable( - remember { MutableInteractionSource() }, - role = Role.Button, - indication = rememberRipple(bounded = false, radius = 24.dp) - ) { - showMenu = true - } - .pointerInput(Unit) { - forEachGesture { - awaitPointerEventScope { - awaitEventFirstDown().mouseEvent?.let { - offset = DpOffset(it.x.dp, it.y.dp) - } - } - } - }, - contentAlignment = Alignment.Center, - content = content - ) -}