Skip to content

Commit

Permalink
Implement updates menu
Browse files Browse the repository at this point in the history
  • Loading branch information
Syer10 committed Sep 28, 2021
1 parent 385b855 commit ec624c8
Show file tree
Hide file tree
Showing 18 changed files with 629 additions and 210 deletions.
13 changes: 9 additions & 4 deletions src/main/kotlin/ca/gosyer/data/download/DownloadService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class DownloadService @Inject constructor(
private val _downloadQueue = MutableStateFlow(emptyList<DownloadChapter>())
val downloadQueue = _downloadQueue.asStateFlow()

private val watching = mutableMapOf<Long, MutableSharedFlow<List<DownloadChapter>>>()
private val watching = mutableMapOf<Long, MutableSharedFlow<Pair<Long, List<DownloadChapter>>>>()
private var errorConnectionCount = 0

private var job: Job? = null
Expand Down Expand Up @@ -87,7 +87,7 @@ class DownloadService @Inject constructor(
_downloadQueue.value = status.queue
val queue = status.queue.groupBy { it.mangaId }
watching.forEach { (mangaId, flow) ->
flow.emit(queue[mangaId].orEmpty())
flow.emit(mangaId to queue[mangaId].orEmpty())
}
}.throwIfCancellation()
}
Expand All @@ -105,10 +105,15 @@ class DownloadService @Inject constructor(
}

fun registerWatch(mangaId: Long) =
MutableSharedFlow<List<DownloadChapter>>().also { watching[mangaId] = it }.asSharedFlow()
MutableSharedFlow<Pair<Long, List<DownloadChapter>>>().also { watching[mangaId] = it }.asSharedFlow()
fun registerWatches(mangaIds: Set<Long>) =
mangaIds.map { registerWatch(it) }

fun removeWatch(mangaId: Long) {
watching.remove(mangaId)
watching -= mangaId
}
fun removeWatches(mangaIds: Set<Long>) {
watching -= mangaIds
}

