mirror of
https://github.com/Suwayomi/TachideskJUI.git
synced 2025-12-10 06:42:05 +01:00
Initial downloads menu implementation
This commit is contained in:
@@ -16,8 +16,10 @@ import ca.gosyer.data.server.Http
|
||||
import ca.gosyer.data.server.HttpProvider
|
||||
import ca.gosyer.data.server.ServerPreferences
|
||||
import ca.gosyer.data.server.ServerService
|
||||
import ca.gosyer.data.server.interactions.BackupInteractionHandler
|
||||
import ca.gosyer.data.server.interactions.CategoryInteractionHandler
|
||||
import ca.gosyer.data.server.interactions.ChapterInteractionHandler
|
||||
import ca.gosyer.data.server.interactions.DownloadInteractionHandler
|
||||
import ca.gosyer.data.server.interactions.ExtensionInteractionHandler
|
||||
import ca.gosyer.data.server.interactions.LibraryInteractionHandler
|
||||
import ca.gosyer.data.server.interactions.MangaInteractionHandler
|
||||
@@ -58,10 +60,14 @@ val DataModule = module {
|
||||
.toProvider(HttpProvider::class)
|
||||
.providesSingleton()
|
||||
|
||||
bind<BackupInteractionHandler>()
|
||||
.toClass<BackupInteractionHandler>()
|
||||
bind<CategoryInteractionHandler>()
|
||||
.toClass<CategoryInteractionHandler>()
|
||||
bind<ChapterInteractionHandler>()
|
||||
.toClass<ChapterInteractionHandler>()
|
||||
bind<DownloadInteractionHandler>()
|
||||
.toClass<DownloadInteractionHandler>()
|
||||
bind<ExtensionInteractionHandler>()
|
||||
.toClass<ExtensionInteractionHandler>()
|
||||
bind<LibraryInteractionHandler>()
|
||||
|
||||
@@ -40,6 +40,10 @@ class DownloadService @Inject constructor(
|
||||
private val serverUrl = serverPreferences.serverUrl().stateIn(GlobalScope)
|
||||
private val _downloaderStatus = MutableStateFlow(DownloaderStatus.Stopped)
|
||||
val downloaderStatus = _downloaderStatus.asStateFlow()
|
||||
|
||||
private val _downloadQueue = MutableStateFlow(emptyList<DownloadChapter>())
|
||||
val downloadQueue = _downloadQueue.asStateFlow()
|
||||
|
||||
private val watching = mutableMapOf<Long, MutableSharedFlow<List<DownloadChapter>>>()
|
||||
|
||||
init {
|
||||
@@ -58,6 +62,7 @@ class DownloadService @Inject constructor(
|
||||
frame as Frame.Text
|
||||
val status = json.decodeFromString<DownloadStatus>(frame.readText())
|
||||
_downloaderStatus.value = status.status
|
||||
_downloadQueue.value = status.queue
|
||||
val queue = status.queue.groupBy { it.mangaId }
|
||||
watching.forEach { (mangaId, flow) ->
|
||||
flow.emit(queue[mangaId].orEmpty())
|
||||
|
||||
@@ -13,8 +13,8 @@ import kotlinx.serialization.Serializable
|
||||
data class DownloadChapter(
|
||||
val chapterIndex: Int,
|
||||
val mangaId: Long,
|
||||
var state: DownloadState = DownloadState.Queued,
|
||||
var progress: Float = 0f,
|
||||
var tries: Int = 0,
|
||||
var chapter: Chapter? = null,
|
||||
val state: DownloadState = DownloadState.Queued,
|
||||
val progress: Float = 0f,
|
||||
val tries: Int = 0,
|
||||
val chapter: Chapter? = null,
|
||||
)
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.base.components
|
||||
|
||||
import androidx.compose.foundation.BoxWithTooltip
|
||||
import androidx.compose.foundation.layout.BoxScope
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.unit.DpOffset
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
fun BoxWithTooltipSurface(
|
||||
tooltip: @Composable () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
contentAlignment: Alignment = Alignment.TopStart,
|
||||
propagateMinConstraints: Boolean = false,
|
||||
delay: Int = 500,
|
||||
offset: DpOffset = DpOffset.Zero,
|
||||
content: @Composable BoxScope.() -> Unit
|
||||
) {
|
||||
BoxWithTooltip(
|
||||
{
|
||||
Surface(
|
||||
modifier = Modifier.shadow(4.dp),
|
||||
shape = RoundedCornerShape(4.dp),
|
||||
elevation = 4.dp,
|
||||
content = tooltip
|
||||
)
|
||||
},
|
||||
modifier,
|
||||
contentAlignment,
|
||||
propagateMinConstraints,
|
||||
delay,
|
||||
offset,
|
||||
content
|
||||
)
|
||||
}
|
||||
@@ -26,7 +26,7 @@ import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.unit.IntOffset
|
||||
import java.awt.event.MouseEvent
|
||||
|
||||
suspend fun AwaitPointerEventScope.awaitEventFirstDown(): PointerEvent {
|
||||
internal suspend fun AwaitPointerEventScope.awaitEventFirstDown(): PointerEvent {
|
||||
var event: PointerEvent
|
||||
do {
|
||||
event = awaitPointerEvent()
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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.base.components
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.gestures.forEachGesture
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxScope
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material.DropdownMenu
|
||||
import androidx.compose.material.ripple.rememberRipple
|
||||
import androidx.compose.runtime.Composable
|
||||
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.input.pointer.pointerInput
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.unit.DpOffset
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
fun DropdownIconButton(
|
||||
key: Any? = Unit,
|
||||
dropdownItems: @Composable ColumnScope.() -> Unit,
|
||||
content: @Composable BoxScope.() -> Unit
|
||||
) {
|
||||
var showMenu by remember(key) { mutableStateOf(false) }
|
||||
var offset by remember(key) { 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
|
||||
)
|
||||
}
|
||||
@@ -36,6 +36,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.RectangleShape
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -94,13 +95,22 @@ fun Toolbar(
|
||||
Row {
|
||||
actions()
|
||||
if (closable) {
|
||||
IconButton(
|
||||
onClick = onClose
|
||||
) {
|
||||
Icon(Icons.Default.Close, "close", Modifier.size(52.dp))
|
||||
}
|
||||
ActionIcon(onClick = onClose, "Close", Icons.Default.Close)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ActionIcon(onClick: () -> Unit, contentDescription: String, icon: ImageVector) {
|
||||
BoxWithTooltipSurface(
|
||||
{
|
||||
Text(contentDescription, modifier = Modifier.padding(10.dp))
|
||||
}
|
||||
) {
|
||||
IconButton(onClick = onClick) {
|
||||
Icon(icon, contentDescription, Modifier.size(52.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
144
src/main/kotlin/ca/gosyer/ui/downloads/DownloadsMenu.kt
Normal file
144
src/main/kotlin/ca/gosyer/ui/downloads/DownloadsMenu.kt
Normal file
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* 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.downloads
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.IntrinsicSize
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
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.requiredWidth
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.ContentAlpha
|
||||
import androidx.compose.material.DropdownMenuItem
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.LinearProgressIndicator
|
||||
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.ClearAll
|
||||
import androidx.compose.material.icons.filled.MoreVert
|
||||
import androidx.compose.material.icons.filled.Pause
|
||||
import androidx.compose.material.icons.filled.PlayArrow
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import ca.gosyer.BuildConfig
|
||||
import ca.gosyer.data.download.model.DownloadChapter
|
||||
import ca.gosyer.data.download.model.DownloaderStatus
|
||||
import ca.gosyer.data.models.Chapter
|
||||
import ca.gosyer.ui.base.components.ActionIcon
|
||||
import ca.gosyer.ui.base.components.DropdownIconButton
|
||||
import ca.gosyer.ui.base.components.Toolbar
|
||||
import ca.gosyer.ui.base.vm.viewModel
|
||||
import ca.gosyer.util.compose.ThemedWindow
|
||||
|
||||
fun openDownloadsMenu() {
|
||||
ThemedWindow(BuildConfig.NAME) {
|
||||
DownloadsMenu()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DownloadsMenu() {
|
||||
val vm = viewModel<DownloadsMenuViewModel>()
|
||||
val downloadQueue by vm.downloadQueue.collectAsState()
|
||||
|
||||
Surface {
|
||||
Column {
|
||||
Toolbar(
|
||||
"Downloads",
|
||||
closable = false,
|
||||
actions = {
|
||||
val downloadStatus by vm.downloaderStatus.collectAsState()
|
||||
if (downloadStatus == DownloaderStatus.Started) {
|
||||
ActionIcon(onClick = vm::pause, "Pause", Icons.Default.Pause)
|
||||
} else {
|
||||
ActionIcon(onClick = vm::start, "Continue", Icons.Default.PlayArrow)
|
||||
}
|
||||
ActionIcon(onClick = vm::clear, "Clear queue", Icons.Default.ClearAll)
|
||||
}
|
||||
)
|
||||
LazyColumn(Modifier.fillMaxSize()) {
|
||||
items(downloadQueue) {
|
||||
downloadsItem(
|
||||
it,
|
||||
vm::stopDownload,
|
||||
vm::moveToBottom
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun downloadsItem(
|
||||
chapter: DownloadChapter,
|
||||
onDownloadCancel: (Chapter?) -> Unit,
|
||||
onMoveDownloadToBottom: (Chapter?) -> Unit
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
.height(56.dp)
|
||||
.padding(vertical = 8.dp),
|
||||
horizontalArrangement = Arrangement.SpaceAround
|
||||
) {
|
||||
Column(Modifier.fillMaxHeight(), verticalArrangement = Arrangement.SpaceAround) {
|
||||
Row(Modifier.fillMaxWidth().padding(horizontal = 32.dp), horizontalArrangement = Arrangement.SpaceBetween) {
|
||||
Text(chapter.chapter?.name.toString(), maxLines = 1, overflow = TextOverflow.Ellipsis)
|
||||
// Spacer(Modifier.width(16.dp))
|
||||
if (chapter.chapter?.pageCount != null && chapter.chapter.pageCount != -1) {
|
||||
Text(
|
||||
"${(chapter.chapter.pageCount * chapter.progress).toInt()}/${chapter.chapter.pageCount}",
|
||||
Modifier.padding(start = 16.dp).requiredWidth(IntrinsicSize.Max),
|
||||
style = MaterialTheme.typography.body2,
|
||||
color = LocalContentColor.current.copy(alpha = ContentAlpha.disabled),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Visible
|
||||
)
|
||||
} else {
|
||||
Spacer(Modifier.width(32.dp))
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.height(4.dp))
|
||||
LinearProgressIndicator(
|
||||
chapter.progress,
|
||||
Modifier.fillMaxWidth()
|
||||
.padding(start = 32.dp, end = 16.dp, bottom = 8.dp)
|
||||
)
|
||||
}
|
||||
DropdownIconButton(
|
||||
chapter.mangaId to chapter.chapterIndex,
|
||||
{
|
||||
DropdownMenuItem(onClick = { onDownloadCancel(chapter.chapter) }) {
|
||||
Text("Cancel")
|
||||
}
|
||||
DropdownMenuItem(onClick = { onMoveDownloadToBottom(chapter.chapter) }) {
|
||||
Text("Move to bottom")
|
||||
}
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
Icons.Default.MoreVert,
|
||||
null
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* 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.downloads
|
||||
|
||||
import ca.gosyer.data.download.DownloadService
|
||||
import ca.gosyer.data.models.Chapter
|
||||
import ca.gosyer.data.server.interactions.ChapterInteractionHandler
|
||||
import ca.gosyer.data.server.interactions.DownloadInteractionHandler
|
||||
import ca.gosyer.ui.base.vm.ViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
class DownloadsMenuViewModel @Inject constructor(
|
||||
private val downloadService: DownloadService,
|
||||
private val downloadsHandler: DownloadInteractionHandler,
|
||||
private val chapterHandler: ChapterInteractionHandler
|
||||
) : ViewModel() {
|
||||
val downloaderStatus get() = downloadService.downloaderStatus
|
||||
val downloadQueue get() = downloadService.downloadQueue
|
||||
|
||||
fun start() {
|
||||
scope.launch {
|
||||
downloadsHandler.startDownloading()
|
||||
}
|
||||
}
|
||||
|
||||
fun pause() {
|
||||
scope.launch {
|
||||
downloadsHandler.stopDownloading()
|
||||
}
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
scope.launch {
|
||||
downloadsHandler.clearDownloadQueue()
|
||||
}
|
||||
}
|
||||
|
||||
fun stopDownload(chapter: Chapter?) {
|
||||
chapter ?: return
|
||||
scope.launch {
|
||||
chapterHandler.deleteChapterDownload(chapter)
|
||||
}
|
||||
}
|
||||
|
||||
fun moveToBottom(chapter: Chapter?) {
|
||||
chapter ?: return
|
||||
scope.launch {
|
||||
chapterHandler.deleteChapterDownload(chapter)
|
||||
chapterHandler.queueChapterDownload(chapter)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
package ca.gosyer.ui.extensions
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.VerticalScrollbar
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
@@ -26,6 +25,7 @@ import androidx.compose.foundation.rememberScrollbarAdapter
|
||||
import androidx.compose.material.Button
|
||||
import androidx.compose.material.ContentAlpha
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Surface
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
@@ -39,6 +39,7 @@ import androidx.compose.ui.text.withStyle
|
||||
import androidx.compose.ui.unit.IntSize
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import ca.gosyer.BuildConfig
|
||||
import ca.gosyer.data.models.Extension
|
||||
import ca.gosyer.ui.base.components.KtorImage
|
||||
import ca.gosyer.ui.base.components.LoadingScreen
|
||||
@@ -49,12 +50,11 @@ import ca.gosyer.util.compose.persistentLazyListState
|
||||
import java.util.Locale
|
||||
|
||||
fun openExtensionsMenu() {
|
||||
ThemedWindow(title = "TachideskJUI - Extensions", size = IntSize(550, 700)) {
|
||||
ThemedWindow(BuildConfig.NAME, size = IntSize(550, 700)) {
|
||||
ExtensionsMenu()
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun ExtensionsMenu() {
|
||||
val vm = viewModel<ExtensionsMenuViewModel>()
|
||||
@@ -63,7 +63,7 @@ fun ExtensionsMenu() {
|
||||
val serverUrl by vm.serverUrl.collectAsState()
|
||||
val search by vm.searchQuery.collectAsState()
|
||||
|
||||
Box(Modifier.fillMaxSize().background(MaterialTheme.colors.background)) {
|
||||
Surface(Modifier.fillMaxSize()) {
|
||||
if (isLoading) {
|
||||
LoadingScreen(isLoading)
|
||||
} else {
|
||||
|
||||
@@ -13,6 +13,7 @@ import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.material.ScrollableTabRow
|
||||
import androidx.compose.material.Surface
|
||||
import androidx.compose.material.Tab
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -24,6 +25,7 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.util.fastForEachIndexed
|
||||
import ca.gosyer.BuildConfig
|
||||
import ca.gosyer.data.library.model.DisplayMode
|
||||
import ca.gosyer.data.models.Category
|
||||
import ca.gosyer.data.models.Manga
|
||||
@@ -35,7 +37,7 @@ import com.google.accompanist.pager.HorizontalPager
|
||||
import com.google.accompanist.pager.PagerState
|
||||
|
||||
fun openLibraryMenu() {
|
||||
ThemedWindow {
|
||||
ThemedWindow(BuildConfig.NAME) {
|
||||
LibraryScreen()
|
||||
}
|
||||
}
|
||||
@@ -50,46 +52,48 @@ fun LibraryScreen(onClickManga: (Long) -> Unit = { openMangaMenu(it) }) {
|
||||
val serverUrl by vm.serverUrl.collectAsState()
|
||||
// val sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)
|
||||
|
||||
if (categories.isEmpty()) {
|
||||
LoadingScreen(isLoading)
|
||||
} else {
|
||||
/*ModalBottomSheetLayout(
|
||||
sheetState = sheetState,
|
||||
sheetContent = { *//*LibrarySheet()*//* }
|
||||
) {*/
|
||||
Column(Modifier.fillMaxWidth()) {
|
||||
/*Toolbar(
|
||||
title = {
|
||||
val text = if (vm.showCategoryTabs) {
|
||||
stringResource(R.string.library_label)
|
||||
} else {
|
||||
vm.selectedCategory?.visibleName.orEmpty()
|
||||
Surface {
|
||||
if (categories.isEmpty()) {
|
||||
LoadingScreen(isLoading)
|
||||
} else {
|
||||
/*ModalBottomSheetLayout(
|
||||
sheetState = sheetState,
|
||||
sheetContent = { *//*LibrarySheet()*//* }
|
||||
) {*/
|
||||
Column(Modifier.fillMaxWidth()) {
|
||||
/*Toolbar(
|
||||
title = {
|
||||
val text = if (vm.showCategoryTabs) {
|
||||
stringResource(R.string.library_label)
|
||||
} else {
|
||||
vm.selectedCategory?.visibleName.orEmpty()
|
||||
}
|
||||
Text(text)
|
||||
},
|
||||
actions = {
|
||||
IconButton(onClick = { scope.launch { sheetState.show() }}) {
|
||||
Icon(Icons.Default.FilterList, contentDescription = null)
|
||||
}
|
||||
}
|
||||
Text(text)
|
||||
},
|
||||
actions = {
|
||||
IconButton(onClick = { scope.launch { sheetState.show() }}) {
|
||||
Icon(Icons.Default.FilterList, contentDescription = null)
|
||||
}
|
||||
}
|
||||
)*/
|
||||
LibraryTabs(
|
||||
visible = true, // vm.showCategoryTabs,
|
||||
categories = categories,
|
||||
selectedPage = selectedCategoryIndex,
|
||||
onPageChanged = vm::setSelectedPage
|
||||
)
|
||||
LibraryPager(
|
||||
categories = categories,
|
||||
displayMode = displayMode,
|
||||
selectedPage = selectedCategoryIndex,
|
||||
serverUrl = serverUrl,
|
||||
getLibraryForPage = { vm.getLibraryForCategoryIndex(it).collectAsState() },
|
||||
onPageChanged = { vm.setSelectedPage(it) },
|
||||
onClickManga = onClickManga
|
||||
)
|
||||
)*/
|
||||
LibraryTabs(
|
||||
visible = true, // vm.showCategoryTabs,
|
||||
categories = categories,
|
||||
selectedPage = selectedCategoryIndex,
|
||||
onPageChanged = vm::setSelectedPage
|
||||
)
|
||||
LibraryPager(
|
||||
categories = categories,
|
||||
displayMode = displayMode,
|
||||
selectedPage = selectedCategoryIndex,
|
||||
serverUrl = serverUrl,
|
||||
getLibraryForPage = { vm.getLibraryForCategoryIndex(it).collectAsState() },
|
||||
onPageChanged = { vm.setSelectedPage(it) },
|
||||
onClickManga = onClickManga
|
||||
)
|
||||
}
|
||||
// }
|
||||
}
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,23 +15,30 @@ import androidx.compose.foundation.layout.fillMaxHeight
|
||||
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.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Card
|
||||
import androidx.compose.material.ContentAlpha
|
||||
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.Book
|
||||
import androidx.compose.material.icons.filled.Download
|
||||
import androidx.compose.material.icons.filled.Explore
|
||||
import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.material.icons.filled.Store
|
||||
import androidx.compose.material.icons.outlined.Book
|
||||
import androidx.compose.material.icons.outlined.Download
|
||||
import androidx.compose.material.icons.outlined.Explore
|
||||
import androidx.compose.material.icons.outlined.Settings
|
||||
import androidx.compose.material.icons.outlined.Store
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -42,9 +49,14 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import ca.gosyer.BuildConfig
|
||||
import ca.gosyer.data.ui.model.StartScreen
|
||||
import ca.gosyer.ui.base.components.combinedMouseClickable
|
||||
import ca.gosyer.ui.base.vm.viewModel
|
||||
import ca.gosyer.ui.downloads.DownloadsMenu
|
||||
import ca.gosyer.ui.downloads.DownloadsMenuViewModel
|
||||
import ca.gosyer.ui.extensions.ExtensionsMenu
|
||||
import ca.gosyer.ui.extensions.openExtensionsMenu
|
||||
import ca.gosyer.ui.library.LibraryScreen
|
||||
import ca.gosyer.ui.library.openLibraryMenu
|
||||
import ca.gosyer.ui.manga.MangaMenu
|
||||
import ca.gosyer.ui.settings.SettingsAdvancedScreen
|
||||
import ca.gosyer.ui.settings.SettingsAppearance
|
||||
@@ -56,6 +68,8 @@ import ca.gosyer.ui.settings.SettingsReaderScreen
|
||||
import ca.gosyer.ui.settings.SettingsScreen
|
||||
import ca.gosyer.ui.settings.SettingsServerScreen
|
||||
import ca.gosyer.ui.sources.SourcesMenu
|
||||
import ca.gosyer.ui.sources.openSourcesMenu
|
||||
import com.github.zsoltk.compose.router.BackStack
|
||||
import com.github.zsoltk.compose.router.Router
|
||||
import com.github.zsoltk.compose.savedinstancestate.Bundle
|
||||
import com.github.zsoltk.compose.savedinstancestate.BundleScope
|
||||
@@ -66,51 +80,39 @@ fun MainMenu(rootBundle: Bundle) {
|
||||
Surface {
|
||||
Router("TopLevel", vm.startScreen.toRoute()) { backStack ->
|
||||
Row {
|
||||
Surface(elevation = 2.dp) {
|
||||
Column(Modifier.width(200.dp).fillMaxHeight(),) {
|
||||
Box(Modifier.fillMaxWidth().height(60.dp)) {
|
||||
Text(
|
||||
BuildConfig.NAME,
|
||||
fontSize = 30.sp,
|
||||
modifier = Modifier.align(Alignment.Center)
|
||||
)
|
||||
}
|
||||
Spacer(Modifier.height(20.dp))
|
||||
remember { TopLevelMenus.values() }.forEach { topLevelMenu ->
|
||||
MainMenuItem(
|
||||
topLevelMenu,
|
||||
backStack.elements.first() == topLevelMenu.menu
|
||||
) {
|
||||
backStack.newRoot(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Column(Modifier.fillMaxSize()) {
|
||||
BundleScope("K${backStack.lastIndex}", rootBundle, false) {
|
||||
when (val routing = backStack.last()) {
|
||||
is Route.Library -> LibraryScreen {
|
||||
backStack.push(Route.Manga(it))
|
||||
}
|
||||
is Route.Sources -> SourcesMenu {
|
||||
backStack.push(Route.Manga(it))
|
||||
}
|
||||
is Route.Extensions -> ExtensionsMenu()
|
||||
is Route.Manga -> MangaMenu(routing.mangaId, backStack)
|
||||
SideMenu(backStack)
|
||||
MainWindow(rootBundle, backStack)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is Route.Settings -> SettingsScreen(backStack)
|
||||
is Route.SettingsGeneral -> SettingsGeneralScreen(backStack)
|
||||
is Route.SettingsAppearance -> SettingsAppearance(backStack)
|
||||
is Route.SettingsServer -> SettingsServerScreen(backStack)
|
||||
is Route.SettingsLibrary -> SettingsLibraryScreen(backStack)
|
||||
is Route.SettingsReader -> SettingsReaderScreen(backStack)
|
||||
/*is Route.SettingsDownloads -> SettingsDownloadsScreen(backStack)
|
||||
is Route.SettingsTracking -> SettingsTrackingScreen(backStack)*/
|
||||
is Route.SettingsBrowse -> SettingsBrowseScreen(backStack)
|
||||
is Route.SettingsBackup -> SettingsBackupScreen(backStack)
|
||||
/*is Route.SettingsSecurity -> SettingsSecurityScreen(backStack)
|
||||
is Route.SettingsParentalControls -> SettingsParentalControlsScreen(backStack)*/
|
||||
is Route.SettingsAdvanced -> SettingsAdvancedScreen(backStack)
|
||||
@Composable
|
||||
fun SideMenu(backStack: BackStack<Route>) {
|
||||
Surface(Modifier.width(200.dp).fillMaxHeight(), elevation = 2.dp) {
|
||||
Box(Modifier.fillMaxSize()) {
|
||||
Column(Modifier.fillMaxSize()) {
|
||||
Box(Modifier.fillMaxWidth().height(60.dp)) {
|
||||
Text(
|
||||
BuildConfig.NAME,
|
||||
fontSize = 30.sp,
|
||||
modifier = Modifier.align(Alignment.Center)
|
||||
)
|
||||
}
|
||||
Spacer(Modifier.height(20.dp))
|
||||
remember { TopLevelMenus.values().filter { it.top } }.forEach { topLevelMenu ->
|
||||
SideMenuItem(
|
||||
topLevelMenu,
|
||||
backStack
|
||||
)
|
||||
}
|
||||
Box(Modifier.fillMaxSize()) {
|
||||
Column(Modifier.align(Alignment.BottomStart).padding(bottom = 8.dp)) {
|
||||
remember { TopLevelMenus.values().filterNot { it.top } }.forEach { topLevelMenu ->
|
||||
SideMenuItem(
|
||||
topLevelMenu,
|
||||
backStack
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -120,33 +122,101 @@ fun MainMenu(rootBundle: Bundle) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MainMenuItem(menu: TopLevelMenus, selected: Boolean, onClick: (Route) -> Unit) {
|
||||
fun SideMenuItem(topLevelMenu: TopLevelMenus, backStack: BackStack<Route>) {
|
||||
MainMenuItem(
|
||||
backStack.elements.first() == topLevelMenu.menu,
|
||||
topLevelMenu.text,
|
||||
topLevelMenu.menu,
|
||||
topLevelMenu.selectedIcon,
|
||||
topLevelMenu.unselectedIcon,
|
||||
topLevelMenu.openInNewWindow,
|
||||
topLevelMenu.extraInfo
|
||||
) {
|
||||
backStack.newRoot(it)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MainWindow(rootBundle: Bundle, backStack: BackStack<Route>) {
|
||||
Column(Modifier.fillMaxSize()) {
|
||||
BundleScope("K${backStack.lastIndex}", rootBundle, false) {
|
||||
when (val routing = backStack.last()) {
|
||||
is Route.Library -> LibraryScreen {
|
||||
backStack.push(Route.Manga(it))
|
||||
}
|
||||
is Route.Sources -> SourcesMenu {
|
||||
backStack.push(Route.Manga(it))
|
||||
}
|
||||
is Route.Extensions -> ExtensionsMenu()
|
||||
is Route.Manga -> MangaMenu(routing.mangaId, backStack)
|
||||
is Route.Downloads -> DownloadsMenu()
|
||||
|
||||
is Route.Settings -> SettingsScreen(backStack)
|
||||
is Route.SettingsGeneral -> SettingsGeneralScreen(backStack)
|
||||
is Route.SettingsAppearance -> SettingsAppearance(backStack)
|
||||
is Route.SettingsServer -> SettingsServerScreen(backStack)
|
||||
is Route.SettingsLibrary -> SettingsLibraryScreen(backStack)
|
||||
is Route.SettingsReader -> SettingsReaderScreen(backStack)
|
||||
/*is Route.SettingsDownloads -> SettingsDownloadsScreen(backStack)
|
||||
is Route.SettingsTracking -> SettingsTrackingScreen(backStack)*/
|
||||
is Route.SettingsBrowse -> SettingsBrowseScreen(backStack)
|
||||
is Route.SettingsBackup -> SettingsBackupScreen(backStack)
|
||||
/*is Route.SettingsSecurity -> SettingsSecurityScreen(backStack)
|
||||
is Route.SettingsParentalControls -> SettingsParentalControlsScreen(backStack)*/
|
||||
is Route.SettingsAdvanced -> SettingsAdvancedScreen(backStack)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MainMenuItem(
|
||||
selected: Boolean,
|
||||
text: String,
|
||||
menu: Route,
|
||||
selectedIcon: ImageVector,
|
||||
unselectedIcon: ImageVector,
|
||||
onMiddleClick: () -> Unit,
|
||||
extraInfo: (@Composable () -> Unit)? = null,
|
||||
onClick: (Route) -> Unit
|
||||
) {
|
||||
Card(
|
||||
{ onClick(menu.menu) },
|
||||
Modifier.fillMaxWidth().height(40.dp),
|
||||
Modifier.fillMaxWidth(),
|
||||
backgroundColor = if (!selected) {
|
||||
Color.Transparent
|
||||
} else {
|
||||
MaterialTheme.colors.primary.copy(0.30F)
|
||||
},
|
||||
contentColor = Color.Transparent,
|
||||
elevation = 0.dp,
|
||||
shape = RoundedCornerShape(8.dp)
|
||||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxSize()) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
.height(40.dp)
|
||||
.combinedMouseClickable(
|
||||
onClick = { onClick(menu) },
|
||||
onMiddleClick = { onMiddleClick() }
|
||||
)
|
||||
) {
|
||||
Spacer(Modifier.width(16.dp))
|
||||
Image(
|
||||
if (selected) {
|
||||
menu.selectedIcon
|
||||
selectedIcon
|
||||
} else {
|
||||
menu.unselectedIcon
|
||||
unselectedIcon
|
||||
},
|
||||
menu.text,
|
||||
modifier = Modifier.size(20.dp),
|
||||
text,
|
||||
Modifier.size(20.dp),
|
||||
colorFilter = ColorFilter.tint(MaterialTheme.colors.onSurface)
|
||||
)
|
||||
Spacer(Modifier.width(16.dp))
|
||||
Text(menu.text, color = MaterialTheme.colors.onSurface)
|
||||
Column {
|
||||
Text(text, color = MaterialTheme.colors.onSurface)
|
||||
if (extraInfo != null) {
|
||||
extraInfo()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -157,11 +227,33 @@ fun StartScreen.toRoute() = when (this) {
|
||||
StartScreen.Extensions -> Route.Extensions
|
||||
}
|
||||
|
||||
enum class TopLevelMenus(val text: String, val unselectedIcon: ImageVector, val selectedIcon: ImageVector, val menu: Route) {
|
||||
Library("Library", Icons.Outlined.Book, Icons.Filled.Book, Route.Library),
|
||||
Sources("Sources", Icons.Outlined.Explore, Icons.Filled.Explore, Route.Sources),
|
||||
Extensions("Extensions", Icons.Outlined.Store, Icons.Filled.Store, Route.Extensions),
|
||||
Settings("Settings", Icons.Outlined.Settings, Icons.Filled.Settings, Route.Settings)
|
||||
@Composable
|
||||
fun DownloadsExtraInfo() {
|
||||
val vm = viewModel<DownloadsMenuViewModel>()
|
||||
val list by vm.downloadQueue.collectAsState()
|
||||
if (list.isNotEmpty()) {
|
||||
Text(
|
||||
"${list.size} remaining",
|
||||
style = MaterialTheme.typography.body2,
|
||||
color = LocalContentColor.current.copy(alpha = ContentAlpha.disabled)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
enum class TopLevelMenus(
|
||||
val text: String,
|
||||
val unselectedIcon: ImageVector,
|
||||
val selectedIcon: ImageVector,
|
||||
val menu: Route,
|
||||
val top: Boolean,
|
||||
val openInNewWindow: () -> Unit = {},
|
||||
val extraInfo: (@Composable () -> Unit)? = null
|
||||
) {
|
||||
Library("Library", Icons.Outlined.Book, Icons.Filled.Book, Route.Library, true, ::openLibraryMenu),
|
||||
Sources("Sources", Icons.Outlined.Explore, Icons.Filled.Explore, Route.Sources, true, ::openSourcesMenu),
|
||||
Extensions("Extensions", Icons.Outlined.Store, Icons.Filled.Store, Route.Extensions, true, ::openExtensionsMenu),
|
||||
Downloads("Downloads", Icons.Outlined.Download, Icons.Filled.Download, Route.Downloads, false, extraInfo = { DownloadsExtraInfo() }),
|
||||
Settings("Settings", Icons.Outlined.Settings, Icons.Filled.Settings, Route.Settings, false)
|
||||
}
|
||||
|
||||
sealed class Route {
|
||||
@@ -169,6 +261,7 @@ sealed class Route {
|
||||
object Sources : Route()
|
||||
object Extensions : Route()
|
||||
data class Manga(val mangaId: Long) : Route()
|
||||
object Downloads : Route()
|
||||
|
||||
object Settings : Route()
|
||||
object SettingsGeneral : Route()
|
||||
|
||||
@@ -7,26 +7,19 @@
|
||||
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.layout.requiredSize
|
||||
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
|
||||
@@ -38,26 +31,18 @@ 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.DropdownIconButton
|
||||
import ca.gosyer.ui.base.components.combinedMouseClickable
|
||||
import ca.gosyer.util.compose.contextMenu
|
||||
import java.time.Instant
|
||||
@@ -140,7 +125,7 @@ fun ChapterItem(
|
||||
|
||||
when (downloadState) {
|
||||
MangaMenuViewModel.DownloadState.Downloaded -> {
|
||||
DownloadedIconButton(onClick = { deleteDownload(chapter.index) })
|
||||
DownloadedIconButton(chapter.mangaId to chapter.index, onClick = { deleteDownload(chapter.index) })
|
||||
}
|
||||
MangaMenuViewModel.DownloadState.Downloading -> {
|
||||
DownloadingIconButton(downloadChapter, onClick = { stopDownload(chapter.index) })
|
||||
@@ -167,7 +152,7 @@ private fun DownloadIconButton(onClick: () -> Unit) {
|
||||
Icons.Default.ArrowDownward,
|
||||
null,
|
||||
Modifier
|
||||
.size(22.dp)
|
||||
.requiredSize(22.dp)
|
||||
.padding(2.dp),
|
||||
LocalContentColor.current.copy(alpha = ContentAlpha.disabled)
|
||||
)
|
||||
@@ -178,6 +163,7 @@ private fun DownloadIconButton(onClick: () -> Unit) {
|
||||
@Composable
|
||||
private fun DownloadingIconButton(downloadChapter: DownloadChapter?, onClick: () -> Unit) {
|
||||
DropdownIconButton(
|
||||
downloadChapter?.mangaId to downloadChapter?.chapterIndex,
|
||||
{
|
||||
DropdownMenuItem(onClick = onClick) {
|
||||
Text("Cancel")
|
||||
@@ -187,7 +173,7 @@ private fun DownloadingIconButton(downloadChapter: DownloadChapter?, onClick: ()
|
||||
when (downloadChapter?.state) {
|
||||
null, DownloadState.Queued -> CircularProgressIndicator(
|
||||
Modifier
|
||||
.size(26.dp)
|
||||
.requiredSize(26.dp)
|
||||
.padding(2.dp),
|
||||
LocalContentColor.current.copy(alpha = ContentAlpha.disabled),
|
||||
2.dp
|
||||
@@ -196,7 +182,7 @@ private fun DownloadingIconButton(downloadChapter: DownloadChapter?, onClick: ()
|
||||
CircularProgressIndicator(
|
||||
downloadChapter.progress,
|
||||
Modifier
|
||||
.size(26.dp)
|
||||
.requiredSize(26.dp)
|
||||
.padding(2.dp),
|
||||
LocalContentColor.current.copy(alpha = ContentAlpha.disabled),
|
||||
2.dp
|
||||
@@ -205,14 +191,14 @@ private fun DownloadingIconButton(downloadChapter: DownloadChapter?, onClick: ()
|
||||
Icons.Default.ArrowDownward,
|
||||
null,
|
||||
Modifier
|
||||
.size(22.dp)
|
||||
.requiredSize(22.dp)
|
||||
.padding(2.dp),
|
||||
LocalContentColor.current.copy(alpha = ContentAlpha.disabled)
|
||||
)
|
||||
} else {
|
||||
CircularProgressIndicator(
|
||||
Modifier
|
||||
.size(26.dp)
|
||||
.requiredSize(26.dp)
|
||||
.padding(2.dp),
|
||||
LocalContentColor.current.copy(alpha = ContentAlpha.disabled),
|
||||
2.dp
|
||||
@@ -223,7 +209,7 @@ private fun DownloadingIconButton(downloadChapter: DownloadChapter?, onClick: ()
|
||||
Icons.Default.Error,
|
||||
null,
|
||||
Modifier
|
||||
.size(22.dp)
|
||||
.requiredSize(22.dp)
|
||||
.padding(2.dp),
|
||||
Color.Red
|
||||
)
|
||||
@@ -233,7 +219,7 @@ private fun DownloadingIconButton(downloadChapter: DownloadChapter?, onClick: ()
|
||||
Icons.Default.Check,
|
||||
null,
|
||||
Modifier
|
||||
.size(22.dp)
|
||||
.requiredSize(22.dp)
|
||||
.padding(2.dp),
|
||||
MaterialTheme.colors.surface
|
||||
)
|
||||
@@ -243,8 +229,9 @@ private fun DownloadingIconButton(downloadChapter: DownloadChapter?, onClick: ()
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DownloadedIconButton(onClick: () -> Unit) {
|
||||
private fun DownloadedIconButton(chapter: Pair<Long, Int?>, onClick: () -> Unit) {
|
||||
DropdownIconButton(
|
||||
chapter,
|
||||
{
|
||||
DropdownMenuItem(onClick = onClick) {
|
||||
Text("Delete")
|
||||
@@ -256,47 +243,10 @@ private fun DownloadedIconButton(onClick: () -> Unit) {
|
||||
Icons.Default.Check,
|
||||
null,
|
||||
Modifier
|
||||
.size(22.dp)
|
||||
.requiredSize(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
|
||||
)
|
||||
}
|
||||
|
||||
@@ -22,11 +22,13 @@ import androidx.compose.material.Text
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Home
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import ca.gosyer.BuildConfig
|
||||
import ca.gosyer.data.models.Source
|
||||
import ca.gosyer.ui.base.components.KtorImage
|
||||
import ca.gosyer.ui.base.components.Toolbar
|
||||
@@ -41,9 +43,13 @@ import com.github.zsoltk.compose.savedinstancestate.BundleScope
|
||||
import com.github.zsoltk.compose.savedinstancestate.LocalSavedInstanceState
|
||||
|
||||
fun openSourcesMenu() {
|
||||
ThemedWindow(title = "TachideskJUI - Sources") {
|
||||
SourcesMenu {
|
||||
openMangaMenu(it)
|
||||
ThemedWindow(BuildConfig.NAME) {
|
||||
CompositionLocalProvider(
|
||||
LocalSavedInstanceState provides Bundle()
|
||||
) {
|
||||
SourcesMenu {
|
||||
openMangaMenu(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user