mirror of
https://github.com/Suwayomi/TachideskJUI.git
synced 2025-12-09 22:32:04 +01:00
Last part of GQL conversion
This commit is contained in:
@@ -23,18 +23,13 @@ import ca.gosyer.jui.core.lang.throwIfCancellation
|
||||
import ca.gosyer.jui.core.prefs.getAsFlow
|
||||
import ca.gosyer.jui.domain.base.WebsocketService.Actions
|
||||
import ca.gosyer.jui.domain.base.WebsocketService.Status
|
||||
import ca.gosyer.jui.domain.library.model.JobStatus
|
||||
import ca.gosyer.jui.domain.library.model.UpdateStatus
|
||||
import ca.gosyer.jui.domain.library.model.MangaUpdate
|
||||
import ca.gosyer.jui.domain.library.service.LibraryUpdateService
|
||||
import ca.gosyer.jui.domain.library.service.LibraryUpdateService.Companion.status
|
||||
import ca.gosyer.jui.i18n.MR
|
||||
import com.diamondedge.logging.logging
|
||||
import dev.icerock.moko.resources.desc.desc
|
||||
import dev.icerock.moko.resources.format
|
||||
import io.ktor.client.plugins.websocket.ws
|
||||
import io.ktor.http.URLProtocol
|
||||
import io.ktor.websocket.Frame
|
||||
import io.ktor.websocket.readText
|
||||
import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -43,13 +38,9 @@ import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.cancelChildren
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.drop
|
||||
import kotlinx.coroutines.flow.filterIsInstance
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.mapLatest
|
||||
import kotlinx.coroutines.flow.receiveAsFlow
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.job
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
@@ -154,31 +145,12 @@ class AndroidLibraryService : Service() {
|
||||
throw CancellationException()
|
||||
}
|
||||
runCatching {
|
||||
client.ws(
|
||||
host = serverUrl.host,
|
||||
port = serverUrl.port,
|
||||
path = serverUrl.encodedPath + "/api/v1/update",
|
||||
request = {
|
||||
if (serverUrl.protocol == URLProtocol.HTTPS) {
|
||||
url.protocol = URLProtocol.WSS
|
||||
}
|
||||
},
|
||||
) {
|
||||
errorConnectionCount = 0
|
||||
status.value = Status.RUNNING
|
||||
send(Frame.Text("STATUS"))
|
||||
|
||||
incoming.receiveAsFlow()
|
||||
.filterIsInstance<Frame.Text>()
|
||||
.map { json.decodeFromString<UpdateStatus>(it.readText()) }
|
||||
.distinctUntilChanged()
|
||||
.drop(1)
|
||||
.mapLatest(::onReceived)
|
||||
.catch {
|
||||
log.warn(it) { "Error running library update" }
|
||||
}
|
||||
.collect()
|
||||
}
|
||||
appComponent.libraryUpdateService
|
||||
.getSubscription()
|
||||
.onEach {
|
||||
onReceived()
|
||||
}
|
||||
.collect()
|
||||
}.throwIfCancellation().isFailure.let {
|
||||
status.value = Status.STARTING
|
||||
if (it) errorConnectionCount++
|
||||
@@ -193,20 +165,16 @@ class AndroidLibraryService : Service() {
|
||||
.launchIn(ioScope)
|
||||
}
|
||||
|
||||
private fun onReceived(status: UpdateStatus) {
|
||||
LibraryUpdateService.updateStatus.value = status
|
||||
private fun onReceived() {
|
||||
val status = LibraryUpdateService.updateStatus.value
|
||||
|
||||
val complete = status.mangaStatusMap[JobStatus.COMPLETE]?.size ?: 0
|
||||
val failed = status.mangaStatusMap[JobStatus.FAILED]?.size ?: 0
|
||||
val running = status.mangaStatusMap[JobStatus.RUNNING]?.size ?: 0
|
||||
val pending = status.mangaStatusMap[JobStatus.PENDING]?.size ?: 0
|
||||
val skipped = status.mangaStatusMap[JobStatus.SKIPPED]?.size ?: 0
|
||||
val total = complete + failed + running + pending + skipped
|
||||
val current = complete + failed + skipped
|
||||
val total = status.jobsInfo.totalJobs
|
||||
val current = status.jobsInfo.finishedJobs
|
||||
if (current != total) {
|
||||
val notification = with(progressNotificationBuilder) {
|
||||
val updatingText = status.mangaStatusMap[JobStatus.RUNNING]
|
||||
?.joinToString("\n") { it.title.chop(40) }
|
||||
val updatingText = status.mangaUpdates
|
||||
.filter { it.status == MangaUpdate.Status.RUNNING }
|
||||
.joinToString("\n") { it.manga.title.chop(40) }
|
||||
setContentTitle(
|
||||
MR.strings.notification_updating
|
||||
.format(current, total)
|
||||
|
||||
@@ -57,10 +57,14 @@ mutation DeleteCategory($categoryId: Int!) {
|
||||
}
|
||||
}
|
||||
|
||||
query GetCategoryManga($categoryId: [Int!]!) {
|
||||
mangas(orderBy: ID, condition: {categoryIds: $categoryId}) {
|
||||
nodes {
|
||||
...MangaFragment
|
||||
query GetCategoryManga($categoryId: Int!) {
|
||||
category(id: $categoryId) {
|
||||
id
|
||||
mangas {
|
||||
nodes {
|
||||
...LibraryMangaFragment
|
||||
}
|
||||
totalCount
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,3 +3,43 @@ mutation SetMangaInLibrary($id: Int!, $inLibrary: Boolean!) {
|
||||
clientMutationId
|
||||
}
|
||||
}
|
||||
|
||||
query LibraryUpdateStatus {
|
||||
libraryUpdateStatus {
|
||||
categoryUpdates {
|
||||
...CategoryUpdateFragment
|
||||
}
|
||||
jobsInfo {
|
||||
...UpdaterJobsInfoFragment
|
||||
}
|
||||
mangaUpdates {
|
||||
...MangaUpdateFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
subscription LibraryUpdateStatusChanged($maxUpdates: Int = 30) {
|
||||
libraryUpdateStatusChanged(input: {maxUpdates: $maxUpdates}) {
|
||||
initial {
|
||||
categoryUpdates {
|
||||
...CategoryUpdateFragment
|
||||
}
|
||||
jobsInfo {
|
||||
...UpdaterJobsInfoFragment
|
||||
}
|
||||
mangaUpdates {
|
||||
...MangaUpdateFragment
|
||||
}
|
||||
}
|
||||
categoryUpdates {
|
||||
...CategoryUpdateFragment
|
||||
}
|
||||
mangaUpdates {
|
||||
...MangaUpdateFragment
|
||||
}
|
||||
jobsInfo {
|
||||
...UpdaterJobsInfoFragment
|
||||
}
|
||||
omittedUpdates
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
fragment CategoryUpdateFragment on CategoryUpdateType {
|
||||
category {
|
||||
id
|
||||
name
|
||||
}
|
||||
status
|
||||
}
|
||||
|
||||
fragment UpdaterJobsInfoFragment on UpdaterJobsInfoType {
|
||||
finishedJobs
|
||||
isRunning
|
||||
skippedCategoriesCount
|
||||
skippedMangasCount
|
||||
totalJobs
|
||||
}
|
||||
|
||||
fragment MangaUpdateFragment on MangaUpdateType {
|
||||
manga {
|
||||
id
|
||||
title
|
||||
}
|
||||
status
|
||||
}
|
||||
@@ -20,7 +20,6 @@ import ca.gosyer.jui.data.graphql.SetCategoryMetaMutation
|
||||
import ca.gosyer.jui.data.graphql.fragment.CategoryFragment
|
||||
import ca.gosyer.jui.data.manga.MangaRepositoryImpl.Companion.toManga
|
||||
import ca.gosyer.jui.domain.category.model.Category
|
||||
import ca.gosyer.jui.domain.category.model.CategoryMeta
|
||||
import ca.gosyer.jui.domain.category.service.CategoryRepository
|
||||
import ca.gosyer.jui.domain.manga.model.Manga
|
||||
import ca.gosyer.jui.domain.server.Http
|
||||
@@ -133,12 +132,12 @@ class CategoryRepositoryImpl(
|
||||
|
||||
override fun getMangaFromCategory(categoryId: Long): Flow<List<Manga>> =
|
||||
apolloClient.query(
|
||||
GetCategoryMangaQuery(listOf(categoryId.toInt())),
|
||||
GetCategoryMangaQuery(categoryId.toInt()),
|
||||
)
|
||||
.toFlow()
|
||||
.map {
|
||||
val data = it.dataAssertNoErrors
|
||||
data.mangas.nodes.map { it.mangaFragment.toManga() }
|
||||
data.category.mangas.nodes.map { it.libraryMangaFragment.toManga() }
|
||||
}
|
||||
|
||||
override fun updateCategoryMeta(
|
||||
@@ -162,7 +161,7 @@ class CategoryRepositoryImpl(
|
||||
order = order,
|
||||
name = name,
|
||||
default = default,
|
||||
meta = CategoryMeta(),
|
||||
meta = Category.CategoryMeta(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ import ca.gosyer.jui.data.graphql.fragment.ChapterWithMangaFragment
|
||||
import ca.gosyer.jui.data.graphql.type.UpdateChapterPatchInput
|
||||
import ca.gosyer.jui.data.manga.MangaRepositoryImpl.Companion.toManga
|
||||
import ca.gosyer.jui.domain.chapter.model.Chapter
|
||||
import ca.gosyer.jui.domain.chapter.model.ChapterMeta
|
||||
import ca.gosyer.jui.domain.chapter.service.ChapterRepository
|
||||
import ca.gosyer.jui.domain.server.Http
|
||||
import ca.gosyer.jui.domain.updates.model.MangaAndChapter
|
||||
@@ -190,7 +189,7 @@ class ChapterRepositoryImpl(
|
||||
pageCount = pageCount,
|
||||
lastReadAt = lastPageRead,
|
||||
downloaded = isDownloaded,
|
||||
meta = ChapterMeta(
|
||||
meta = Chapter.ChapterMeta(
|
||||
juiPageOffset = meta.find { it.key == "juiPageOffset" }?.value?.toIntOrNull() ?: 0,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -1,7 +1,19 @@
|
||||
package ca.gosyer.jui.data.library
|
||||
|
||||
import ca.gosyer.jui.data.ApolloAppClient
|
||||
import ca.gosyer.jui.data.graphql.LibraryUpdateStatusChangedSubscription
|
||||
import ca.gosyer.jui.data.graphql.LibraryUpdateStatusQuery
|
||||
import ca.gosyer.jui.data.graphql.SetMangaInLibraryMutation
|
||||
import ca.gosyer.jui.data.graphql.fragment.CategoryUpdateFragment
|
||||
import ca.gosyer.jui.data.graphql.fragment.MangaUpdateFragment
|
||||
import ca.gosyer.jui.data.graphql.fragment.UpdaterJobsInfoFragment
|
||||
import ca.gosyer.jui.data.graphql.type.CategoryJobStatus
|
||||
import ca.gosyer.jui.data.graphql.type.MangaJobStatus
|
||||
import ca.gosyer.jui.domain.library.model.CategoryUpdate
|
||||
import ca.gosyer.jui.domain.library.model.MangaUpdate
|
||||
import ca.gosyer.jui.domain.library.model.UpdateStatus
|
||||
import ca.gosyer.jui.domain.library.model.UpdaterJobsInfo
|
||||
import ca.gosyer.jui.domain.library.model.UpdaterUpdates
|
||||
import ca.gosyer.jui.domain.library.service.LibraryRepository
|
||||
import ca.gosyer.jui.domain.server.Http
|
||||
import com.apollographql.apollo.ApolloClient
|
||||
@@ -33,4 +45,92 @@ class LibraryRepositoryImpl(
|
||||
override fun addMangaToLibrary(mangaId: Long): Flow<Unit> = setMangaInLibrary(mangaId, true)
|
||||
|
||||
override fun removeMangaFromLibrary(mangaId: Long): Flow<Unit> = setMangaInLibrary(mangaId, false)
|
||||
|
||||
override fun libraryUpdateSubscription(): Flow<UpdaterUpdates> =
|
||||
apolloClient.subscription(
|
||||
LibraryUpdateStatusChangedSubscription(),
|
||||
)
|
||||
.toFlow()
|
||||
.map {
|
||||
val data = it.dataAssertNoErrors.libraryUpdateStatusChanged
|
||||
UpdaterUpdates(
|
||||
data.initial?.let {
|
||||
UpdateStatus(
|
||||
it.categoryUpdates.map {
|
||||
it.categoryUpdateFragment.toCategoryUpdate()
|
||||
},
|
||||
it.mangaUpdates.map {
|
||||
it.mangaUpdateFragment.toMangaUpdate()
|
||||
},
|
||||
it.jobsInfo.updaterJobsInfoFragment.toUpdaterJobsInfo(),
|
||||
)
|
||||
},
|
||||
data.categoryUpdates.map {
|
||||
it.categoryUpdateFragment.toCategoryUpdate()
|
||||
},
|
||||
data.mangaUpdates.map {
|
||||
it.mangaUpdateFragment.toMangaUpdate()
|
||||
},
|
||||
data.jobsInfo.updaterJobsInfoFragment.toUpdaterJobsInfo(),
|
||||
data.omittedUpdates,
|
||||
)
|
||||
}
|
||||
|
||||
override fun libraryUpdateStatus(): Flow<UpdateStatus> =
|
||||
apolloClient.query(
|
||||
LibraryUpdateStatusQuery(),
|
||||
)
|
||||
.toFlow()
|
||||
.map {
|
||||
val data = it.dataAssertNoErrors.libraryUpdateStatus
|
||||
UpdateStatus(
|
||||
data.categoryUpdates.map {
|
||||
it.categoryUpdateFragment.toCategoryUpdate()
|
||||
},
|
||||
data.mangaUpdates.map {
|
||||
it.mangaUpdateFragment.toMangaUpdate()
|
||||
},
|
||||
data.jobsInfo.updaterJobsInfoFragment.toUpdaterJobsInfo(),
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun UpdaterJobsInfoFragment.toUpdaterJobsInfo() =
|
||||
UpdaterJobsInfo(
|
||||
finishedJobs,
|
||||
isRunning,
|
||||
skippedCategoriesCount,
|
||||
skippedMangasCount,
|
||||
totalJobs,
|
||||
)
|
||||
|
||||
fun CategoryUpdateFragment.toCategoryUpdate() =
|
||||
CategoryUpdate(
|
||||
CategoryUpdate.UpdateCategory(
|
||||
category.id.toLong(),
|
||||
category.name,
|
||||
),
|
||||
when (status) {
|
||||
CategoryJobStatus.UPDATING -> CategoryUpdate.Status.UPDATING
|
||||
CategoryJobStatus.SKIPPED -> CategoryUpdate.Status.SKIPPED
|
||||
CategoryJobStatus.UNKNOWN__ -> CategoryUpdate.Status.UPDATING
|
||||
},
|
||||
)
|
||||
|
||||
fun MangaUpdateFragment.toMangaUpdate() =
|
||||
MangaUpdate(
|
||||
MangaUpdate.UpdateManga(
|
||||
manga.id.toLong(),
|
||||
manga.title,
|
||||
),
|
||||
when (status) {
|
||||
MangaJobStatus.PENDING -> MangaUpdate.Status.PENDING
|
||||
MangaJobStatus.RUNNING -> MangaUpdate.Status.RUNNING
|
||||
MangaJobStatus.COMPLETE -> MangaUpdate.Status.COMPLETE
|
||||
MangaJobStatus.FAILED -> MangaUpdate.Status.FAILED
|
||||
MangaJobStatus.SKIPPED -> MangaUpdate.Status.SKIPPED
|
||||
MangaJobStatus.UNKNOWN__ -> MangaUpdate.Status.FAILED
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ suspend fun main() {
|
||||
.filter { it == ServerResult.STARTED || it == ServerResult.UNUSED }
|
||||
.onEach {
|
||||
appComponent.downloadService.getSubscription().launchIn(GlobalScope)
|
||||
appComponent.libraryUpdateService.init()
|
||||
appComponent.libraryUpdateService.getSubscription().launchIn(GlobalScope)
|
||||
}
|
||||
.launchIn(GlobalScope)
|
||||
|
||||
|
||||
@@ -113,11 +113,6 @@ interface SharedDomainComponent : CoreComponent {
|
||||
val serverHostPreferencesFactory: ServerHostPreferences
|
||||
get() = ServerHostPreferences(preferenceFactory.create("host"))
|
||||
|
||||
@get:AppScope
|
||||
@get:Provides
|
||||
val libraryUpdateServiceFactory: LibraryUpdateService
|
||||
get() = LibraryUpdateService(serverPreferences, http)
|
||||
|
||||
@get:AppScope
|
||||
@get:Provides
|
||||
val serverListenersFactory: ServerListeners
|
||||
|
||||
@@ -6,9 +6,6 @@
|
||||
|
||||
package ca.gosyer.jui.domain.backup.model
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class BackupValidationResult(
|
||||
val missingSources: List<String>,
|
||||
val missingTrackers: List<String>,
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
|
||||
package ca.gosyer.jui.domain.backup.model
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
enum class RestoreState {
|
||||
IDLE,
|
||||
SUCCESS,
|
||||
@@ -19,7 +17,6 @@ enum class RestoreState {
|
||||
UNKNOWN,
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class RestoreStatus(
|
||||
val state: RestoreState,
|
||||
val completed: Int,
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
package ca.gosyer.jui.domain.category.interactor
|
||||
|
||||
import ca.gosyer.jui.domain.ServerListeners
|
||||
import ca.gosyer.jui.domain.category.service.CategoryRepository
|
||||
import com.diamondedge.logging.logging
|
||||
import kotlinx.coroutines.flow.catch
|
||||
@@ -16,6 +17,7 @@ import me.tatarka.inject.annotations.Inject
|
||||
@Inject
|
||||
class GetCategories(
|
||||
private val categoryRepository: CategoryRepository,
|
||||
private val serverListeners: ServerListeners,
|
||||
) {
|
||||
suspend fun await(
|
||||
dropDefault: Boolean = false,
|
||||
@@ -27,15 +29,16 @@ class GetCategories(
|
||||
}
|
||||
.singleOrNull()
|
||||
|
||||
fun asFlow(dropDefault: Boolean = false) =
|
||||
fun asFlow(dropDefault: Boolean = false) = serverListeners.combineCategoryManga(
|
||||
categoryRepository.getCategories()
|
||||
.map { categories ->
|
||||
if (dropDefault) {
|
||||
categories.filterNot { it.name.equals("default", true) }
|
||||
} else {
|
||||
categories
|
||||
}
|
||||
}
|
||||
).map { categories ->
|
||||
if (dropDefault) {
|
||||
categories.filterNot { it.name.equals("default", true) }
|
||||
} else {
|
||||
categories
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
private val log = logging()
|
||||
|
||||
@@ -7,9 +7,7 @@
|
||||
package ca.gosyer.jui.domain.category.model
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@Immutable
|
||||
data class Category(
|
||||
val id: Long,
|
||||
@@ -17,10 +15,9 @@ data class Category(
|
||||
val name: String,
|
||||
val default: Boolean,
|
||||
val meta: CategoryMeta,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
@Immutable
|
||||
data class CategoryMeta(
|
||||
val example: Int = 0,
|
||||
)
|
||||
) {
|
||||
@Immutable
|
||||
data class CategoryMeta(
|
||||
val example: Int = 0,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -7,9 +7,7 @@
|
||||
package ca.gosyer.jui.domain.chapter.model
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@Immutable
|
||||
data class Chapter(
|
||||
val id: Long,
|
||||
@@ -29,10 +27,10 @@ data class Chapter(
|
||||
val lastReadAt: Int?,
|
||||
val downloaded: Boolean,
|
||||
val meta: ChapterMeta,
|
||||
)
|
||||
) {
|
||||
@Immutable
|
||||
data class ChapterMeta(
|
||||
val juiPageOffset: Int = 0,
|
||||
)
|
||||
}
|
||||
|
||||
@Serializable
|
||||
@Immutable
|
||||
data class ChapterMeta(
|
||||
val juiPageOffset: Int = 0,
|
||||
)
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
/*
|
||||
* 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.jui.domain.chapter.model
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class ChapterBatchEditInput(
|
||||
val chapterIds: List<Long>? = null,
|
||||
val change: ChapterChange?,
|
||||
)
|
||||
@@ -1,17 +0,0 @@
|
||||
/*
|
||||
* 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.jui.domain.chapter.model
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class ChapterChange(
|
||||
val isRead: Boolean? = null,
|
||||
val isBookmarked: Boolean? = null,
|
||||
val lastPageRead: Int? = null,
|
||||
val delete: Boolean? = null,
|
||||
)
|
||||
@@ -1,16 +0,0 @@
|
||||
/*
|
||||
* 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.jui.domain.chapter.model
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class MangaChapterBatchEditInput(
|
||||
val chapterIds: List<Long>? = null,
|
||||
val chapterIndexes: List<Int>? = null,
|
||||
val change: ChapterChange?,
|
||||
)
|
||||
@@ -1,14 +0,0 @@
|
||||
/*
|
||||
* 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.jui.domain.download.model
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class DownloadEnqueue(
|
||||
val chapterIds: List<Long>,
|
||||
)
|
||||
@@ -7,9 +7,7 @@
|
||||
package ca.gosyer.jui.domain.extension.model
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@Immutable
|
||||
data class Extension(
|
||||
val name: String,
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package ca.gosyer.jui.domain.library.model
|
||||
|
||||
data class CategoryUpdate(
|
||||
val category: UpdateCategory,
|
||||
val status: Status,
|
||||
) {
|
||||
enum class Status {
|
||||
UPDATING,
|
||||
SKIPPED
|
||||
}
|
||||
|
||||
data class UpdateCategory(
|
||||
val id: Long,
|
||||
val name: String,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
/*
|
||||
* 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.jui.domain.library.model
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@Stable
|
||||
enum class CategoryUpdateStatus {
|
||||
UPDATING,
|
||||
SKIPPED,
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
/*
|
||||
* 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.jui.domain.library.model
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@Stable
|
||||
enum class JobStatus {
|
||||
PENDING,
|
||||
RUNNING,
|
||||
COMPLETE,
|
||||
FAILED,
|
||||
SKIPPED,
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package ca.gosyer.jui.domain.library.model
|
||||
|
||||
data class MangaUpdate(
|
||||
val manga: UpdateManga,
|
||||
val status: Status,
|
||||
) {
|
||||
enum class Status {
|
||||
PENDING,
|
||||
RUNNING,
|
||||
COMPLETE,
|
||||
FAILED,
|
||||
SKIPPED
|
||||
}
|
||||
|
||||
data class UpdateManga(
|
||||
val id: Long,
|
||||
val title: String,
|
||||
)
|
||||
}
|
||||
@@ -6,15 +6,8 @@
|
||||
|
||||
package ca.gosyer.jui.domain.library.model
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import ca.gosyer.jui.domain.category.model.Category
|
||||
import ca.gosyer.jui.domain.manga.model.Manga
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@Immutable
|
||||
data class UpdateStatus(
|
||||
val categoryStatusMap: Map<CategoryUpdateStatus, List<Category>> = emptyMap(),
|
||||
val mangaStatusMap: Map<JobStatus, List<Manga>> = emptyMap(),
|
||||
val running: Boolean,
|
||||
val categoryUpdates: List<CategoryUpdate>,
|
||||
val mangaUpdates: List<MangaUpdate>,
|
||||
val jobsInfo: UpdaterJobsInfo,
|
||||
)
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
package ca.gosyer.jui.domain.library.model
|
||||
|
||||
data class UpdaterJobsInfo(
|
||||
val finishedJobs: Int,
|
||||
val isRunning: Boolean,
|
||||
val skippedCategoriesCount: Int,
|
||||
val skippedMangasCount: Int,
|
||||
val totalJobs: Int,
|
||||
)
|
||||
@@ -0,0 +1,9 @@
|
||||
package ca.gosyer.jui.domain.library.model
|
||||
|
||||
data class UpdaterUpdates(
|
||||
val initial: UpdateStatus?,
|
||||
val categoryUpdates: List<CategoryUpdate>,
|
||||
val mangaUpdates: List<MangaUpdate>,
|
||||
val jobsInfo: UpdaterJobsInfo,
|
||||
val omittedUpdates: Boolean,
|
||||
)
|
||||
@@ -6,10 +6,17 @@
|
||||
|
||||
package ca.gosyer.jui.domain.library.service
|
||||
|
||||
import ca.gosyer.jui.domain.library.model.UpdateStatus
|
||||
import ca.gosyer.jui.domain.library.model.UpdaterUpdates
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface LibraryRepository {
|
||||
fun addMangaToLibrary(mangaId: Long): Flow<Unit>
|
||||
|
||||
fun removeMangaFromLibrary(mangaId: Long): Flow<Unit>
|
||||
|
||||
|
||||
fun libraryUpdateSubscription(): Flow<UpdaterUpdates>
|
||||
|
||||
fun libraryUpdateStatus(): Flow<UpdateStatus>
|
||||
}
|
||||
|
||||
@@ -8,33 +8,73 @@ package ca.gosyer.jui.domain.library.service
|
||||
|
||||
import ca.gosyer.jui.domain.base.WebsocketService
|
||||
import ca.gosyer.jui.domain.library.model.UpdateStatus
|
||||
import ca.gosyer.jui.domain.server.Http
|
||||
import ca.gosyer.jui.domain.server.service.ServerPreferences
|
||||
import ca.gosyer.jui.domain.library.model.UpdaterJobsInfo
|
||||
import com.diamondedge.logging.logging
|
||||
import io.ktor.websocket.Frame
|
||||
import io.ktor.websocket.readText
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onStart
|
||||
import kotlinx.coroutines.flow.update
|
||||
import me.tatarka.inject.annotations.Inject
|
||||
|
||||
@Inject
|
||||
class LibraryUpdateService(
|
||||
serverPreferences: ServerPreferences,
|
||||
client: Http,
|
||||
) : WebsocketService(serverPreferences, client) {
|
||||
override val status: MutableStateFlow<Status>
|
||||
get() = LibraryUpdateService.status
|
||||
private val libraryUpdateRepository: LibraryRepository,
|
||||
) {
|
||||
fun getSubscription() = libraryUpdateRepository.libraryUpdateSubscription()
|
||||
.onStart {
|
||||
log.info { "Starting library update status subscription" }
|
||||
status.value = WebsocketService.Status.STARTING
|
||||
}
|
||||
.catch { error ->
|
||||
log.error(error) { "Error in library update status subscription" }
|
||||
status.value = WebsocketService.Status.STOPPED
|
||||
}
|
||||
.map { updates ->
|
||||
status.value = WebsocketService.Status.RUNNING
|
||||
if (updates.omittedUpdates) {
|
||||
log.info { "Omitted updates detected, fetching fresh library update status" }
|
||||
fetchLibraryUpdateStatus()
|
||||
return@map
|
||||
}
|
||||
if (updates.initial != null) {
|
||||
updateStatus.value = updates.initial
|
||||
}
|
||||
updates.jobsInfo.let { jobsInfo ->
|
||||
updateStatus.update {
|
||||
it.copy(
|
||||
jobsInfo = jobsInfo,
|
||||
categoryUpdates = updates.categoryUpdates,
|
||||
mangaUpdates = updates.mangaUpdates
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val query: String
|
||||
get() = "/api/v1/update"
|
||||
|
||||
override suspend fun onReceived(frame: Frame.Text) {
|
||||
updateStatus.value = json.decodeFromString<UpdateStatus>(frame.readText())
|
||||
private suspend fun fetchLibraryUpdateStatus() {
|
||||
val status = libraryUpdateRepository.libraryUpdateStatus().firstOrNull()
|
||||
if (status != null) {
|
||||
updateStatus.value = status
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val log = logging()
|
||||
|
||||
val status = MutableStateFlow(Status.STARTING)
|
||||
val updateStatus = MutableStateFlow(UpdateStatus(emptyMap(), emptyMap(), false))
|
||||
val status = MutableStateFlow(WebsocketService.Status.STARTING)
|
||||
val updateStatus = MutableStateFlow(
|
||||
UpdateStatus(
|
||||
emptyList(),
|
||||
emptyList(),
|
||||
UpdaterJobsInfo(
|
||||
0,
|
||||
false,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,10 +11,8 @@ import androidx.compose.runtime.Stable
|
||||
import ca.gosyer.jui.domain.source.model.Source
|
||||
import ca.gosyer.jui.i18n.MR
|
||||
import dev.icerock.moko.resources.StringResource
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
|
||||
@Serializable
|
||||
@Immutable
|
||||
data class Manga(
|
||||
val id: Long,
|
||||
@@ -46,7 +44,6 @@ data class Manga(
|
||||
val chaptersAge: Long?,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
@Immutable
|
||||
data class MangaMeta(
|
||||
val juiReaderMode: String = DEFAULT_READER_MODE,
|
||||
@@ -56,7 +53,6 @@ data class MangaMeta(
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
@Stable
|
||||
enum class MangaStatus(
|
||||
@Transient val res: StringResource,
|
||||
@@ -70,7 +66,6 @@ enum class MangaStatus(
|
||||
ON_HIATUS(MR.strings.status_on_hiatus),
|
||||
}
|
||||
|
||||
@Serializable
|
||||
@Stable
|
||||
enum class UpdateStrategy {
|
||||
ALWAYS_UPDATE,
|
||||
|
||||
@@ -8,9 +8,7 @@ package ca.gosyer.jui.domain.settings.model
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.runtime.Stable
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@Immutable
|
||||
data class About(
|
||||
val name: String,
|
||||
@@ -21,7 +19,6 @@ data class About(
|
||||
val discord: String,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
@Stable
|
||||
enum class AboutBuildType {
|
||||
Preview,
|
||||
|
||||
@@ -8,9 +8,7 @@ package ca.gosyer.jui.domain.source.model
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import ca.gosyer.jui.domain.manga.model.Manga
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@Stable
|
||||
data class MangaPage(
|
||||
val mangaList: List<Manga>,
|
||||
|
||||
@@ -7,10 +7,8 @@
|
||||
package ca.gosyer.jui.domain.source.model
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import kotlinx.serialization.Serializable
|
||||
import ca.gosyer.jui.core.io.Serializable as JvmSerializable
|
||||
|
||||
@Serializable
|
||||
@Stable
|
||||
data class Source(
|
||||
val id: Long,
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
/*
|
||||
* 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.jui.domain.source.model.sourcefilters
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@SerialName("CheckBox")
|
||||
data class CheckBoxFilterOld(
|
||||
override val filter: CheckBoxProps,
|
||||
) : SourceFilterOld() {
|
||||
@Serializable
|
||||
data class CheckBoxProps(
|
||||
override val name: String,
|
||||
override val state: Boolean,
|
||||
) : Props<Boolean>
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
/*
|
||||
* 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.jui.domain.source.model.sourcefilters
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@SerialName("Group")
|
||||
data class GroupFilterOld(
|
||||
override val filter: GroupProps,
|
||||
) : SourceFilterOld() {
|
||||
@Serializable
|
||||
data class GroupProps(
|
||||
override val name: String,
|
||||
override val state: List<SourceFilterOld>,
|
||||
) : Props<List<SourceFilterOld>>
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* 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.jui.domain.source.model.sourcefilters
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
|
||||
@Serializable
|
||||
@SerialName("Header")
|
||||
data class HeaderFilterOld(
|
||||
override val filter: HeaderProps,
|
||||
) : SourceFilterOld() {
|
||||
@Serializable
|
||||
data class HeaderProps(
|
||||
override val name: String,
|
||||
@Transient
|
||||
override val state: Int = 0,
|
||||
) : Props<Int>
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* 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.jui.domain.source.model.sourcefilters
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
|
||||
@Serializable
|
||||
@SerialName("Select")
|
||||
data class SelectFilterOld(
|
||||
override val filter: SelectProps,
|
||||
) : SourceFilterOld() {
|
||||
@Serializable
|
||||
data class SelectProps(
|
||||
override val name: String,
|
||||
override val state: Int,
|
||||
val values: List<JsonElement>,
|
||||
val displayValues: List<String>? = null,
|
||||
) : Props<Int>
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* 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.jui.domain.source.model.sourcefilters
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
|
||||
@Serializable
|
||||
@SerialName("Separator")
|
||||
data class SeparatorFilterOld(
|
||||
override val filter: SeparatorProps,
|
||||
) : SourceFilterOld() {
|
||||
@Serializable
|
||||
data class SeparatorProps(
|
||||
override val name: String,
|
||||
@Transient
|
||||
override val state: Int = 0,
|
||||
) : Props<Int>
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* 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.jui.domain.source.model.sourcefilters
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@SerialName("Sort")
|
||||
data class SortFilterOld(
|
||||
override val filter: SortProps,
|
||||
) : SourceFilterOld() {
|
||||
@Serializable
|
||||
data class SortProps(
|
||||
override val name: String,
|
||||
override val state: Selection?,
|
||||
val values: List<String>,
|
||||
) : Props<Selection?>
|
||||
|
||||
@Serializable
|
||||
data class Selection(
|
||||
val index: Int,
|
||||
val ascending: Boolean,
|
||||
)
|
||||
}
|
||||
@@ -6,23 +6,6 @@
|
||||
|
||||
package ca.gosyer.jui.domain.source.model.sourcefilters
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
@Serializable
|
||||
data class SourceFilterChangeOld(
|
||||
val position: Int,
|
||||
val state: String,
|
||||
) {
|
||||
constructor(position: Int, state: Any) : this(
|
||||
position,
|
||||
if (state is SortFilterOld.Selection) {
|
||||
Json.encodeToString(state)
|
||||
} else {
|
||||
state.toString()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
sealed interface SourceFilter {
|
||||
val position: Int
|
||||
@@ -1,15 +0,0 @@
|
||||
/*
|
||||
* 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.jui.domain.source.model.sourcefilters
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class SourceFilterData(
|
||||
val searchTerm: String?,
|
||||
val filter: List<SourceFilterChangeOld>?,
|
||||
)
|
||||
@@ -1,19 +0,0 @@
|
||||
/*
|
||||
* 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.jui.domain.source.model.sourcefilters
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
sealed class SourceFilterOld {
|
||||
abstract val filter: Props<*>
|
||||
}
|
||||
|
||||
interface Props<T> {
|
||||
val name: String
|
||||
val state: T
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
/*
|
||||
* 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.jui.domain.source.model.sourcefilters
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@SerialName("Text")
|
||||
data class TextFilterOld(
|
||||
override val filter: TextProps,
|
||||
) : SourceFilterOld() {
|
||||
@Serializable
|
||||
data class TextProps(
|
||||
override val name: String,
|
||||
override val state: String,
|
||||
) : Props<String>
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
/*
|
||||
* 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.jui.domain.source.model.sourcefilters
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@SerialName("TriState")
|
||||
data class TriStateFilterOld(
|
||||
override val filter: TriStateProps,
|
||||
) : SourceFilterOld() {
|
||||
@Serializable
|
||||
data class TriStateProps(
|
||||
override val name: String,
|
||||
override val state: Int,
|
||||
) : Props<Int>
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
/*
|
||||
* 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.jui.domain.source.model.sourcepreference
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@SerialName("CheckBoxPreference")
|
||||
@Immutable
|
||||
data class CheckBoxPreferenceOld(
|
||||
override val props: TwoStateProps,
|
||||
) : SourcePreferenceOld()
|
||||
@@ -1,32 +0,0 @@
|
||||
/*
|
||||
* 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.jui.domain.source.model.sourcepreference
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@SerialName("EditTextPreference")
|
||||
@Immutable
|
||||
data class EditTextPreferenceOld(
|
||||
override val props: EditTextProps,
|
||||
) : SourcePreferenceOld() {
|
||||
@Serializable
|
||||
@Immutable
|
||||
data class EditTextProps(
|
||||
override val key: String,
|
||||
override val title: String?,
|
||||
override val summary: String?,
|
||||
override val currentValue: String?,
|
||||
override val defaultValue: String?,
|
||||
override val defaultValueType: String,
|
||||
val dialogTitle: String?,
|
||||
val dialogMessage: String?,
|
||||
val text: String?,
|
||||
) : Props<String?>
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
/*
|
||||
* 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.jui.domain.source.model.sourcepreference
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@SerialName("ListPreference")
|
||||
@Immutable
|
||||
data class ListPreferenceOld(
|
||||
override val props: ListProps,
|
||||
) : SourcePreferenceOld() {
|
||||
@Serializable
|
||||
@Immutable
|
||||
data class ListProps(
|
||||
override val key: String,
|
||||
override val title: String,
|
||||
override val summary: String?,
|
||||
override val currentValue: String?,
|
||||
override val defaultValue: String?,
|
||||
override val defaultValueType: String,
|
||||
val entries: List<String>,
|
||||
val entryValues: List<String>,
|
||||
) : Props<String?>
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* 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.jui.domain.source.model.sourcepreference
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@SerialName("MultiSelectListPreference")
|
||||
@Immutable
|
||||
data class MultiSelectListPreferenceOld(
|
||||
override val props: MultiSelectListProps,
|
||||
) : SourcePreferenceOld() {
|
||||
@Serializable
|
||||
@Immutable
|
||||
data class MultiSelectListProps(
|
||||
override val key: String,
|
||||
override val title: String,
|
||||
override val summary: String?,
|
||||
override val currentValue: List<String>?,
|
||||
override val defaultValue: List<String>?,
|
||||
override val defaultValueType: String,
|
||||
val dialogTitle: String?,
|
||||
val dialogMessage: String?,
|
||||
val entries: List<String>,
|
||||
val entryValues: List<String>,
|
||||
) : Props<List<String>?>
|
||||
}
|
||||
@@ -6,25 +6,6 @@
|
||||
|
||||
package ca.gosyer.jui.domain.source.model.sourcepreference
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@Immutable
|
||||
sealed class SourcePreferenceOld {
|
||||
abstract val props: Props<*>
|
||||
}
|
||||
|
||||
@Immutable
|
||||
interface Props<T> {
|
||||
val key: String
|
||||
val title: String?
|
||||
val summary: String?
|
||||
val currentValue: T
|
||||
val defaultValue: T
|
||||
val defaultValueType: String
|
||||
}
|
||||
|
||||
sealed interface SourcePreference {
|
||||
val position: Int
|
||||
}
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
* 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.jui.domain.source.model.sourcepreference
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
@Serializable
|
||||
data class SourcePreferenceChange(
|
||||
val position: Int,
|
||||
val value: String,
|
||||
) {
|
||||
constructor(position: Int, value: Any) : this(
|
||||
position,
|
||||
if (value is List<*>) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
Json.encodeToString(value as List<String>)
|
||||
} else {
|
||||
value.toString()
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
/*
|
||||
* 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.jui.domain.source.model.sourcepreference
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@SerialName("SwitchPreferenceCompat")
|
||||
@Immutable
|
||||
data class SwitchPreferenceOld(
|
||||
override val props: TwoStateProps,
|
||||
) : SourcePreferenceOld()
|
||||
@@ -1,21 +0,0 @@
|
||||
/*
|
||||
* 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.jui.domain.source.model.sourcepreference
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@Immutable
|
||||
data class TwoStateProps(
|
||||
override val key: String,
|
||||
override val title: String?,
|
||||
override val summary: String?,
|
||||
override val currentValue: Boolean?,
|
||||
override val defaultValue: Boolean?,
|
||||
override val defaultValueType: String,
|
||||
) : Props<Boolean?>
|
||||
@@ -9,9 +9,7 @@ package ca.gosyer.jui.domain.updates.model
|
||||
import androidx.compose.runtime.Immutable
|
||||
import ca.gosyer.jui.domain.chapter.model.Chapter
|
||||
import ca.gosyer.jui.domain.manga.model.Manga
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@Immutable
|
||||
data class MangaAndChapter(
|
||||
val manga: Manga,
|
||||
|
||||
@@ -7,9 +7,7 @@
|
||||
package ca.gosyer.jui.domain.updates.model
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@Immutable
|
||||
data class Updates(
|
||||
val page: List<MangaAndChapter>,
|
||||
|
||||
@@ -22,7 +22,6 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import ca.gosyer.jui.domain.base.WebsocketService
|
||||
import ca.gosyer.jui.domain.library.model.JobStatus
|
||||
import ca.gosyer.jui.i18n.MR
|
||||
import ca.gosyer.jui.ui.base.LocalViewModels
|
||||
import ca.gosyer.jui.uicore.resources.stringResource
|
||||
@@ -37,27 +36,18 @@ fun LibraryUpdatesExtraInfo() {
|
||||
val serviceStatus by vm.serviceStatus.collectAsState()
|
||||
val updateStatus by vm.updateStatus.collectAsState()
|
||||
|
||||
fun Map<JobStatus, List<*>>.getSize(jobStatus: JobStatus): Int = get(jobStatus)?.size ?: 0
|
||||
val current = remember(updateStatus) {
|
||||
updateStatus.mangaStatusMap.run {
|
||||
getSize(JobStatus.COMPLETE) + getSize(JobStatus.FAILED) + getSize(JobStatus.SKIPPED)
|
||||
}
|
||||
updateStatus.jobsInfo.finishedJobs
|
||||
}
|
||||
val total = remember(updateStatus) {
|
||||
updateStatus.mangaStatusMap.run {
|
||||
getSize(JobStatus.COMPLETE) +
|
||||
getSize(JobStatus.FAILED) +
|
||||
getSize(JobStatus.PENDING) +
|
||||
getSize(JobStatus.RUNNING) +
|
||||
getSize(JobStatus.SKIPPED)
|
||||
}
|
||||
updateStatus.jobsInfo.totalJobs
|
||||
}
|
||||
|
||||
val text = when (serviceStatus) {
|
||||
WebsocketService.Status.STARTING -> stringResource(MR.strings.downloads_loading)
|
||||
|
||||
WebsocketService.Status.RUNNING -> {
|
||||
if (updateStatus.running) {
|
||||
if (updateStatus.jobsInfo.isRunning) {
|
||||
stringResource(MR.strings.notification_updating, current, total)
|
||||
} else {
|
||||
null
|
||||
|
||||
@@ -9,11 +9,13 @@ package ca.gosyer.jui.ui.main.components
|
||||
import ca.gosyer.jui.domain.base.WebsocketService
|
||||
import ca.gosyer.jui.domain.library.service.LibraryUpdateService
|
||||
import ca.gosyer.jui.uicore.vm.ContextWrapper
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
|
||||
internal actual fun startLibraryUpdatesService(
|
||||
contextWrapper: ContextWrapper,
|
||||
libraryUpdatesService: LibraryUpdateService,
|
||||
actions: WebsocketService.Actions,
|
||||
) {
|
||||
libraryUpdatesService.init()
|
||||
libraryUpdatesService.getSubscription().launchIn(GlobalScope)
|
||||
}
|
||||
|
||||
@@ -9,11 +9,13 @@ package ca.gosyer.jui.ui.main.components
|
||||
import ca.gosyer.jui.domain.base.WebsocketService
|
||||
import ca.gosyer.jui.domain.library.service.LibraryUpdateService
|
||||
import ca.gosyer.jui.uicore.vm.ContextWrapper
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
|
||||
internal actual fun startLibraryUpdatesService(
|
||||
contextWrapper: ContextWrapper,
|
||||
libraryUpdatesService: LibraryUpdateService,
|
||||
actions: WebsocketService.Actions,
|
||||
) {
|
||||
libraryUpdatesService.init()
|
||||
libraryUpdatesService.getSubscription().launchIn(GlobalScope)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user