enum class Status {
Expand Down
1 change: 1 addition & 0 deletions src/main/kotlin/ca/gosyer/data/models/Chapter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ data class Chapter(
val bookmarked: Boolean,
val lastPageRead: Int,
val index: Int,
val fetchedAt: Long,
val chapterCount: Int?,
val pageCount: Int?,
val lastReadAt: Int?,
Expand Down
15 changes: 15 additions & 0 deletions src/main/kotlin/ca/gosyer/data/models/MangaAndChapter.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* 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.data.models

import kotlinx.serialization.Serializable

@Serializable
data class MangaAndChapter(
val manga: Manga,
val chapter: Chapter
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package ca.gosyer.data.server.interactions

import ca.gosyer.data.models.MangaAndChapter
import ca.gosyer.data.server.Http
import ca.gosyer.data.server.ServerPreferences
import ca.gosyer.data.server.requests.recentUpdatesQuery
import ca.gosyer.util.lang.withIOContext
import io.ktor.client.request.get
import javax.inject.Inject

class UpdatesInteractionHandler @Inject constructor(
client: Http,
serverPreferences: ServerPreferences
) : BaseInteractionHandler(client, serverPreferences) {

suspend fun getRecentUpdates() = withIOContext {
client.get<List<MangaAndChapter>>(
serverUrl + recentUpdatesQuery()
)
}
}
5 changes: 5 additions & 0 deletions src/main/kotlin/ca/gosyer/data/server/requests/Updates.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package ca.gosyer.data.server.requests

@Get
fun recentUpdatesQuery() =
"/api/v1/update/recentChapters"
2 changes: 1 addition & 1 deletion src/main/kotlin/ca/gosyer/data/ui/model/StartScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import kotlinx.serialization.Serializable
@Serializable
enum class StartScreen {
Library,
Updates,

// Updates,
// History,
Sources,
Extensions
Expand Down
227 changes: 227 additions & 0 deletions src/main/kotlin/ca/gosyer/ui/base/components/ChapterDownloadButtons.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
/*
* 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.animation.core.animateFloatAsState
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.ContentAlpha
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.ProgressIndicatorDefaults
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.ArrowDownward
import androidx.compose.material.icons.rounded.Check
import androidx.compose.material.icons.rounded.Error
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import ca.gosyer.data.download.model.DownloadChapter
import ca.gosyer.data.download.model.DownloadState
import ca.gosyer.data.models.Chapter
import ca.gosyer.data.models.Manga
import ca.gosyer.data.server.interactions.ChapterInteractionHandler
import ca.gosyer.ui.base.resources.stringResource
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow

data class ChapterDownloadItem(
val manga: Manga?,
val chapter: Chapter,
private val _downloadState: MutableStateFlow<ChapterDownloadState> = MutableStateFlow(
if (chapter.downloaded) {
ChapterDownloadState.Downloaded
} else {
ChapterDownloadState.NotDownloaded
}
),
private val _downloadChapterFlow: MutableStateFlow<DownloadChapter?> = MutableStateFlow(null)
) {
val downloadState = _downloadState.asStateFlow()
val downloadChapterFlow = _downloadChapterFlow.asStateFlow()

fun updateFrom(downloadingChapters: List<DownloadChapter>) {
val downloadingChapter = downloadingChapters.find {
it.chapterIndex == chapter.index
}
if (downloadingChapter != null && downloadState.value != ChapterDownloadState.Downloading) {
_downloadState.value = ChapterDownloadState.Downloading
}
if (downloadState.value == ChapterDownloadState.Downloading && downloadingChapter == null) {
_downloadState.value = ChapterDownloadState.Downloaded
}
_downloadChapterFlow.value = downloadingChapter
}

suspend fun deleteDownload(chapterHandler: ChapterInteractionHandler) {
chapterHandler.deleteChapterDownload(chapter)
_downloadState.value = ChapterDownloadState.NotDownloaded
}
}

enum class ChapterDownloadState {
NotDownloaded,
Downloading,
Downloaded
}

@Composable
fun ChapterDownloadIcon(
chapter: ChapterDownloadItem,
downloadAChapter: (Chapter) -> Unit,
deleteDownload: (Chapter) -> Unit
) {
val downloadChapter by chapter.downloadChapterFlow.collectAsState()
val downloadState by chapter.downloadState.collectAsState()

when (downloadState) {
ChapterDownloadState.Downloaded -> {
DownloadedIconButton(
chapter.chapter.mangaId to chapter.chapter.index,
onClick = { deleteDownload(chapter.chapter) }
)
}
ChapterDownloadState.Downloading -> {
DownloadingIconButton(
downloadChapter,
onClick = { deleteDownload(chapter.chapter) }
)
}
ChapterDownloadState.NotDownloaded -> {
DownloadIconButton(onClick = { downloadAChapter(chapter.chapter) })
}
}
}

@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.Rounded.ArrowDownward,
null,
Modifier
.size(22.dp)
.padding(2.dp),
LocalContentColor.current.copy(alpha = ContentAlpha.disabled)
)
}
}
}

@Composable
private fun DownloadingIconButton(downloadChapter: DownloadChapter?, onClick: () -> Unit) {
DropdownIconButton(
downloadChapter?.mangaId to downloadChapter?.chapterIndex,
{
DropdownMenuItem(onClick = onClick) {
Text(stringResource("action_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) {
val animatedProgress by animateFloatAsState(
targetValue = downloadChapter.progress,
animationSpec = ProgressIndicatorDefaults.ProgressAnimationSpec
)
CircularProgressIndicator(
animatedProgress,
Modifier
.size(26.dp)
.padding(2.dp),
LocalContentColor.current.copy(alpha = ContentAlpha.disabled),
2.dp
)
Icon(
Icons.Rounded.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.Rounded.Error,
null,
Modifier
.size(22.dp)
.padding(2.dp),
Color.Red
)
}
DownloadState.Finished -> Surface(shape = CircleShape, color = LocalContentColor.current) {
Icon(
Icons.Rounded.Check,
null,
Modifier
.size(22.dp)
.padding(2.dp),
MaterialTheme.colors.surface
)
}
}
}
}

@Composable
private fun DownloadedIconButton(chapter: Pair<Long, Int?>, onClick: () -> Unit) {
DropdownIconButton(
chapter,
{
DropdownMenuItem(onClick = onClick) {
Text(stringResource("action_delete"))
}
}
) {
Surface(shape = CircleShape, color = LocalContentColor.current) {
Icon(
Icons.Rounded.Check,
null,
Modifier
.size(22.dp)
.padding(2.dp),
MaterialTheme.colors.surface
)
}
}
}
Loading

0 comments on commit ec624c8

Please sign in to comment.