mirror of
https://github.com/Suwayomi/TachideskJUI.git
synced 2025-12-10 23:02:04 +01:00
Integrate Ktorfit
This commit is contained in:
@@ -62,6 +62,10 @@ dependencies {
|
|||||||
implementation(libs.ktor.websockets)
|
implementation(libs.ktor.websockets)
|
||||||
implementation(libs.ktor.auth)
|
implementation(libs.ktor.auth)
|
||||||
|
|
||||||
|
// Ktorfit
|
||||||
|
implementation(libs.ktorfit.lib)
|
||||||
|
ksp(libs.ktorfit.ksp)
|
||||||
|
|
||||||
// Logging
|
// Logging
|
||||||
implementation(libs.logging.kmlogging)
|
implementation(libs.logging.kmlogging)
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ import ca.gosyer.jui.domain.download.model.DownloadState
|
|||||||
import ca.gosyer.jui.domain.download.model.DownloadStatus
|
import ca.gosyer.jui.domain.download.model.DownloadStatus
|
||||||
import ca.gosyer.jui.domain.download.service.DownloadService
|
import ca.gosyer.jui.domain.download.service.DownloadService
|
||||||
import ca.gosyer.jui.domain.download.service.DownloadService.Companion.status
|
import ca.gosyer.jui.domain.download.service.DownloadService.Companion.status
|
||||||
import ca.gosyer.jui.domain.server.model.requests.downloadsQuery
|
|
||||||
import ca.gosyer.jui.i18n.MR
|
import ca.gosyer.jui.i18n.MR
|
||||||
import dev.icerock.moko.resources.desc.desc
|
import dev.icerock.moko.resources.desc.desc
|
||||||
import dev.icerock.moko.resources.format
|
import dev.icerock.moko.resources.format
|
||||||
@@ -146,7 +145,7 @@ class AndroidDownloadService : Service() {
|
|||||||
client.ws(
|
client.ws(
|
||||||
host = serverUrl.host,
|
host = serverUrl.host,
|
||||||
port = serverUrl.port,
|
port = serverUrl.port,
|
||||||
path = serverUrl.encodedPath + downloadsQuery()
|
path = serverUrl.encodedPath + "/api/v1/downloads"
|
||||||
) {
|
) {
|
||||||
errorConnectionCount = 0
|
errorConnectionCount = 0
|
||||||
status.value = Status.RUNNING
|
status.value = Status.RUNNING
|
||||||
|
|||||||
@@ -17,3 +17,10 @@ fun String.chop(count: Int, replacement: String = "…"): String {
|
|||||||
this
|
this
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun String.addSuffix(char: Char): String {
|
||||||
|
return if (endsWith(char)) {
|
||||||
|
this
|
||||||
|
} else this + char
|
||||||
|
}
|
||||||
@@ -6,57 +6,60 @@
|
|||||||
|
|
||||||
package ca.gosyer.jui.data
|
package ca.gosyer.jui.data
|
||||||
|
|
||||||
import ca.gosyer.jui.data.backup.BackupRepositoryImpl
|
import ca.gosyer.jui.core.lang.addSuffix
|
||||||
import ca.gosyer.jui.data.category.CategoryRepositoryImpl
|
|
||||||
import ca.gosyer.jui.data.chapter.ChapterRepositoryImpl
|
|
||||||
import ca.gosyer.jui.data.download.DownloadRepositoryImpl
|
|
||||||
import ca.gosyer.jui.data.extension.ExtensionRepositoryImpl
|
|
||||||
import ca.gosyer.jui.data.library.LibraryRepositoryImpl
|
|
||||||
import ca.gosyer.jui.data.manga.MangaRepositoryImpl
|
|
||||||
import ca.gosyer.jui.data.settings.SettingsRepositoryImpl
|
|
||||||
import ca.gosyer.jui.data.source.SourceRepositoryImpl
|
|
||||||
import ca.gosyer.jui.data.updates.UpdatesRepositoryImpl
|
|
||||||
import ca.gosyer.jui.domain.backup.service.BackupRepository
|
import ca.gosyer.jui.domain.backup.service.BackupRepository
|
||||||
import ca.gosyer.jui.domain.category.service.CategoryRepository
|
import ca.gosyer.jui.domain.category.service.CategoryRepository
|
||||||
import ca.gosyer.jui.domain.chapter.service.ChapterRepository
|
import ca.gosyer.jui.domain.chapter.service.ChapterRepository
|
||||||
|
import ca.gosyer.jui.domain.createIt
|
||||||
import ca.gosyer.jui.domain.download.service.DownloadRepository
|
import ca.gosyer.jui.domain.download.service.DownloadRepository
|
||||||
import ca.gosyer.jui.domain.extension.service.ExtensionRepository
|
import ca.gosyer.jui.domain.extension.service.ExtensionRepository
|
||||||
import ca.gosyer.jui.domain.library.service.LibraryRepository
|
import ca.gosyer.jui.domain.library.service.LibraryRepository
|
||||||
import ca.gosyer.jui.domain.manga.service.MangaRepository
|
import ca.gosyer.jui.domain.manga.service.MangaRepository
|
||||||
|
import ca.gosyer.jui.domain.server.Http
|
||||||
|
import ca.gosyer.jui.domain.server.service.ServerPreferences
|
||||||
import ca.gosyer.jui.domain.settings.service.SettingsRepository
|
import ca.gosyer.jui.domain.settings.service.SettingsRepository
|
||||||
import ca.gosyer.jui.domain.source.service.SourceRepository
|
import ca.gosyer.jui.domain.source.service.SourceRepository
|
||||||
import ca.gosyer.jui.domain.updates.service.UpdatesRepository
|
import ca.gosyer.jui.domain.updates.service.UpdatesRepository
|
||||||
|
import de.jensklingenberg.ktorfit.Ktorfit
|
||||||
import me.tatarka.inject.annotations.Provides
|
import me.tatarka.inject.annotations.Provides
|
||||||
|
|
||||||
interface DataComponent {
|
interface DataComponent {
|
||||||
|
|
||||||
val BackupRepositoryImpl.bind: BackupRepository
|
@Provides
|
||||||
@Provides get() = this
|
fun ktorfit(http: Http, serverPreferences: ServerPreferences) = Ktorfit
|
||||||
|
.Builder()
|
||||||
|
.httpClient(http)
|
||||||
|
.requestConverter(FlowIORequestConverter())
|
||||||
|
.baseUrl(serverPreferences.serverUrl().get().toString().addSuffix('/'))
|
||||||
|
.build()
|
||||||
|
|
||||||
val CategoryRepositoryImpl.bind: CategoryRepository
|
@Provides
|
||||||
@Provides get() = this
|
fun backupRepository(ktorfit: Ktorfit) = ktorfit.createIt<BackupRepository>()
|
||||||
|
|
||||||
val ChapterRepositoryImpl.bind: ChapterRepository
|
@Provides
|
||||||
@Provides get() = this
|
fun categoryRepository(ktorfit: Ktorfit) = ktorfit.createIt<CategoryRepository>()
|
||||||
|
|
||||||
val DownloadRepositoryImpl.bind: DownloadRepository
|
@Provides
|
||||||
@Provides get() = this
|
fun chapterRepository(ktorfit: Ktorfit) = ktorfit.createIt<ChapterRepository>()
|
||||||
|
|
||||||
val ExtensionRepositoryImpl.bind: ExtensionRepository
|
@Provides
|
||||||
@Provides get() = this
|
fun downloadRepository(ktorfit: Ktorfit) = ktorfit.createIt<DownloadRepository>()
|
||||||
|
|
||||||
val LibraryRepositoryImpl.bind: LibraryRepository
|
@Provides
|
||||||
@Provides get() = this
|
fun extensionRepository(ktorfit: Ktorfit) = ktorfit.createIt<ExtensionRepository>()
|
||||||
|
|
||||||
val MangaRepositoryImpl.bind: MangaRepository
|
@Provides
|
||||||
@Provides get() = this
|
fun libraryRepository(ktorfit: Ktorfit) = ktorfit.createIt<LibraryRepository>()
|
||||||
|
|
||||||
val SettingsRepositoryImpl.bind: SettingsRepository
|
@Provides
|
||||||
@Provides get() = this
|
fun mangaRepository(ktorfit: Ktorfit) = ktorfit.createIt<MangaRepository>()
|
||||||
|
|
||||||
val SourceRepositoryImpl.bind: SourceRepository
|
@Provides
|
||||||
@Provides get() = this
|
fun settingsRepository(ktorfit: Ktorfit) = ktorfit.createIt<SettingsRepository>()
|
||||||
|
|
||||||
val UpdatesRepositoryImpl.bind: UpdatesRepository
|
@Provides
|
||||||
@Provides get() = this
|
fun sourceRepository(ktorfit: Ktorfit) = ktorfit.createIt<SourceRepository>()
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
fun updatesRepository(ktorfit: Ktorfit) = ktorfit.createIt<UpdatesRepository>()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* 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.data
|
||||||
|
|
||||||
|
import ca.gosyer.jui.core.lang.IO
|
||||||
|
import de.jensklingenberg.ktorfit.Ktorfit
|
||||||
|
import de.jensklingenberg.ktorfit.converter.request.RequestConverter
|
||||||
|
import de.jensklingenberg.ktorfit.internal.TypeData
|
||||||
|
import io.ktor.client.call.body
|
||||||
|
import io.ktor.client.statement.HttpResponse
|
||||||
|
import io.ktor.util.reflect.TypeInfo
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.flow
|
||||||
|
import kotlinx.coroutines.flow.flowOn
|
||||||
|
|
||||||
|
class FlowIORequestConverter : RequestConverter {
|
||||||
|
|
||||||
|
override fun supportedType(typeData: TypeData, isSuspend: Boolean): Boolean {
|
||||||
|
return typeData.qualifiedName == "kotlinx.coroutines.flow.Flow"
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun <RequestType> convertRequest(
|
||||||
|
typeData: TypeData,
|
||||||
|
requestFunction: suspend () -> Pair<TypeInfo, HttpResponse?>,
|
||||||
|
ktorfit: Ktorfit
|
||||||
|
): Any {
|
||||||
|
return flow {
|
||||||
|
try {
|
||||||
|
val (info, response) = requestFunction()
|
||||||
|
if (info.type == HttpResponse::class) {
|
||||||
|
emit(response!!)
|
||||||
|
} else {
|
||||||
|
emit(response!!.body(info))
|
||||||
|
}
|
||||||
|
} catch (exception: Exception) {
|
||||||
|
throw exception
|
||||||
|
}
|
||||||
|
}.flowOn(Dispatchers.IO)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,84 +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.data.backup
|
|
||||||
|
|
||||||
import ca.gosyer.jui.core.io.SYSTEM
|
|
||||||
import ca.gosyer.jui.core.lang.IO
|
|
||||||
import ca.gosyer.jui.data.base.BaseRepository
|
|
||||||
import ca.gosyer.jui.domain.backup.model.BackupValidationResult
|
|
||||||
import ca.gosyer.jui.domain.backup.service.BackupRepository
|
|
||||||
import ca.gosyer.jui.domain.server.Http
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.backupFileExportRequest
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.backupFileImportRequest
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.validateBackupFileRequest
|
|
||||||
import ca.gosyer.jui.domain.server.service.ServerPreferences
|
|
||||||
import io.ktor.client.call.body
|
|
||||||
import io.ktor.client.plugins.expectSuccess
|
|
||||||
import io.ktor.client.request.HttpRequestBuilder
|
|
||||||
import io.ktor.client.request.forms.formData
|
|
||||||
import io.ktor.client.request.forms.submitFormWithBinaryData
|
|
||||||
import io.ktor.client.request.get
|
|
||||||
import io.ktor.http.ContentType
|
|
||||||
import io.ktor.http.Headers
|
|
||||||
import io.ktor.http.HttpHeaders
|
|
||||||
import io.ktor.http.path
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.flow.flow
|
|
||||||
import kotlinx.coroutines.flow.flowOn
|
|
||||||
import me.tatarka.inject.annotations.Inject
|
|
||||||
import okio.FileSystem
|
|
||||||
import okio.Path
|
|
||||||
import okio.buffer
|
|
||||||
|
|
||||||
class BackupRepositoryImpl @Inject constructor(
|
|
||||||
client: Http,
|
|
||||||
serverPreferences: ServerPreferences
|
|
||||||
) : BaseRepository(client, serverPreferences), BackupRepository {
|
|
||||||
|
|
||||||
private fun buildFormData(file: Path) = formData {
|
|
||||||
append(
|
|
||||||
"backup.proto.gz",
|
|
||||||
FileSystem.SYSTEM.source(file).buffer().readByteArray(),
|
|
||||||
Headers.build {
|
|
||||||
append(HttpHeaders.ContentType, ContentType.MultiPart.FormData.toString())
|
|
||||||
append(HttpHeaders.ContentDisposition, "filename=backup.proto.gz")
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun importBackupFile(file: Path, block: HttpRequestBuilder.() -> Unit) = flow {
|
|
||||||
val response = client.submitFormWithBinaryData(
|
|
||||||
buildUrl { path(backupFileImportRequest()) },
|
|
||||||
formData = buildFormData(file)
|
|
||||||
) {
|
|
||||||
expectSuccess = true
|
|
||||||
block()
|
|
||||||
}
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
|
|
||||||
override fun validateBackupFile(file: Path, block: HttpRequestBuilder.() -> Unit) = flow {
|
|
||||||
val response = client.submitFormWithBinaryData(
|
|
||||||
buildUrl { path(validateBackupFileRequest()) },
|
|
||||||
formData = buildFormData(file)
|
|
||||||
) {
|
|
||||||
expectSuccess = true
|
|
||||||
block()
|
|
||||||
}.body<BackupValidationResult>()
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
|
|
||||||
override fun exportBackupFile(block: HttpRequestBuilder.() -> Unit) = flow {
|
|
||||||
val response = client.get(
|
|
||||||
buildUrl { path(backupFileExportRequest()) }
|
|
||||||
) {
|
|
||||||
expectSuccess = true
|
|
||||||
block()
|
|
||||||
}
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
}
|
|
||||||
@@ -1,23 +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.data.base
|
|
||||||
|
|
||||||
import ca.gosyer.jui.domain.server.Http
|
|
||||||
import ca.gosyer.jui.domain.server.service.ServerPreferences
|
|
||||||
import io.ktor.http.URLBuilder
|
|
||||||
|
|
||||||
open class BaseRepository(
|
|
||||||
protected val client: Http,
|
|
||||||
serverPreferences: ServerPreferences
|
|
||||||
) {
|
|
||||||
private val _serverUrl = serverPreferences.serverUrl()
|
|
||||||
val serverUrl get() = _serverUrl.get().toString()
|
|
||||||
|
|
||||||
fun buildUrl(builder: URLBuilder.() -> Unit) = URLBuilder(serverUrl).apply(builder).buildString()
|
|
||||||
|
|
||||||
fun URLBuilder.parameter(key: String, value: Any) = encodedParameters.append(key, value.toString())
|
|
||||||
}
|
|
||||||
@@ -1,144 +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.data.category
|
|
||||||
|
|
||||||
import ca.gosyer.jui.core.lang.IO
|
|
||||||
import ca.gosyer.jui.data.base.BaseRepository
|
|
||||||
import ca.gosyer.jui.domain.category.model.Category
|
|
||||||
import ca.gosyer.jui.domain.category.service.CategoryRepository
|
|
||||||
import ca.gosyer.jui.domain.manga.model.Manga
|
|
||||||
import ca.gosyer.jui.domain.server.Http
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.addMangaToCategoryQuery
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.categoryDeleteRequest
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.categoryModifyRequest
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.categoryReorderRequest
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.createCategoryRequest
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.getCategoriesQuery
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.getMangaCategoriesQuery
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.getMangaInCategoryQuery
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.removeMangaFromCategoryRequest
|
|
||||||
import ca.gosyer.jui.domain.server.service.ServerPreferences
|
|
||||||
import io.ktor.client.call.body
|
|
||||||
import io.ktor.client.plugins.expectSuccess
|
|
||||||
import io.ktor.client.request.delete
|
|
||||||
import io.ktor.client.request.forms.submitForm
|
|
||||||
import io.ktor.client.request.get
|
|
||||||
import io.ktor.http.HttpMethod
|
|
||||||
import io.ktor.http.Parameters
|
|
||||||
import io.ktor.http.path
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.flow.flow
|
|
||||||
import kotlinx.coroutines.flow.flowOn
|
|
||||||
import me.tatarka.inject.annotations.Inject
|
|
||||||
|
|
||||||
class CategoryRepositoryImpl @Inject constructor(
|
|
||||||
client: Http,
|
|
||||||
serverPreferences: ServerPreferences
|
|
||||||
) : BaseRepository(client, serverPreferences), CategoryRepository {
|
|
||||||
|
|
||||||
override fun getMangaCategories(mangaId: Long) = flow {
|
|
||||||
val response = client.get(
|
|
||||||
buildUrl { path(getMangaCategoriesQuery(mangaId)) }
|
|
||||||
) {
|
|
||||||
expectSuccess = true
|
|
||||||
}.body<List<Category>>()
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
|
|
||||||
override fun addMangaToCategory(mangaId: Long, categoryId: Long) = flow {
|
|
||||||
val response = client.get(
|
|
||||||
buildUrl { path(addMangaToCategoryQuery(mangaId, categoryId)) }
|
|
||||||
) {
|
|
||||||
expectSuccess = true
|
|
||||||
}
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
|
|
||||||
override fun removeMangaFromCategory(mangaId: Long, categoryId: Long) = flow {
|
|
||||||
val response = client.delete(
|
|
||||||
buildUrl { path(removeMangaFromCategoryRequest(mangaId, categoryId)) }
|
|
||||||
) {
|
|
||||||
expectSuccess = true
|
|
||||||
}
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
|
|
||||||
override fun getCategories(dropDefault: Boolean) = flow {
|
|
||||||
val response = client.get(
|
|
||||||
buildUrl { path(getCategoriesQuery()) }
|
|
||||||
) {
|
|
||||||
expectSuccess = true
|
|
||||||
}.body<List<Category>>().let { categories ->
|
|
||||||
if (dropDefault) {
|
|
||||||
categories.filterNot { it.name.equals("default", true) }
|
|
||||||
} else categories
|
|
||||||
}
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
|
|
||||||
override fun createCategory(name: String) = flow {
|
|
||||||
val response = client.submitForm(
|
|
||||||
buildUrl { path(createCategoryRequest()) },
|
|
||||||
formParameters = Parameters.build {
|
|
||||||
append("name", name)
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
expectSuccess = true
|
|
||||||
}
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
|
|
||||||
override fun modifyCategory(categoryId: Long, name: String?, isLanding: Boolean?) = flow {
|
|
||||||
val response = client.submitForm(
|
|
||||||
buildUrl { path(categoryModifyRequest(categoryId)) },
|
|
||||||
formParameters = Parameters.build {
|
|
||||||
if (name != null) {
|
|
||||||
append("name", name)
|
|
||||||
}
|
|
||||||
if (isLanding != null) {
|
|
||||||
append("isLanding", isLanding.toString())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
method = HttpMethod.Patch
|
|
||||||
expectSuccess = true
|
|
||||||
}
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
|
|
||||||
override fun reorderCategory(to: Int, from: Int) = flow {
|
|
||||||
val response = client.submitForm(
|
|
||||||
buildUrl { path(categoryReorderRequest()) },
|
|
||||||
formParameters = Parameters.build {
|
|
||||||
append("to", to.toString())
|
|
||||||
append("from", from.toString())
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
method = HttpMethod.Patch
|
|
||||||
expectSuccess = true
|
|
||||||
}
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
|
|
||||||
override fun deleteCategory(categoryId: Long) = flow {
|
|
||||||
val response = client.delete(
|
|
||||||
buildUrl { path(categoryDeleteRequest(categoryId)) }
|
|
||||||
) {
|
|
||||||
expectSuccess = true
|
|
||||||
}
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
|
|
||||||
override fun getMangaFromCategory(categoryId: Long) = flow {
|
|
||||||
val response = client.get(
|
|
||||||
buildUrl { path(getMangaInCategoryQuery(categoryId)) }
|
|
||||||
) {
|
|
||||||
expectSuccess = true
|
|
||||||
}.body<List<Manga>>()
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
}
|
|
||||||
@@ -1,146 +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.data.chapter
|
|
||||||
|
|
||||||
import ca.gosyer.jui.core.lang.IO
|
|
||||||
import ca.gosyer.jui.data.base.BaseRepository
|
|
||||||
import ca.gosyer.jui.domain.chapter.model.Chapter
|
|
||||||
import ca.gosyer.jui.domain.chapter.service.ChapterRepository
|
|
||||||
import ca.gosyer.jui.domain.server.Http
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.deleteDownloadedChapterRequest
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.getChapterQuery
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.getMangaChaptersQuery
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.getPageQuery
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.queueDownloadChapterRequest
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.stopDownloadingChapterRequest
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.updateChapterMetaRequest
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.updateChapterRequest
|
|
||||||
import ca.gosyer.jui.domain.server.service.ServerPreferences
|
|
||||||
import io.ktor.client.call.body
|
|
||||||
import io.ktor.client.plugins.expectSuccess
|
|
||||||
import io.ktor.client.request.HttpRequestBuilder
|
|
||||||
import io.ktor.client.request.delete
|
|
||||||
import io.ktor.client.request.forms.submitForm
|
|
||||||
import io.ktor.client.request.get
|
|
||||||
import io.ktor.http.HttpMethod
|
|
||||||
import io.ktor.http.Parameters
|
|
||||||
import io.ktor.http.path
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.flow.flow
|
|
||||||
import kotlinx.coroutines.flow.flowOn
|
|
||||||
import me.tatarka.inject.annotations.Inject
|
|
||||||
|
|
||||||
class ChapterRepositoryImpl @Inject constructor(
|
|
||||||
client: Http,
|
|
||||||
serverPreferences: ServerPreferences
|
|
||||||
) : BaseRepository(client, serverPreferences), ChapterRepository {
|
|
||||||
|
|
||||||
override fun getChapters(mangaId: Long, refresh: Boolean) = flow {
|
|
||||||
val response = client.get(
|
|
||||||
buildUrl {
|
|
||||||
path(getMangaChaptersQuery(mangaId))
|
|
||||||
if (refresh) {
|
|
||||||
parameter("onlineFetch", true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
expectSuccess = true
|
|
||||||
}.body<List<Chapter>>()
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
|
|
||||||
override fun getChapter(mangaId: Long, chapterIndex: Int) = flow {
|
|
||||||
val response = client.get(
|
|
||||||
buildUrl { path(getChapterQuery(mangaId, chapterIndex)) }
|
|
||||||
) {
|
|
||||||
expectSuccess = true
|
|
||||||
}.body<Chapter>()
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
|
|
||||||
override fun updateChapter(
|
|
||||||
mangaId: Long,
|
|
||||||
chapterIndex: Int,
|
|
||||||
read: Boolean?,
|
|
||||||
bookmarked: Boolean?,
|
|
||||||
lastPageRead: Int?,
|
|
||||||
markPreviousRead: Boolean?
|
|
||||||
) = flow {
|
|
||||||
val response = client.submitForm(
|
|
||||||
buildUrl { path(updateChapterRequest(mangaId, chapterIndex)) },
|
|
||||||
formParameters = Parameters.build {
|
|
||||||
if (read != null) {
|
|
||||||
append("read", read.toString())
|
|
||||||
}
|
|
||||||
if (bookmarked != null) {
|
|
||||||
append("bookmarked", bookmarked.toString())
|
|
||||||
}
|
|
||||||
if (lastPageRead != null) {
|
|
||||||
append("lastPageRead", lastPageRead.toString())
|
|
||||||
}
|
|
||||||
if (markPreviousRead != null) {
|
|
||||||
append("markPrevRead", markPreviousRead.toString())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
method = HttpMethod.Patch
|
|
||||||
expectSuccess = true
|
|
||||||
}
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
|
|
||||||
override fun getPage(mangaId: Long, chapterIndex: Int, pageNum: Int, block: HttpRequestBuilder.() -> Unit) = flow {
|
|
||||||
val response = client.get(
|
|
||||||
buildUrl { path(getPageQuery(mangaId, chapterIndex, pageNum)) }
|
|
||||||
) {
|
|
||||||
expectSuccess = true
|
|
||||||
block()
|
|
||||||
}
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
|
|
||||||
override fun deleteChapterDownload(mangaId: Long, chapterIndex: Int) = flow {
|
|
||||||
val response = client.delete(
|
|
||||||
buildUrl { path(deleteDownloadedChapterRequest(mangaId, chapterIndex)) }
|
|
||||||
) {
|
|
||||||
expectSuccess = true
|
|
||||||
}
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
|
|
||||||
override fun queueChapterDownload(mangaId: Long, chapterIndex: Int) = flow {
|
|
||||||
val response = client.get(
|
|
||||||
buildUrl { path(queueDownloadChapterRequest(mangaId, chapterIndex)) }
|
|
||||||
) {
|
|
||||||
expectSuccess = true
|
|
||||||
}
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
|
|
||||||
override fun stopChapterDownload(mangaId: Long, chapterIndex: Int) = flow {
|
|
||||||
val response = client.delete(
|
|
||||||
buildUrl { path(stopDownloadingChapterRequest(mangaId, chapterIndex)) }
|
|
||||||
) {
|
|
||||||
expectSuccess = true
|
|
||||||
}
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
|
|
||||||
override fun updateChapterMeta(mangaId: Long, chapterIndex: Int, key: String, value: String) = flow {
|
|
||||||
val response = client.submitForm(
|
|
||||||
buildUrl { path(updateChapterMetaRequest(mangaId, chapterIndex)) },
|
|
||||||
formParameters = Parameters.build {
|
|
||||||
append("key", key)
|
|
||||||
append("value", value)
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
method = HttpMethod.Patch
|
|
||||||
expectSuccess = true
|
|
||||||
}
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
}
|
|
||||||
@@ -1,56 +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.data.download
|
|
||||||
|
|
||||||
import ca.gosyer.jui.core.lang.IO
|
|
||||||
import ca.gosyer.jui.data.base.BaseRepository
|
|
||||||
import ca.gosyer.jui.domain.download.service.DownloadRepository
|
|
||||||
import ca.gosyer.jui.domain.server.Http
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.downloadsClearRequest
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.downloadsStartRequest
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.downloadsStopRequest
|
|
||||||
import ca.gosyer.jui.domain.server.service.ServerPreferences
|
|
||||||
import io.ktor.client.plugins.expectSuccess
|
|
||||||
import io.ktor.client.request.get
|
|
||||||
import io.ktor.http.path
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.flow.flow
|
|
||||||
import kotlinx.coroutines.flow.flowOn
|
|
||||||
import me.tatarka.inject.annotations.Inject
|
|
||||||
|
|
||||||
class DownloadRepositoryImpl @Inject constructor(
|
|
||||||
client: Http,
|
|
||||||
serverPreferences: ServerPreferences
|
|
||||||
) : BaseRepository(client, serverPreferences), DownloadRepository {
|
|
||||||
|
|
||||||
override fun startDownloading() = flow {
|
|
||||||
val response = client.get(
|
|
||||||
buildUrl { path(downloadsStartRequest()) }
|
|
||||||
) {
|
|
||||||
expectSuccess = true
|
|
||||||
}
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
|
|
||||||
override fun stopDownloading() = flow {
|
|
||||||
val response = client.get(
|
|
||||||
buildUrl { path(downloadsStopRequest()) }
|
|
||||||
) {
|
|
||||||
expectSuccess = true
|
|
||||||
}
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
|
|
||||||
override fun clearDownloadQueue() = flow {
|
|
||||||
val response = client.get(
|
|
||||||
buildUrl { path(downloadsClearRequest()) }
|
|
||||||
) {
|
|
||||||
expectSuccess = true
|
|
||||||
}
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
}
|
|
||||||
@@ -1,81 +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.data.extension
|
|
||||||
|
|
||||||
import ca.gosyer.jui.core.lang.IO
|
|
||||||
import ca.gosyer.jui.data.base.BaseRepository
|
|
||||||
import ca.gosyer.jui.domain.extension.model.Extension
|
|
||||||
import ca.gosyer.jui.domain.extension.service.ExtensionRepository
|
|
||||||
import ca.gosyer.jui.domain.server.Http
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.apkIconQuery
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.apkInstallQuery
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.apkUninstallQuery
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.apkUpdateQuery
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.extensionListQuery
|
|
||||||
import ca.gosyer.jui.domain.server.service.ServerPreferences
|
|
||||||
import io.ktor.client.call.body
|
|
||||||
import io.ktor.client.plugins.expectSuccess
|
|
||||||
import io.ktor.client.request.HttpRequestBuilder
|
|
||||||
import io.ktor.client.request.get
|
|
||||||
import io.ktor.client.statement.bodyAsChannel
|
|
||||||
import io.ktor.http.path
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.flow.flow
|
|
||||||
import kotlinx.coroutines.flow.flowOn
|
|
||||||
import me.tatarka.inject.annotations.Inject
|
|
||||||
|
|
||||||
class ExtensionRepositoryImpl @Inject constructor(
|
|
||||||
client: Http,
|
|
||||||
serverPreferences: ServerPreferences
|
|
||||||
) : BaseRepository(client, serverPreferences), ExtensionRepository {
|
|
||||||
|
|
||||||
override fun getExtensionList() = flow {
|
|
||||||
val response = client.get(
|
|
||||||
buildUrl { path(extensionListQuery()) }
|
|
||||||
) {
|
|
||||||
expectSuccess = true
|
|
||||||
}.body<List<Extension>>()
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
|
|
||||||
override fun installExtension(extension: Extension) = flow {
|
|
||||||
val response = client.get(
|
|
||||||
buildUrl { path(apkInstallQuery(extension.pkgName)) }
|
|
||||||
) {
|
|
||||||
expectSuccess = true
|
|
||||||
}
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
|
|
||||||
override fun updateExtension(extension: Extension) = flow {
|
|
||||||
val response = client.get(
|
|
||||||
buildUrl { path(apkUpdateQuery(extension.pkgName)) }
|
|
||||||
) {
|
|
||||||
expectSuccess = true
|
|
||||||
}
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
|
|
||||||
override fun uninstallExtension(extension: Extension) = flow {
|
|
||||||
val response = client.get(
|
|
||||||
buildUrl { path(apkUninstallQuery(extension.pkgName)) }
|
|
||||||
) {
|
|
||||||
expectSuccess = true
|
|
||||||
}
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
|
|
||||||
override fun getApkIcon(extension: Extension, block: HttpRequestBuilder.() -> Unit) = flow {
|
|
||||||
val response = client.get(
|
|
||||||
buildUrl { path(apkIconQuery(extension.apkName)) }
|
|
||||||
) {
|
|
||||||
expectSuccess = true
|
|
||||||
block()
|
|
||||||
}.bodyAsChannel()
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
}
|
|
||||||
@@ -1,47 +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.data.library
|
|
||||||
|
|
||||||
import ca.gosyer.jui.core.lang.IO
|
|
||||||
import ca.gosyer.jui.data.base.BaseRepository
|
|
||||||
import ca.gosyer.jui.domain.library.service.LibraryRepository
|
|
||||||
import ca.gosyer.jui.domain.server.Http
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.addMangaToLibraryQuery
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.removeMangaFromLibraryRequest
|
|
||||||
import ca.gosyer.jui.domain.server.service.ServerPreferences
|
|
||||||
import io.ktor.client.plugins.expectSuccess
|
|
||||||
import io.ktor.client.request.delete
|
|
||||||
import io.ktor.client.request.get
|
|
||||||
import io.ktor.http.path
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.flow.flow
|
|
||||||
import kotlinx.coroutines.flow.flowOn
|
|
||||||
import me.tatarka.inject.annotations.Inject
|
|
||||||
|
|
||||||
class LibraryRepositoryImpl @Inject constructor(
|
|
||||||
client: Http,
|
|
||||||
serverPreferences: ServerPreferences
|
|
||||||
) : BaseRepository(client, serverPreferences), LibraryRepository {
|
|
||||||
|
|
||||||
override fun addMangaToLibrary(mangaId: Long) = flow {
|
|
||||||
val response = client.get(
|
|
||||||
buildUrl { path(addMangaToLibraryQuery(mangaId)) }
|
|
||||||
) {
|
|
||||||
expectSuccess = true
|
|
||||||
}
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
|
|
||||||
override fun removeMangaFromLibrary(mangaId: Long) = flow {
|
|
||||||
val response = client.delete(
|
|
||||||
buildUrl { path(removeMangaFromLibraryRequest(mangaId)) }
|
|
||||||
) {
|
|
||||||
expectSuccess = true
|
|
||||||
}
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
}
|
|
||||||
@@ -1,74 +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.data.manga
|
|
||||||
|
|
||||||
import ca.gosyer.jui.core.lang.IO
|
|
||||||
import ca.gosyer.jui.data.base.BaseRepository
|
|
||||||
import ca.gosyer.jui.domain.manga.model.Manga
|
|
||||||
import ca.gosyer.jui.domain.manga.service.MangaRepository
|
|
||||||
import ca.gosyer.jui.domain.server.Http
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.mangaQuery
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.mangaThumbnailQuery
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.updateMangaMetaRequest
|
|
||||||
import ca.gosyer.jui.domain.server.service.ServerPreferences
|
|
||||||
import io.ktor.client.call.body
|
|
||||||
import io.ktor.client.plugins.expectSuccess
|
|
||||||
import io.ktor.client.request.HttpRequestBuilder
|
|
||||||
import io.ktor.client.request.forms.submitForm
|
|
||||||
import io.ktor.client.request.get
|
|
||||||
import io.ktor.client.statement.bodyAsChannel
|
|
||||||
import io.ktor.http.HttpMethod
|
|
||||||
import io.ktor.http.Parameters
|
|
||||||
import io.ktor.http.path
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.flow.flow
|
|
||||||
import kotlinx.coroutines.flow.flowOn
|
|
||||||
import me.tatarka.inject.annotations.Inject
|
|
||||||
|
|
||||||
class MangaRepositoryImpl @Inject constructor(
|
|
||||||
client: Http,
|
|
||||||
serverPreferences: ServerPreferences
|
|
||||||
) : BaseRepository(client, serverPreferences), MangaRepository {
|
|
||||||
|
|
||||||
override fun getManga(mangaId: Long, refresh: Boolean) = flow {
|
|
||||||
val response = client.get(
|
|
||||||
buildUrl {
|
|
||||||
path(mangaQuery(mangaId))
|
|
||||||
if (refresh) {
|
|
||||||
parameter("onlineFetch", true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
expectSuccess = true
|
|
||||||
}.body<Manga>()
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
|
|
||||||
override fun getMangaThumbnail(mangaId: Long, block: HttpRequestBuilder.() -> Unit) = flow {
|
|
||||||
val response = client.get(
|
|
||||||
buildUrl { path(mangaThumbnailQuery(mangaId)) }
|
|
||||||
) {
|
|
||||||
expectSuccess = true
|
|
||||||
block()
|
|
||||||
}.bodyAsChannel()
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
|
|
||||||
override fun updateMangaMeta(mangaId: Long, key: String, value: String) = flow {
|
|
||||||
val response = client.submitForm(
|
|
||||||
buildUrl { path(updateMangaMetaRequest(mangaId)) },
|
|
||||||
formParameters = Parameters.build {
|
|
||||||
append("key", key)
|
|
||||||
append("value", value)
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
method = HttpMethod.Patch
|
|
||||||
expectSuccess = true
|
|
||||||
}
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
}
|
|
||||||
@@ -1,49 +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.data.settings
|
|
||||||
|
|
||||||
import ca.gosyer.jui.core.lang.IO
|
|
||||||
import ca.gosyer.jui.data.base.BaseRepository
|
|
||||||
import ca.gosyer.jui.domain.server.Http
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.aboutQuery
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.checkUpdateQuery
|
|
||||||
import ca.gosyer.jui.domain.server.service.ServerPreferences
|
|
||||||
import ca.gosyer.jui.domain.settings.model.About
|
|
||||||
import ca.gosyer.jui.domain.settings.service.SettingsRepository
|
|
||||||
import io.ktor.client.call.body
|
|
||||||
import io.ktor.client.plugins.expectSuccess
|
|
||||||
import io.ktor.client.request.get
|
|
||||||
import io.ktor.client.request.post
|
|
||||||
import io.ktor.http.path
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.flow.flow
|
|
||||||
import kotlinx.coroutines.flow.flowOn
|
|
||||||
import me.tatarka.inject.annotations.Inject
|
|
||||||
|
|
||||||
class SettingsRepositoryImpl @Inject constructor(
|
|
||||||
client: Http,
|
|
||||||
serverPreferences: ServerPreferences
|
|
||||||
) : BaseRepository(client, serverPreferences), SettingsRepository {
|
|
||||||
|
|
||||||
override fun aboutServer() = flow {
|
|
||||||
val response = client.get(
|
|
||||||
buildUrl { path(aboutQuery()) }
|
|
||||||
) {
|
|
||||||
expectSuccess = true
|
|
||||||
}.body<About>()
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
|
|
||||||
override fun checkUpdate() = flow {
|
|
||||||
val response = client.post(
|
|
||||||
buildUrl { path(checkUpdateQuery()) }
|
|
||||||
) {
|
|
||||||
expectSuccess = true
|
|
||||||
}
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
}
|
|
||||||
@@ -1,162 +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.data.source
|
|
||||||
|
|
||||||
import ca.gosyer.jui.core.lang.IO
|
|
||||||
import ca.gosyer.jui.data.base.BaseRepository
|
|
||||||
import ca.gosyer.jui.domain.server.Http
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.getFilterListQuery
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.getSourceSettingsQuery
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.setFilterRequest
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.sourceInfoQuery
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.sourceLatestQuery
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.sourceListQuery
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.sourcePopularQuery
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.sourceSearchQuery
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.updateSourceSettingQuery
|
|
||||||
import ca.gosyer.jui.domain.server.service.ServerPreferences
|
|
||||||
import ca.gosyer.jui.domain.source.model.MangaPage
|
|
||||||
import ca.gosyer.jui.domain.source.model.Source
|
|
||||||
import ca.gosyer.jui.domain.source.model.sourcefilters.SourceFilter
|
|
||||||
import ca.gosyer.jui.domain.source.model.sourcefilters.SourceFilterChange
|
|
||||||
import ca.gosyer.jui.domain.source.model.sourcepreference.SourcePreference
|
|
||||||
import ca.gosyer.jui.domain.source.model.sourcepreference.SourcePreferenceChange
|
|
||||||
import ca.gosyer.jui.domain.source.service.SourceRepository
|
|
||||||
import io.ktor.client.call.body
|
|
||||||
import io.ktor.client.plugins.expectSuccess
|
|
||||||
import io.ktor.client.request.get
|
|
||||||
import io.ktor.client.request.post
|
|
||||||
import io.ktor.client.request.setBody
|
|
||||||
import io.ktor.http.ContentType
|
|
||||||
import io.ktor.http.contentType
|
|
||||||
import io.ktor.http.path
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.flow.flow
|
|
||||||
import kotlinx.coroutines.flow.flowOn
|
|
||||||
import kotlinx.serialization.encodeToString
|
|
||||||
import kotlinx.serialization.json.Json
|
|
||||||
import me.tatarka.inject.annotations.Inject
|
|
||||||
|
|
||||||
class SourceRepositoryImpl @Inject constructor(
|
|
||||||
client: Http,
|
|
||||||
serverPreferences: ServerPreferences
|
|
||||||
) : BaseRepository(client, serverPreferences), SourceRepository {
|
|
||||||
|
|
||||||
override fun getSourceList() = flow {
|
|
||||||
val response = client.get(
|
|
||||||
buildUrl { path(sourceListQuery()) }
|
|
||||||
) {
|
|
||||||
expectSuccess = true
|
|
||||||
}.body<List<Source>>()
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
|
|
||||||
override fun getSourceInfo(sourceId: Long) = flow {
|
|
||||||
val response = client.get(
|
|
||||||
buildUrl { path(sourceInfoQuery(sourceId)) }
|
|
||||||
) {
|
|
||||||
expectSuccess = true
|
|
||||||
}.body<Source>()
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
|
|
||||||
override fun getPopularManga(sourceId: Long, pageNum: Int) = flow {
|
|
||||||
val response = client.get(
|
|
||||||
buildUrl { path(sourcePopularQuery(sourceId, pageNum)) }
|
|
||||||
) {
|
|
||||||
expectSuccess = true
|
|
||||||
}.body<MangaPage>()
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
|
|
||||||
override fun getLatestManga(sourceId: Long, pageNum: Int) = flow {
|
|
||||||
val response = client.get(
|
|
||||||
buildUrl { path(sourceLatestQuery(sourceId, pageNum)) }
|
|
||||||
) {
|
|
||||||
expectSuccess = true
|
|
||||||
}.body<MangaPage>()
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
|
|
||||||
override fun getSearchResults(sourceId: Long, searchTerm: String, pageNum: Int) = flow {
|
|
||||||
val response = client.get(
|
|
||||||
buildUrl {
|
|
||||||
path(sourceSearchQuery(sourceId))
|
|
||||||
parameter("pageNum", pageNum)
|
|
||||||
if (searchTerm.isNotBlank()) {
|
|
||||||
parameter("searchTerm", searchTerm)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
expectSuccess = true
|
|
||||||
}.body<MangaPage>()
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
|
|
||||||
override fun getFilterList(sourceId: Long, reset: Boolean) = flow {
|
|
||||||
val response = client.get(
|
|
||||||
buildUrl {
|
|
||||||
path(getFilterListQuery(sourceId))
|
|
||||||
if (reset) {
|
|
||||||
parameter("reset", true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
expectSuccess = true
|
|
||||||
}.body<List<SourceFilter>>()
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
|
|
||||||
override fun setFilter(sourceId: Long, sourceFilter: SourceFilterChange) = flow {
|
|
||||||
val response = client.post(
|
|
||||||
buildUrl { path(setFilterRequest(sourceId)) }
|
|
||||||
) {
|
|
||||||
contentType(ContentType.Application.Json)
|
|
||||||
setBody(sourceFilter)
|
|
||||||
expectSuccess = true
|
|
||||||
}
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
|
|
||||||
override fun setFilter(sourceId: Long, position: Int, value: Any) = setFilter(
|
|
||||||
sourceId,
|
|
||||||
SourceFilterChange(position, value)
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun setFilter(sourceId: Long, parentPosition: Int, childPosition: Int, value: Any) = setFilter(
|
|
||||||
sourceId,
|
|
||||||
SourceFilterChange(
|
|
||||||
parentPosition,
|
|
||||||
Json.encodeToString(SourceFilterChange(childPosition, value))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
override fun getSourceSettings(sourceId: Long) = flow {
|
|
||||||
val response = client.get(
|
|
||||||
buildUrl { path(getSourceSettingsQuery(sourceId)) }
|
|
||||||
) {
|
|
||||||
expectSuccess = true
|
|
||||||
}.body<List<SourcePreference>>()
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
|
|
||||||
override fun setSourceSetting(sourceId: Long, sourcePreference: SourcePreferenceChange) = flow {
|
|
||||||
val response = client.post(
|
|
||||||
buildUrl { path(updateSourceSettingQuery(sourceId)) }
|
|
||||||
) {
|
|
||||||
contentType(ContentType.Application.Json)
|
|
||||||
setBody(sourcePreference)
|
|
||||||
expectSuccess = true
|
|
||||||
}
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
|
|
||||||
override fun setSourceSetting(sourceId: Long, position: Int, value: Any) = setSourceSetting(
|
|
||||||
sourceId,
|
|
||||||
SourcePreferenceChange(position, value)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,63 +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.data.updates
|
|
||||||
|
|
||||||
import ca.gosyer.jui.core.lang.IO
|
|
||||||
import ca.gosyer.jui.data.base.BaseRepository
|
|
||||||
import ca.gosyer.jui.domain.server.Http
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.fetchUpdatesRequest
|
|
||||||
import ca.gosyer.jui.domain.server.model.requests.recentUpdatesQuery
|
|
||||||
import ca.gosyer.jui.domain.server.service.ServerPreferences
|
|
||||||
import ca.gosyer.jui.domain.updates.model.Updates
|
|
||||||
import ca.gosyer.jui.domain.updates.service.UpdatesRepository
|
|
||||||
import io.ktor.client.call.body
|
|
||||||
import io.ktor.client.plugins.expectSuccess
|
|
||||||
import io.ktor.client.request.forms.submitForm
|
|
||||||
import io.ktor.client.request.get
|
|
||||||
import io.ktor.client.request.post
|
|
||||||
import io.ktor.http.Parameters
|
|
||||||
import io.ktor.http.path
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.flow.flow
|
|
||||||
import kotlinx.coroutines.flow.flowOn
|
|
||||||
import me.tatarka.inject.annotations.Inject
|
|
||||||
|
|
||||||
class UpdatesRepositoryImpl @Inject constructor(
|
|
||||||
client: Http,
|
|
||||||
serverPreferences: ServerPreferences
|
|
||||||
) : BaseRepository(client, serverPreferences), UpdatesRepository {
|
|
||||||
|
|
||||||
override fun getRecentUpdates(pageNum: Int) = flow {
|
|
||||||
val response = client.get(
|
|
||||||
buildUrl { path(recentUpdatesQuery(pageNum)) }
|
|
||||||
) {
|
|
||||||
expectSuccess = true
|
|
||||||
}.body<Updates>()
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
|
|
||||||
override fun updateLibrary() = flow {
|
|
||||||
val response = client.post(
|
|
||||||
buildUrl { path(fetchUpdatesRequest()) }
|
|
||||||
) {
|
|
||||||
expectSuccess = true
|
|
||||||
}
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
|
|
||||||
override fun updateCategory(categoryId: Long) = flow {
|
|
||||||
val response = client.submitForm(
|
|
||||||
buildUrl { path(fetchUpdatesRequest()) },
|
|
||||||
formParameters = Parameters.build {
|
|
||||||
append("category", categoryId.toString())
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
expectSuccess = true
|
|
||||||
}
|
|
||||||
emit(response)
|
|
||||||
}.flowOn(Dispatchers.IO)
|
|
||||||
}
|
|
||||||
@@ -63,6 +63,10 @@ dependencies {
|
|||||||
implementation(libs.ktor.websockets)
|
implementation(libs.ktor.websockets)
|
||||||
implementation(libs.ktor.auth)
|
implementation(libs.ktor.auth)
|
||||||
|
|
||||||
|
// Ktorfit
|
||||||
|
implementation(libs.ktorfit.lib)
|
||||||
|
ksp(libs.ktorfit.ksp)
|
||||||
|
|
||||||
// Logging
|
// Logging
|
||||||
implementation(libs.logging.slf4j.api)
|
implementation(libs.logging.slf4j.api)
|
||||||
implementation(libs.logging.slf4j.jul)
|
implementation(libs.logging.slf4j.jul)
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ kotlin {
|
|||||||
languageSettings {
|
languageSettings {
|
||||||
optIn("kotlin.RequiresOptIn")
|
optIn("kotlin.RequiresOptIn")
|
||||||
optIn("kotlinx.coroutines.ExperimentalCoroutinesApi")
|
optIn("kotlinx.coroutines.ExperimentalCoroutinesApi")
|
||||||
|
optIn("de.jensklingenberg.ktorfit.internal.InternalKtorfitApi")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val commonMain by getting {
|
val commonMain by getting {
|
||||||
@@ -48,6 +49,7 @@ kotlin {
|
|||||||
api(libs.ktor.auth)
|
api(libs.ktor.auth)
|
||||||
api(libs.ktor.logging)
|
api(libs.ktor.logging)
|
||||||
api(libs.ktor.websockets)
|
api(libs.ktor.websockets)
|
||||||
|
api(libs.ktorfit.lib)
|
||||||
api(libs.okio)
|
api(libs.okio)
|
||||||
api(libs.dateTime)
|
api(libs.dateTime)
|
||||||
api(projects.core)
|
api(projects.core)
|
||||||
@@ -113,6 +115,9 @@ kotlin {
|
|||||||
dependencies {
|
dependencies {
|
||||||
add("kspDesktop", libs.kotlinInject.compiler)
|
add("kspDesktop", libs.kotlinInject.compiler)
|
||||||
add("kspAndroid", libs.kotlinInject.compiler)
|
add("kspAndroid", libs.kotlinInject.compiler)
|
||||||
|
add("kspCommonMainMetadata", libs.ktorfit.ksp)
|
||||||
|
add("kspDesktop", libs.ktorfit.ksp)
|
||||||
|
add("kspAndroid", libs.ktorfit.ksp)
|
||||||
}
|
}
|
||||||
|
|
||||||
buildkonfig {
|
buildkonfig {
|
||||||
|
|||||||
@@ -4,14 +4,9 @@
|
|||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package ca.gosyer.jui.domain.server.model.requests
|
package ca.gosyer.jui.domain
|
||||||
|
|
||||||
annotation class Get
|
import de.jensklingenberg.ktorfit.Ktorfit
|
||||||
|
import de.jensklingenberg.ktorfit.create
|
||||||
|
|
||||||
annotation class Post
|
inline fun <reified T> Ktorfit.createIt(): T = create()
|
||||||
|
|
||||||
annotation class Delete
|
|
||||||
|
|
||||||
annotation class Patch
|
|
||||||
|
|
||||||
annotation class WS
|
|
||||||
@@ -21,7 +21,7 @@ class ImportBackupFile @Inject constructor(private val backupRepository: BackupR
|
|||||||
.singleOrNull()
|
.singleOrNull()
|
||||||
|
|
||||||
fun asFlow(file: Path, block: HttpRequestBuilder.() -> Unit = {}) =
|
fun asFlow(file: Path, block: HttpRequestBuilder.() -> Unit = {}) =
|
||||||
backupRepository.importBackupFile(file, block)
|
backupRepository.importBackupFile(BackupRepository.buildBackupFormData(file), block)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val log = logging()
|
private val log = logging()
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ class ValidateBackupFile @Inject constructor(private val backupRepository: Backu
|
|||||||
.singleOrNull()
|
.singleOrNull()
|
||||||
|
|
||||||
fun asFlow(file: Path, block: HttpRequestBuilder.() -> Unit = {}) =
|
fun asFlow(file: Path, block: HttpRequestBuilder.() -> Unit = {}) =
|
||||||
backupRepository.validateBackupFile(file, block)
|
backupRepository.validateBackupFile(BackupRepository.buildBackupFormData(file), block)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val log = logging()
|
private val log = logging()
|
||||||
|
|||||||
@@ -6,14 +6,55 @@
|
|||||||
|
|
||||||
package ca.gosyer.jui.domain.backup.service
|
package ca.gosyer.jui.domain.backup.service
|
||||||
|
|
||||||
|
import ca.gosyer.jui.core.io.SYSTEM
|
||||||
import ca.gosyer.jui.domain.backup.model.BackupValidationResult
|
import ca.gosyer.jui.domain.backup.model.BackupValidationResult
|
||||||
|
import de.jensklingenberg.ktorfit.http.Multipart
|
||||||
|
import de.jensklingenberg.ktorfit.http.POST
|
||||||
|
import de.jensklingenberg.ktorfit.http.Part
|
||||||
|
import de.jensklingenberg.ktorfit.http.ReqBuilder
|
||||||
import io.ktor.client.request.HttpRequestBuilder
|
import io.ktor.client.request.HttpRequestBuilder
|
||||||
|
import io.ktor.client.request.forms.formData
|
||||||
import io.ktor.client.statement.HttpResponse
|
import io.ktor.client.statement.HttpResponse
|
||||||
|
import io.ktor.http.ContentType
|
||||||
|
import io.ktor.http.Headers
|
||||||
|
import io.ktor.http.HttpHeaders
|
||||||
|
import io.ktor.http.content.PartData
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import okio.FileSystem
|
||||||
import okio.Path
|
import okio.Path
|
||||||
|
import okio.buffer
|
||||||
|
|
||||||
interface BackupRepository {
|
interface BackupRepository {
|
||||||
fun importBackupFile(file: Path, block: HttpRequestBuilder.() -> Unit = {}): Flow<HttpResponse>
|
|
||||||
fun validateBackupFile(file: Path, block: HttpRequestBuilder.() -> Unit = {}): Flow<BackupValidationResult>
|
@Multipart
|
||||||
fun exportBackupFile(block: HttpRequestBuilder.() -> Unit = {}): Flow<HttpResponse>
|
@POST("api/v1/backup/import/file")
|
||||||
|
fun importBackupFile(
|
||||||
|
@Part("") formData: List<PartData>,
|
||||||
|
@ReqBuilder block: HttpRequestBuilder.() -> Unit = {}
|
||||||
|
): Flow<HttpResponse>
|
||||||
|
|
||||||
|
@Multipart
|
||||||
|
@POST("api/v1/backup/validate/file")
|
||||||
|
fun validateBackupFile(
|
||||||
|
@Part("") formData: List<PartData>,
|
||||||
|
@ReqBuilder block: HttpRequestBuilder.() -> Unit = {}
|
||||||
|
): Flow<BackupValidationResult>
|
||||||
|
|
||||||
|
@POST("api/v1/backup/export/file")
|
||||||
|
fun exportBackupFile(
|
||||||
|
@ReqBuilder block: HttpRequestBuilder.() -> Unit = {}
|
||||||
|
): Flow<HttpResponse>
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun buildBackupFormData(file: Path) = formData {
|
||||||
|
append(
|
||||||
|
"backup.proto.gz",
|
||||||
|
FileSystem.SYSTEM.source(file).buffer().readByteArray(),
|
||||||
|
Headers.build {
|
||||||
|
append(HttpHeaders.ContentType, ContentType.MultiPart.FormData.toString())
|
||||||
|
append(HttpHeaders.ContentDisposition, "filename=backup.proto.gz")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ package ca.gosyer.jui.domain.category.interactor
|
|||||||
|
|
||||||
import ca.gosyer.jui.domain.category.service.CategoryRepository
|
import ca.gosyer.jui.domain.category.service.CategoryRepository
|
||||||
import kotlinx.coroutines.flow.catch
|
import kotlinx.coroutines.flow.catch
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.singleOrNull
|
import kotlinx.coroutines.flow.singleOrNull
|
||||||
import me.tatarka.inject.annotations.Inject
|
import me.tatarka.inject.annotations.Inject
|
||||||
import org.lighthousegames.logging.logging
|
import org.lighthousegames.logging.logging
|
||||||
@@ -18,7 +19,12 @@ class GetCategories @Inject constructor(private val categoryRepository: Category
|
|||||||
.catch { log.warn(it) { "Failed to get categories" } }
|
.catch { log.warn(it) { "Failed to get categories" } }
|
||||||
.singleOrNull()
|
.singleOrNull()
|
||||||
|
|
||||||
fun asFlow(dropDefault: Boolean = false) = categoryRepository.getCategories(dropDefault)
|
fun asFlow(dropDefault: Boolean = false) = categoryRepository.getCategories()
|
||||||
|
.map { categories ->
|
||||||
|
if (dropDefault) {
|
||||||
|
categories.filterNot { it.name.equals("default", true) }
|
||||||
|
} else categories
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val log = logging()
|
private val log = logging()
|
||||||
|
|||||||
@@ -15,28 +15,24 @@ import org.lighthousegames.logging.logging
|
|||||||
|
|
||||||
class ModifyCategory @Inject constructor(private val categoryRepository: CategoryRepository) {
|
class ModifyCategory @Inject constructor(private val categoryRepository: CategoryRepository) {
|
||||||
|
|
||||||
suspend fun await(categoryId: Long, name: String? = null, isLanding: Boolean? = null) = asFlow(
|
suspend fun await(categoryId: Long, name: String) = asFlow(
|
||||||
categoryId = categoryId,
|
categoryId = categoryId,
|
||||||
name = name,
|
name = name
|
||||||
isLanding = isLanding
|
).catch { log.warn(it) { "Failed to modify category $categoryId with options: name=$name" } }.collect()
|
||||||
).catch { log.warn(it) { "Failed to modify category $categoryId with options: name=$name,isLanding=$isLanding" } }.collect()
|
|
||||||
|
|
||||||
suspend fun await(category: Category, name: String? = null, isLanding: Boolean? = null) = asFlow(
|
suspend fun await(category: Category, name: String? = null) = asFlow(
|
||||||
category = category,
|
category = category,
|
||||||
name = name,
|
name = name
|
||||||
isLanding = isLanding
|
).catch { log.warn(it) { "Failed to modify category ${category.name} with options: name=$name" } }.collect()
|
||||||
).catch { log.warn(it) { "Failed to modify category ${category.name} with options: name=$name,isLanding=$isLanding" } }.collect()
|
|
||||||
|
|
||||||
fun asFlow(categoryId: Long, name: String? = null, isLanding: Boolean? = null) = categoryRepository.modifyCategory(
|
fun asFlow(categoryId: Long, name: String) = categoryRepository.modifyCategory(
|
||||||
categoryId = categoryId,
|
categoryId = categoryId,
|
||||||
name = name,
|
name = name
|
||||||
isLanding = isLanding
|
|
||||||
)
|
)
|
||||||
|
|
||||||
fun asFlow(category: Category, name: String? = null, isLanding: Boolean? = null) = categoryRepository.modifyCategory(
|
fun asFlow(category: Category, name: String? = null) = categoryRepository.modifyCategory(
|
||||||
categoryId = category.id,
|
categoryId = category.id,
|
||||||
name = name,
|
name = name ?: category.name
|
||||||
isLanding = isLanding
|
|
||||||
)
|
)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@@ -8,17 +8,64 @@ package ca.gosyer.jui.domain.category.service
|
|||||||
|
|
||||||
import ca.gosyer.jui.domain.category.model.Category
|
import ca.gosyer.jui.domain.category.model.Category
|
||||||
import ca.gosyer.jui.domain.manga.model.Manga
|
import ca.gosyer.jui.domain.manga.model.Manga
|
||||||
|
import de.jensklingenberg.ktorfit.http.DELETE
|
||||||
|
import de.jensklingenberg.ktorfit.http.Field
|
||||||
|
import de.jensklingenberg.ktorfit.http.FormUrlEncoded
|
||||||
|
import de.jensklingenberg.ktorfit.http.GET
|
||||||
|
import de.jensklingenberg.ktorfit.http.PATCH
|
||||||
|
import de.jensklingenberg.ktorfit.http.POST
|
||||||
|
import de.jensklingenberg.ktorfit.http.Path
|
||||||
import io.ktor.client.statement.HttpResponse
|
import io.ktor.client.statement.HttpResponse
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
interface CategoryRepository {
|
interface CategoryRepository {
|
||||||
fun getMangaCategories(mangaId: Long): Flow<List<Category>>
|
@GET("api/v1/manga/{mangaId}/category/")
|
||||||
fun addMangaToCategory(mangaId: Long, categoryId: Long): Flow<HttpResponse>
|
fun getMangaCategories(
|
||||||
fun removeMangaFromCategory(mangaId: Long, categoryId: Long): Flow<HttpResponse>
|
@Path("mangaId") mangaId: Long
|
||||||
fun getCategories(dropDefault: Boolean = false): Flow<List<Category>>
|
): Flow<List<Category>>
|
||||||
fun createCategory(name: String): Flow<HttpResponse>
|
|
||||||
fun modifyCategory(categoryId: Long, name: String? = null, isLanding: Boolean? = null): Flow<HttpResponse>
|
@GET("api/v1/manga/{mangaId}/category/{categoryId}")
|
||||||
fun reorderCategory(to: Int, from: Int): Flow<HttpResponse>
|
fun addMangaToCategory(
|
||||||
fun deleteCategory(categoryId: Long): Flow<HttpResponse>
|
@Path("mangaId") mangaId: Long,
|
||||||
fun getMangaFromCategory(categoryId: Long): Flow<List<Manga>>
|
@Path("categoryId") categoryId: Long
|
||||||
|
): Flow<HttpResponse>
|
||||||
|
|
||||||
|
@DELETE("api/v1/manga/{mangaId}/category/{categoryId}")
|
||||||
|
fun removeMangaFromCategory(
|
||||||
|
@Path("mangaId") mangaId: Long,
|
||||||
|
@Path("categoryId") categoryId: Long
|
||||||
|
): Flow<HttpResponse>
|
||||||
|
|
||||||
|
@GET("api/v1/category/")
|
||||||
|
fun getCategories(): Flow<List<Category>>
|
||||||
|
|
||||||
|
@FormUrlEncoded
|
||||||
|
@POST("api/v1/category/")
|
||||||
|
fun createCategory(
|
||||||
|
@Field("name") name: String
|
||||||
|
): Flow<HttpResponse>
|
||||||
|
|
||||||
|
@FormUrlEncoded
|
||||||
|
@PATCH("api/v1/category/{categoryId}")
|
||||||
|
fun modifyCategory(
|
||||||
|
@Path("categoryId") categoryId: Long,
|
||||||
|
@Field("name") name: String
|
||||||
|
): Flow<HttpResponse>
|
||||||
|
|
||||||
|
@FormUrlEncoded
|
||||||
|
@PATCH("api/v1/category/reorder")
|
||||||
|
fun reorderCategory(
|
||||||
|
@Field("to") to: Int,
|
||||||
|
@Field("from") from: Int
|
||||||
|
): Flow<HttpResponse>
|
||||||
|
|
||||||
|
@DELETE("api/v1/category/{categoryId}")
|
||||||
|
fun deleteCategory(
|
||||||
|
@Path("categoryId") categoryId: Long
|
||||||
|
): Flow<HttpResponse>
|
||||||
|
|
||||||
|
@GET("api/v1/category/{categoryId}")
|
||||||
|
fun getMangaFromCategory(
|
||||||
|
@Path("categoryId") categoryId: Long
|
||||||
|
): Flow<List<Manga>>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* 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.interactor
|
||||||
|
|
||||||
|
import ca.gosyer.jui.domain.chapter.model.Chapter
|
||||||
|
import ca.gosyer.jui.domain.chapter.service.ChapterRepository
|
||||||
|
import ca.gosyer.jui.domain.manga.model.Manga
|
||||||
|
import kotlinx.coroutines.flow.catch
|
||||||
|
import kotlinx.coroutines.flow.collect
|
||||||
|
import me.tatarka.inject.annotations.Inject
|
||||||
|
import org.lighthousegames.logging.logging
|
||||||
|
|
||||||
|
class UpdateChapterBookmarked @Inject constructor(private val chapterRepository: ChapterRepository) {
|
||||||
|
|
||||||
|
suspend fun await(
|
||||||
|
mangaId: Long,
|
||||||
|
index: Int,
|
||||||
|
bookmarked: Boolean,
|
||||||
|
) = asFlow(mangaId, index, bookmarked)
|
||||||
|
.catch { log.warn(it) { "Failed to update chapter bookmark for chapter $index of $mangaId" } }
|
||||||
|
.collect()
|
||||||
|
|
||||||
|
suspend fun await(
|
||||||
|
manga: Manga,
|
||||||
|
index: Int,
|
||||||
|
bookmarked: Boolean,
|
||||||
|
) = asFlow(manga, index, bookmarked)
|
||||||
|
.catch { log.warn(it) { "Failed to update chapter bookmark for chapter $index of ${manga.title}(${manga.id})" } }
|
||||||
|
.collect()
|
||||||
|
|
||||||
|
suspend fun await(
|
||||||
|
chapter: Chapter,
|
||||||
|
bookmarked: Boolean,
|
||||||
|
) = asFlow(chapter, bookmarked)
|
||||||
|
.catch { log.warn(it) { "Failed to update chapter bookmark for chapter ${chapter.index} of ${chapter.mangaId}" } }
|
||||||
|
.collect()
|
||||||
|
|
||||||
|
fun asFlow(
|
||||||
|
mangaId: Long,
|
||||||
|
index: Int,
|
||||||
|
bookmarked: Boolean,
|
||||||
|
) = chapterRepository.updateChapterBookmarked(
|
||||||
|
mangaId = mangaId,
|
||||||
|
chapterIndex = index,
|
||||||
|
bookmarked = bookmarked,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun asFlow(
|
||||||
|
manga: Manga,
|
||||||
|
index: Int,
|
||||||
|
bookmarked: Boolean,
|
||||||
|
) = chapterRepository.updateChapterBookmarked(
|
||||||
|
mangaId = manga.id,
|
||||||
|
chapterIndex = index,
|
||||||
|
bookmarked = bookmarked,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun asFlow(
|
||||||
|
chapter: Chapter,
|
||||||
|
bookmarked: Boolean,
|
||||||
|
) = chapterRepository.updateChapterBookmarked(
|
||||||
|
mangaId = chapter.mangaId,
|
||||||
|
chapterIndex = chapter.index,
|
||||||
|
bookmarked = bookmarked,
|
||||||
|
)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val log = logging()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,101 +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.interactor
|
|
||||||
|
|
||||||
import ca.gosyer.jui.domain.chapter.model.Chapter
|
|
||||||
import ca.gosyer.jui.domain.chapter.service.ChapterRepository
|
|
||||||
import ca.gosyer.jui.domain.manga.model.Manga
|
|
||||||
import kotlinx.coroutines.flow.catch
|
|
||||||
import kotlinx.coroutines.flow.collect
|
|
||||||
import me.tatarka.inject.annotations.Inject
|
|
||||||
import org.lighthousegames.logging.logging
|
|
||||||
|
|
||||||
class UpdateChapterFlags @Inject constructor(private val chapterRepository: ChapterRepository) {
|
|
||||||
|
|
||||||
suspend fun await(
|
|
||||||
mangaId: Long,
|
|
||||||
index: Int,
|
|
||||||
read: Boolean? = null,
|
|
||||||
bookmarked: Boolean? = null,
|
|
||||||
lastPageRead: Int? = null,
|
|
||||||
markPreviousRead: Boolean? = null
|
|
||||||
) = asFlow(mangaId, index, read, bookmarked, lastPageRead, markPreviousRead)
|
|
||||||
.catch { log.warn(it) { "Failed to update chapter flags for chapter $index of $mangaId" } }
|
|
||||||
.collect()
|
|
||||||
|
|
||||||
suspend fun await(
|
|
||||||
manga: Manga,
|
|
||||||
index: Int,
|
|
||||||
read: Boolean? = null,
|
|
||||||
bookmarked: Boolean? = null,
|
|
||||||
lastPageRead: Int? = null,
|
|
||||||
markPreviousRead: Boolean? = null
|
|
||||||
) = asFlow(manga, index, read, bookmarked, lastPageRead, markPreviousRead)
|
|
||||||
.catch { log.warn(it) { "Failed to update chapter flags for chapter $index of ${manga.title}(${manga.id})" } }
|
|
||||||
.collect()
|
|
||||||
|
|
||||||
suspend fun await(
|
|
||||||
chapter: Chapter,
|
|
||||||
read: Boolean? = null,
|
|
||||||
bookmarked: Boolean? = null,
|
|
||||||
lastPageRead: Int? = null,
|
|
||||||
markPreviousRead: Boolean? = null
|
|
||||||
) = asFlow(chapter, read, bookmarked, lastPageRead, markPreviousRead)
|
|
||||||
.catch { log.warn(it) { "Failed to update chapter flags for chapter ${chapter.index} of ${chapter.mangaId}" } }
|
|
||||||
.collect()
|
|
||||||
|
|
||||||
fun asFlow(
|
|
||||||
mangaId: Long,
|
|
||||||
index: Int,
|
|
||||||
read: Boolean? = null,
|
|
||||||
bookmarked: Boolean? = null,
|
|
||||||
lastPageRead: Int? = null,
|
|
||||||
markPreviousRead: Boolean? = null
|
|
||||||
) = chapterRepository.updateChapter(
|
|
||||||
mangaId = mangaId,
|
|
||||||
chapterIndex = index,
|
|
||||||
read = read,
|
|
||||||
bookmarked = bookmarked,
|
|
||||||
lastPageRead = lastPageRead,
|
|
||||||
markPreviousRead = markPreviousRead
|
|
||||||
)
|
|
||||||
|
|
||||||
fun asFlow(
|
|
||||||
manga: Manga,
|
|
||||||
index: Int,
|
|
||||||
read: Boolean? = null,
|
|
||||||
bookmarked: Boolean? = null,
|
|
||||||
lastPageRead: Int? = null,
|
|
||||||
markPreviousRead: Boolean? = null
|
|
||||||
) = chapterRepository.updateChapter(
|
|
||||||
mangaId = manga.id,
|
|
||||||
chapterIndex = index,
|
|
||||||
read = read,
|
|
||||||
bookmarked = bookmarked,
|
|
||||||
lastPageRead = lastPageRead,
|
|
||||||
markPreviousRead = markPreviousRead
|
|
||||||
)
|
|
||||||
|
|
||||||
fun asFlow(
|
|
||||||
chapter: Chapter,
|
|
||||||
read: Boolean? = null,
|
|
||||||
bookmarked: Boolean? = null,
|
|
||||||
lastPageRead: Int? = null,
|
|
||||||
markPreviousRead: Boolean? = null
|
|
||||||
) = chapterRepository.updateChapter(
|
|
||||||
mangaId = chapter.mangaId,
|
|
||||||
chapterIndex = chapter.index,
|
|
||||||
read = read,
|
|
||||||
bookmarked = bookmarked,
|
|
||||||
lastPageRead = lastPageRead,
|
|
||||||
markPreviousRead = markPreviousRead
|
|
||||||
)
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val log = logging()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* 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.interactor
|
||||||
|
|
||||||
|
import ca.gosyer.jui.domain.chapter.model.Chapter
|
||||||
|
import ca.gosyer.jui.domain.chapter.service.ChapterRepository
|
||||||
|
import ca.gosyer.jui.domain.manga.model.Manga
|
||||||
|
import kotlinx.coroutines.flow.catch
|
||||||
|
import kotlinx.coroutines.flow.collect
|
||||||
|
import me.tatarka.inject.annotations.Inject
|
||||||
|
import org.lighthousegames.logging.logging
|
||||||
|
|
||||||
|
class UpdateChapterLastPageRead @Inject constructor(private val chapterRepository: ChapterRepository) {
|
||||||
|
|
||||||
|
suspend fun await(
|
||||||
|
mangaId: Long,
|
||||||
|
index: Int,
|
||||||
|
lastPageRead: Int,
|
||||||
|
) = asFlow(mangaId, index, lastPageRead)
|
||||||
|
.catch { log.warn(it) { "Failed to update chapter last page read for chapter $index of $mangaId" } }
|
||||||
|
.collect()
|
||||||
|
|
||||||
|
suspend fun await(
|
||||||
|
manga: Manga,
|
||||||
|
index: Int,
|
||||||
|
lastPageRead: Int,
|
||||||
|
) = asFlow(manga, index, lastPageRead)
|
||||||
|
.catch { log.warn(it) { "Failed to update chapter last page read for chapter $index of ${manga.title}(${manga.id})" } }
|
||||||
|
.collect()
|
||||||
|
|
||||||
|
suspend fun await(
|
||||||
|
chapter: Chapter,
|
||||||
|
lastPageRead: Int,
|
||||||
|
) = asFlow(chapter, lastPageRead)
|
||||||
|
.catch { log.warn(it) { "Failed to update chapter last page read for chapter ${chapter.index} of ${chapter.mangaId}" } }
|
||||||
|
.collect()
|
||||||
|
|
||||||
|
fun asFlow(
|
||||||
|
mangaId: Long,
|
||||||
|
index: Int,
|
||||||
|
lastPageRead: Int,
|
||||||
|
) = chapterRepository.updateChapterLastPageRead(
|
||||||
|
mangaId = mangaId,
|
||||||
|
chapterIndex = index,
|
||||||
|
lastPageRead = lastPageRead,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun asFlow(
|
||||||
|
manga: Manga,
|
||||||
|
index: Int,
|
||||||
|
lastPageRead: Int,
|
||||||
|
) = chapterRepository.updateChapterLastPageRead(
|
||||||
|
mangaId = manga.id,
|
||||||
|
chapterIndex = index,
|
||||||
|
lastPageRead = lastPageRead,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun asFlow(
|
||||||
|
chapter: Chapter,
|
||||||
|
lastPageRead: Int,
|
||||||
|
) = chapterRepository.updateChapterLastPageRead(
|
||||||
|
mangaId = chapter.mangaId,
|
||||||
|
chapterIndex = chapter.index,
|
||||||
|
lastPageRead = lastPageRead,
|
||||||
|
)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val log = logging()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* 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.interactor
|
||||||
|
|
||||||
|
import ca.gosyer.jui.domain.chapter.model.Chapter
|
||||||
|
import ca.gosyer.jui.domain.chapter.service.ChapterRepository
|
||||||
|
import ca.gosyer.jui.domain.manga.model.Manga
|
||||||
|
import kotlinx.coroutines.flow.catch
|
||||||
|
import kotlinx.coroutines.flow.collect
|
||||||
|
import me.tatarka.inject.annotations.Inject
|
||||||
|
import org.lighthousegames.logging.logging
|
||||||
|
|
||||||
|
class UpdateChapterMarkPreviousRead @Inject constructor(private val chapterRepository: ChapterRepository) {
|
||||||
|
|
||||||
|
suspend fun await(
|
||||||
|
mangaId: Long,
|
||||||
|
index: Int
|
||||||
|
) = asFlow(mangaId, index)
|
||||||
|
.catch { log.warn(it) { "Failed to update chapter read status for chapter $index of $mangaId" } }
|
||||||
|
.collect()
|
||||||
|
|
||||||
|
suspend fun await(
|
||||||
|
manga: Manga,
|
||||||
|
index: Int
|
||||||
|
) = asFlow(manga, index)
|
||||||
|
.catch { log.warn(it) { "Failed to update chapter read status for chapter $index of ${manga.title}(${manga.id})" } }
|
||||||
|
.collect()
|
||||||
|
|
||||||
|
suspend fun await(
|
||||||
|
chapter: Chapter
|
||||||
|
) = asFlow(chapter)
|
||||||
|
.catch { log.warn(it) { "Failed to update chapter read status for chapter ${chapter.index} of ${chapter.mangaId}" } }
|
||||||
|
.collect()
|
||||||
|
|
||||||
|
fun asFlow(
|
||||||
|
mangaId: Long,
|
||||||
|
index: Int,
|
||||||
|
) = chapterRepository.updateChapterMarkPrevRead(
|
||||||
|
mangaId = mangaId,
|
||||||
|
chapterIndex = index
|
||||||
|
)
|
||||||
|
|
||||||
|
fun asFlow(
|
||||||
|
manga: Manga,
|
||||||
|
index: Int,
|
||||||
|
) = chapterRepository.updateChapterMarkPrevRead(
|
||||||
|
mangaId = manga.id,
|
||||||
|
chapterIndex = index,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun asFlow(
|
||||||
|
chapter: Chapter,
|
||||||
|
) = chapterRepository.updateChapterMarkPrevRead(
|
||||||
|
mangaId = chapter.mangaId,
|
||||||
|
chapterIndex = chapter.index,
|
||||||
|
)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val log = logging()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* 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.interactor
|
||||||
|
|
||||||
|
import ca.gosyer.jui.domain.chapter.model.Chapter
|
||||||
|
import ca.gosyer.jui.domain.chapter.service.ChapterRepository
|
||||||
|
import ca.gosyer.jui.domain.manga.model.Manga
|
||||||
|
import kotlinx.coroutines.flow.catch
|
||||||
|
import kotlinx.coroutines.flow.collect
|
||||||
|
import me.tatarka.inject.annotations.Inject
|
||||||
|
import org.lighthousegames.logging.logging
|
||||||
|
|
||||||
|
class UpdateChapterRead @Inject constructor(private val chapterRepository: ChapterRepository) {
|
||||||
|
|
||||||
|
suspend fun await(
|
||||||
|
mangaId: Long,
|
||||||
|
index: Int,
|
||||||
|
read: Boolean,
|
||||||
|
) = asFlow(mangaId, index, read)
|
||||||
|
.catch { log.warn(it) { "Failed to update chapter read status for chapter $index of $mangaId" } }
|
||||||
|
.collect()
|
||||||
|
|
||||||
|
suspend fun await(
|
||||||
|
manga: Manga,
|
||||||
|
index: Int,
|
||||||
|
read: Boolean,
|
||||||
|
) = asFlow(manga, index, read)
|
||||||
|
.catch { log.warn(it) { "Failed to update chapter read status for chapter $index of ${manga.title}(${manga.id})" } }
|
||||||
|
.collect()
|
||||||
|
|
||||||
|
suspend fun await(
|
||||||
|
chapter: Chapter,
|
||||||
|
read: Boolean,
|
||||||
|
) = asFlow(chapter, read)
|
||||||
|
.catch { log.warn(it) { "Failed to update chapter read status for chapter ${chapter.index} of ${chapter.mangaId}" } }
|
||||||
|
.collect()
|
||||||
|
|
||||||
|
fun asFlow(
|
||||||
|
mangaId: Long,
|
||||||
|
index: Int,
|
||||||
|
read: Boolean,
|
||||||
|
) = chapterRepository.updateChapterRead(
|
||||||
|
mangaId = mangaId,
|
||||||
|
chapterIndex = index,
|
||||||
|
read = read,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun asFlow(
|
||||||
|
manga: Manga,
|
||||||
|
index: Int,
|
||||||
|
read: Boolean,
|
||||||
|
) = chapterRepository.updateChapterRead(
|
||||||
|
mangaId = manga.id,
|
||||||
|
chapterIndex = index,
|
||||||
|
read = read,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun asFlow(
|
||||||
|
chapter: Chapter,
|
||||||
|
read: Boolean,
|
||||||
|
) = chapterRepository.updateChapterRead(
|
||||||
|
mangaId = chapter.mangaId,
|
||||||
|
chapterIndex = chapter.index,
|
||||||
|
read = read,
|
||||||
|
)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val log = logging()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,31 +7,109 @@
|
|||||||
package ca.gosyer.jui.domain.chapter.service
|
package ca.gosyer.jui.domain.chapter.service
|
||||||
|
|
||||||
import ca.gosyer.jui.domain.chapter.model.Chapter
|
import ca.gosyer.jui.domain.chapter.model.Chapter
|
||||||
|
import de.jensklingenberg.ktorfit.http.DELETE
|
||||||
|
import de.jensklingenberg.ktorfit.http.Field
|
||||||
|
import de.jensklingenberg.ktorfit.http.FormUrlEncoded
|
||||||
|
import de.jensklingenberg.ktorfit.http.GET
|
||||||
|
import de.jensklingenberg.ktorfit.http.PATCH
|
||||||
|
import de.jensklingenberg.ktorfit.http.Path
|
||||||
|
import de.jensklingenberg.ktorfit.http.Query
|
||||||
|
import de.jensklingenberg.ktorfit.http.ReqBuilder
|
||||||
import io.ktor.client.request.HttpRequestBuilder
|
import io.ktor.client.request.HttpRequestBuilder
|
||||||
import io.ktor.client.statement.HttpResponse
|
import io.ktor.client.statement.HttpResponse
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
interface ChapterRepository {
|
interface ChapterRepository {
|
||||||
fun getChapters(mangaId: Long, refresh: Boolean = false): Flow<List<Chapter>>
|
|
||||||
fun getChapter(mangaId: Long, chapterIndex: Int): Flow<Chapter>
|
@GET("api/v1/manga/{mangaId}/chapters")
|
||||||
|
fun getChapters(
|
||||||
|
@Path("mangaId") mangaId: Long,
|
||||||
|
@Query("onlineFetch") refresh: Boolean = false
|
||||||
|
): Flow<List<Chapter>>
|
||||||
|
|
||||||
|
@GET("api/v1/manga/{mangaId}/chapter/{chapterIndex}")
|
||||||
|
fun getChapter(
|
||||||
|
@Path("mangaId") mangaId: Long,
|
||||||
|
@Path("chapterIndex") chapterIndex: Int
|
||||||
|
): Flow<Chapter>
|
||||||
|
|
||||||
|
/* TODO add once ktorfit supports nullable paremters
|
||||||
|
@FormUrlEncoded
|
||||||
|
@PATCH("/api/v1/manga/{mangaId}/chapter/{chapterIndex}")
|
||||||
fun updateChapter(
|
fun updateChapter(
|
||||||
mangaId: Long,
|
@Path("mangaId") mangaId: Long,
|
||||||
chapterIndex: Int,
|
@Path("chapterIndex") chapterIndex: Int,
|
||||||
read: Boolean? = null,
|
@Field("read") read: Boolean? = null,
|
||||||
bookmarked: Boolean? = null,
|
@Field("bookmarked") bookmarked: Boolean? = null,
|
||||||
lastPageRead: Int? = null,
|
@Field("lastPageRead") lastPageRead: Int? = null,
|
||||||
markPreviousRead: Boolean? = null
|
@Field("markPrevRead") markPreviousRead: Boolean? = null
|
||||||
|
): Flow<HttpResponse>*/
|
||||||
|
|
||||||
|
//todo remove following updateChapter functions once ktorfit supports nullable parameters
|
||||||
|
@FormUrlEncoded
|
||||||
|
@PATCH("api/v1/manga/{mangaId}/chapter/{chapterIndex}")
|
||||||
|
fun updateChapterRead(
|
||||||
|
@Path("mangaId") mangaId: Long,
|
||||||
|
@Path("chapterIndex") chapterIndex: Int,
|
||||||
|
@Field("read") read: Boolean,
|
||||||
): Flow<HttpResponse>
|
): Flow<HttpResponse>
|
||||||
|
|
||||||
|
@FormUrlEncoded
|
||||||
|
@PATCH("api/v1/manga/{mangaId}/chapter/{chapterIndex}")
|
||||||
|
fun updateChapterBookmarked(
|
||||||
|
@Path("mangaId") mangaId: Long,
|
||||||
|
@Path("chapterIndex") chapterIndex: Int,
|
||||||
|
@Field("bookmarked") bookmarked: Boolean,
|
||||||
|
): Flow<HttpResponse>
|
||||||
|
|
||||||
|
@FormUrlEncoded
|
||||||
|
@PATCH("api/v1/manga/{mangaId}/chapter/{chapterIndex}")
|
||||||
|
fun updateChapterLastPageRead(
|
||||||
|
@Path("mangaId") mangaId: Long,
|
||||||
|
@Path("chapterIndex") chapterIndex: Int,
|
||||||
|
@Field("lastPageRead") lastPageRead: Int,
|
||||||
|
): Flow<HttpResponse>
|
||||||
|
|
||||||
|
@FormUrlEncoded
|
||||||
|
@PATCH("api/v1/manga/{mangaId}/chapter/{chapterIndex}")
|
||||||
|
fun updateChapterMarkPrevRead(
|
||||||
|
@Path("mangaId") mangaId: Long,
|
||||||
|
@Path("chapterIndex") chapterIndex: Int,
|
||||||
|
@Field("markPrevRead") markPreviousRead: Boolean = true
|
||||||
|
): Flow<HttpResponse>
|
||||||
|
|
||||||
|
@GET("api/v1/manga/{mangaId}/chapter/{chapterIndex}/page/{pageNum}")
|
||||||
fun getPage(
|
fun getPage(
|
||||||
mangaId: Long,
|
@Path("mangaId") mangaId: Long,
|
||||||
chapterIndex: Int,
|
@Path("chapterIndex") chapterIndex: Int,
|
||||||
pageNum: Int,
|
@Path("pageNum") pageNum: Int,
|
||||||
block: HttpRequestBuilder.() -> Unit
|
@ReqBuilder block: HttpRequestBuilder.() -> Unit
|
||||||
): Flow<HttpResponse>
|
): Flow<HttpResponse>
|
||||||
|
|
||||||
fun deleteChapterDownload(mangaId: Long, chapterIndex: Int): Flow<HttpResponse>
|
@DELETE("api/v1/manga/{mangaId}/chapter/{chapterIndex}")
|
||||||
fun queueChapterDownload(mangaId: Long, chapterIndex: Int): Flow<HttpResponse>
|
fun deleteChapterDownload(
|
||||||
fun stopChapterDownload(mangaId: Long, chapterIndex: Int): Flow<HttpResponse>
|
@Path("mangaId") mangaId: Long,
|
||||||
fun updateChapterMeta(mangaId: Long, chapterIndex: Int, key: String, value: String): Flow<HttpResponse>
|
@Path("chapterIndex") chapterIndex: Int
|
||||||
|
): Flow<HttpResponse>
|
||||||
|
|
||||||
|
@GET("api/v1/download/{mangaId}/chapter/{chapterIndex}")
|
||||||
|
fun queueChapterDownload(
|
||||||
|
@Path("mangaId") mangaId: Long,
|
||||||
|
@Path("chapterIndex") chapterIndex: Int
|
||||||
|
): Flow<HttpResponse>
|
||||||
|
|
||||||
|
@DELETE("api/v1/download/{mangaId}/chapter/{chapterIndex}")
|
||||||
|
fun stopChapterDownload(
|
||||||
|
@Path("mangaId") mangaId: Long,
|
||||||
|
@Path("chapterIndex") chapterIndex: Int
|
||||||
|
): Flow<HttpResponse>
|
||||||
|
|
||||||
|
@FormUrlEncoded
|
||||||
|
@PATCH("api/v1/manga/{mangaId}/chapter/{chapterIndex}/meta")
|
||||||
|
fun updateChapterMeta(
|
||||||
|
@Path("mangaId") mangaId: Long,
|
||||||
|
@Path("chapterIndex") chapterIndex: Int,
|
||||||
|
@Field("key") key: String,
|
||||||
|
@Field("value") value: String
|
||||||
|
): Flow<HttpResponse>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,11 +6,17 @@
|
|||||||
|
|
||||||
package ca.gosyer.jui.domain.download.service
|
package ca.gosyer.jui.domain.download.service
|
||||||
|
|
||||||
|
import de.jensklingenberg.ktorfit.http.GET
|
||||||
import io.ktor.client.statement.HttpResponse
|
import io.ktor.client.statement.HttpResponse
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
interface DownloadRepository {
|
interface DownloadRepository {
|
||||||
|
@GET("api/v1/downloads/start")
|
||||||
fun startDownloading(): Flow<HttpResponse>
|
fun startDownloading(): Flow<HttpResponse>
|
||||||
|
|
||||||
|
@GET("api/v1/downloads/stop")
|
||||||
fun stopDownloading(): Flow<HttpResponse>
|
fun stopDownloading(): Flow<HttpResponse>
|
||||||
|
|
||||||
|
@GET("api/v1/downloads/clear")
|
||||||
fun clearDownloadQueue(): Flow<HttpResponse>
|
fun clearDownloadQueue(): Flow<HttpResponse>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import ca.gosyer.jui.domain.download.model.DownloadChapter
|
|||||||
import ca.gosyer.jui.domain.download.model.DownloadStatus
|
import ca.gosyer.jui.domain.download.model.DownloadStatus
|
||||||
import ca.gosyer.jui.domain.download.model.DownloaderStatus
|
import ca.gosyer.jui.domain.download.model.DownloaderStatus
|
||||||
import ca.gosyer.jui.domain.server.Http
|
import ca.gosyer.jui.domain.server.Http
|
||||||
import ca.gosyer.jui.domain.server.model.requests.downloadsQuery
|
|
||||||
import ca.gosyer.jui.domain.server.service.ServerPreferences
|
import ca.gosyer.jui.domain.server.service.ServerPreferences
|
||||||
import io.ktor.websocket.Frame
|
import io.ktor.websocket.Frame
|
||||||
import io.ktor.websocket.readText
|
import io.ktor.websocket.readText
|
||||||
@@ -28,7 +27,7 @@ class DownloadService @Inject constructor(
|
|||||||
get() = status
|
get() = status
|
||||||
|
|
||||||
override val query: String
|
override val query: String
|
||||||
get() = downloadsQuery()
|
get() = "/api/v1/downloads"
|
||||||
|
|
||||||
override suspend fun onReceived(frame: Frame.Text) {
|
override suspend fun onReceived(frame: Frame.Text) {
|
||||||
val status = json.decodeFromString<DownloadStatus>(frame.readText())
|
val status = json.decodeFromString<DownloadStatus>(frame.readText())
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ class InstallExtension @Inject constructor(private val extensionRepository: Exte
|
|||||||
.catch { log.warn(it) { "Failed to install extension ${extension.apkName}" } }
|
.catch { log.warn(it) { "Failed to install extension ${extension.apkName}" } }
|
||||||
.collect()
|
.collect()
|
||||||
|
|
||||||
fun asFlow(extension: Extension) = extensionRepository.installExtension(extension)
|
fun asFlow(extension: Extension) = extensionRepository.installExtension(extension.pkgName)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val log = logging()
|
private val log = logging()
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ class UninstallExtension @Inject constructor(private val extensionRepository: Ex
|
|||||||
.catch { log.warn(it) { "Failed to uninstall extension ${extension.apkName}" } }
|
.catch { log.warn(it) { "Failed to uninstall extension ${extension.apkName}" } }
|
||||||
.collect()
|
.collect()
|
||||||
|
|
||||||
fun asFlow(extension: Extension) = extensionRepository.uninstallExtension(extension)
|
fun asFlow(extension: Extension) = extensionRepository.uninstallExtension(extension.pkgName)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val log = logging()
|
private val log = logging()
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ class UpdateExtension @Inject constructor(private val extensionRepository: Exten
|
|||||||
.catch { log.warn(it) { "Failed to update extension ${extension.apkName}" } }
|
.catch { log.warn(it) { "Failed to update extension ${extension.apkName}" } }
|
||||||
.collect()
|
.collect()
|
||||||
|
|
||||||
fun asFlow(extension: Extension) = extensionRepository.updateExtension(extension)
|
fun asFlow(extension: Extension) = extensionRepository.updateExtension(extension.pkgName)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val log = logging()
|
private val log = logging()
|
||||||
|
|||||||
@@ -7,15 +7,36 @@
|
|||||||
package ca.gosyer.jui.domain.extension.service
|
package ca.gosyer.jui.domain.extension.service
|
||||||
|
|
||||||
import ca.gosyer.jui.domain.extension.model.Extension
|
import ca.gosyer.jui.domain.extension.model.Extension
|
||||||
|
import de.jensklingenberg.ktorfit.http.GET
|
||||||
|
import de.jensklingenberg.ktorfit.http.Path
|
||||||
|
import de.jensklingenberg.ktorfit.http.ReqBuilder
|
||||||
import io.ktor.client.request.HttpRequestBuilder
|
import io.ktor.client.request.HttpRequestBuilder
|
||||||
import io.ktor.client.statement.HttpResponse
|
import io.ktor.client.statement.HttpResponse
|
||||||
import io.ktor.utils.io.ByteReadChannel
|
import io.ktor.utils.io.ByteReadChannel
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
interface ExtensionRepository {
|
interface ExtensionRepository {
|
||||||
|
@GET("api/v1/extension/list")
|
||||||
fun getExtensionList(): Flow<List<Extension>>
|
fun getExtensionList(): Flow<List<Extension>>
|
||||||
fun installExtension(extension: Extension): Flow<HttpResponse>
|
|
||||||
fun updateExtension(extension: Extension): Flow<HttpResponse>
|
@GET("api/v1/extension/install/{pkgName}")
|
||||||
fun uninstallExtension(extension: Extension): Flow<HttpResponse>
|
fun installExtension(
|
||||||
fun getApkIcon(extension: Extension, block: HttpRequestBuilder.() -> Unit): Flow<ByteReadChannel>
|
@Path("pkgName") pkgName: String
|
||||||
|
): Flow<HttpResponse>
|
||||||
|
|
||||||
|
@GET("api/v1/extension/update/{pkgName}")
|
||||||
|
fun updateExtension(
|
||||||
|
@Path("pkgName") pkgName: String
|
||||||
|
): Flow<HttpResponse>
|
||||||
|
|
||||||
|
@GET("api/v1/extension/uninstall/{pkgName}")
|
||||||
|
fun uninstallExtension(
|
||||||
|
@Path("pkgName") pkgName: String
|
||||||
|
): Flow<HttpResponse>
|
||||||
|
|
||||||
|
@GET("api/v1/extension/icon/{apkName}")
|
||||||
|
fun getApkIcon(
|
||||||
|
@Path("apkName") apkName: String,
|
||||||
|
@ReqBuilder block: HttpRequestBuilder.() -> Unit
|
||||||
|
): Flow<ByteReadChannel>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,10 +6,21 @@
|
|||||||
|
|
||||||
package ca.gosyer.jui.domain.library.service
|
package ca.gosyer.jui.domain.library.service
|
||||||
|
|
||||||
|
import de.jensklingenberg.ktorfit.http.DELETE
|
||||||
|
import de.jensklingenberg.ktorfit.http.GET
|
||||||
|
import de.jensklingenberg.ktorfit.http.Path
|
||||||
import io.ktor.client.statement.HttpResponse
|
import io.ktor.client.statement.HttpResponse
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
interface LibraryRepository {
|
interface LibraryRepository {
|
||||||
fun addMangaToLibrary(mangaId: Long): Flow<HttpResponse>
|
|
||||||
fun removeMangaFromLibrary(mangaId: Long): Flow<HttpResponse>
|
@GET("api/v1/manga/{mangaId}/library")
|
||||||
|
fun addMangaToLibrary(
|
||||||
|
@Path("mangaId") mangaId: Long
|
||||||
|
): Flow<HttpResponse>
|
||||||
|
|
||||||
|
@DELETE("api/v1/manga/{mangaId}/library")
|
||||||
|
fun removeMangaFromLibrary(
|
||||||
|
@Path("mangaId") mangaId: Long
|
||||||
|
): Flow<HttpResponse>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ package ca.gosyer.jui.domain.library.service
|
|||||||
import ca.gosyer.jui.domain.base.WebsocketService
|
import ca.gosyer.jui.domain.base.WebsocketService
|
||||||
import ca.gosyer.jui.domain.library.model.UpdateStatus
|
import ca.gosyer.jui.domain.library.model.UpdateStatus
|
||||||
import ca.gosyer.jui.domain.server.Http
|
import ca.gosyer.jui.domain.server.Http
|
||||||
import ca.gosyer.jui.domain.server.model.requests.updatesQuery
|
|
||||||
import ca.gosyer.jui.domain.server.service.ServerPreferences
|
import ca.gosyer.jui.domain.server.service.ServerPreferences
|
||||||
import io.ktor.websocket.Frame
|
import io.ktor.websocket.Frame
|
||||||
import io.ktor.websocket.readText
|
import io.ktor.websocket.readText
|
||||||
@@ -26,7 +25,7 @@ class LibraryUpdateService @Inject constructor(
|
|||||||
override val _status: MutableStateFlow<Status> = MutableStateFlow(Status.STARTING)
|
override val _status: MutableStateFlow<Status> = MutableStateFlow(Status.STARTING)
|
||||||
|
|
||||||
override val query: String
|
override val query: String
|
||||||
get() = updatesQuery()
|
get() = "/api/v1/update"
|
||||||
|
|
||||||
override suspend fun onReceived(frame: Frame.Text) {
|
override suspend fun onReceived(frame: Frame.Text) {
|
||||||
val status = json.decodeFromString<UpdateStatus>(frame.readText())
|
val status = json.decodeFromString<UpdateStatus>(frame.readText())
|
||||||
|
|||||||
@@ -7,13 +7,36 @@
|
|||||||
package ca.gosyer.jui.domain.manga.service
|
package ca.gosyer.jui.domain.manga.service
|
||||||
|
|
||||||
import ca.gosyer.jui.domain.manga.model.Manga
|
import ca.gosyer.jui.domain.manga.model.Manga
|
||||||
|
import de.jensklingenberg.ktorfit.http.Field
|
||||||
|
import de.jensklingenberg.ktorfit.http.FormUrlEncoded
|
||||||
|
import de.jensklingenberg.ktorfit.http.GET
|
||||||
|
import de.jensklingenberg.ktorfit.http.PATCH
|
||||||
|
import de.jensklingenberg.ktorfit.http.Path
|
||||||
|
import de.jensklingenberg.ktorfit.http.Query
|
||||||
|
import de.jensklingenberg.ktorfit.http.ReqBuilder
|
||||||
import io.ktor.client.request.HttpRequestBuilder
|
import io.ktor.client.request.HttpRequestBuilder
|
||||||
import io.ktor.client.statement.HttpResponse
|
import io.ktor.client.statement.HttpResponse
|
||||||
import io.ktor.utils.io.ByteReadChannel
|
import io.ktor.utils.io.ByteReadChannel
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
interface MangaRepository {
|
interface MangaRepository {
|
||||||
fun getManga(mangaId: Long, refresh: Boolean = false): Flow<Manga>
|
@GET("api/v1/manga/{mangaId}/")
|
||||||
fun getMangaThumbnail(mangaId: Long, block: HttpRequestBuilder.() -> Unit): Flow<ByteReadChannel>
|
fun getManga(
|
||||||
fun updateMangaMeta(mangaId: Long, key: String, value: String): Flow<HttpResponse>
|
@Path("mangaId") mangaId: Long,
|
||||||
|
@Query("onlineFetch") refresh: Boolean = false
|
||||||
|
): Flow<Manga>
|
||||||
|
|
||||||
|
@GET("api/v1/manga/{mangaId}/thumbnail")
|
||||||
|
fun getMangaThumbnail(
|
||||||
|
@Path("mangaId") mangaId: Long,
|
||||||
|
@ReqBuilder block: HttpRequestBuilder.() -> Unit
|
||||||
|
): Flow<ByteReadChannel>
|
||||||
|
|
||||||
|
@PATCH("api/v1/manga/{mangaId}/meta")
|
||||||
|
@FormUrlEncoded
|
||||||
|
fun updateMangaMeta(
|
||||||
|
@Path("mangaId") mangaId: Long,
|
||||||
|
@Field("key") key: String,
|
||||||
|
@Field("value") value: String
|
||||||
|
): Flow<HttpResponse>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,27 +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.server.model.requests
|
|
||||||
|
|
||||||
@Post
|
|
||||||
fun backupImportRequest() =
|
|
||||||
"/api/v1/backup/import"
|
|
||||||
|
|
||||||
@Post
|
|
||||||
fun backupFileImportRequest() =
|
|
||||||
"/api/v1/backup/import/file"
|
|
||||||
|
|
||||||
@Post
|
|
||||||
fun backupExportRequest() =
|
|
||||||
"/api/v1/backup/export"
|
|
||||||
|
|
||||||
@Post
|
|
||||||
fun backupFileExportRequest() =
|
|
||||||
"/api/v1/backup/export/file"
|
|
||||||
|
|
||||||
@Post
|
|
||||||
fun validateBackupFileRequest() =
|
|
||||||
"/api/v1/backup/validate/file"
|
|
||||||
@@ -1,46 +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.server.model.requests
|
|
||||||
|
|
||||||
@Get
|
|
||||||
fun getMangaCategoriesQuery(mangaId: Long) =
|
|
||||||
"/api/v1/manga/$mangaId/category/"
|
|
||||||
|
|
||||||
@Get
|
|
||||||
fun addMangaToCategoryQuery(mangaId: Long, categoryId: Long) =
|
|
||||||
"/api/v1/manga/$mangaId/category/$categoryId"
|
|
||||||
|
|
||||||
@Delete
|
|
||||||
fun removeMangaFromCategoryRequest(mangaId: Long, categoryId: Long) =
|
|
||||||
"/api/v1/manga/$mangaId/category/$categoryId"
|
|
||||||
|
|
||||||
@Get
|
|
||||||
fun getCategoriesQuery() =
|
|
||||||
"/api/v1/category/"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Post a formbody with the param {name} for creation of a category
|
|
||||||
*/
|
|
||||||
@Post
|
|
||||||
fun createCategoryRequest() =
|
|
||||||
"/api/v1/category/"
|
|
||||||
|
|
||||||
@Patch
|
|
||||||
fun categoryModifyRequest(categoryId: Long) =
|
|
||||||
"/api/v1/category/$categoryId"
|
|
||||||
|
|
||||||
@Patch
|
|
||||||
fun categoryReorderRequest() =
|
|
||||||
"/api/v1/category/reorder"
|
|
||||||
|
|
||||||
@Delete
|
|
||||||
fun categoryDeleteRequest(categoryId: Long) =
|
|
||||||
"/api/v1/category/$categoryId"
|
|
||||||
|
|
||||||
@Get
|
|
||||||
fun getMangaInCategoryQuery(categoryId: Long) =
|
|
||||||
"/api/v1/category/$categoryId"
|
|
||||||
@@ -1,39 +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.server.model.requests
|
|
||||||
|
|
||||||
@Get
|
|
||||||
fun getMangaChaptersQuery(mangaId: Long) =
|
|
||||||
"/api/v1/manga/$mangaId/chapters"
|
|
||||||
|
|
||||||
@Get
|
|
||||||
fun getChapterQuery(mangaId: Long, chapterIndex: Int) =
|
|
||||||
"/api/v1/manga/$mangaId/chapter/$chapterIndex"
|
|
||||||
|
|
||||||
@Patch
|
|
||||||
fun updateChapterRequest(mangaId: Long, chapterIndex: Int) =
|
|
||||||
"/api/v1/manga/$mangaId/chapter/$chapterIndex"
|
|
||||||
|
|
||||||
@Get
|
|
||||||
fun getPageQuery(mangaId: Long, chapterIndex: Int, index: Int) =
|
|
||||||
"/api/v1/manga/$mangaId/chapter/$chapterIndex/page/$index"
|
|
||||||
|
|
||||||
@Delete
|
|
||||||
fun deleteDownloadedChapterRequest(mangaId: Long, chapterIndex: Int) =
|
|
||||||
"/api/v1/manga/$mangaId/chapter/$chapterIndex"
|
|
||||||
|
|
||||||
@Get
|
|
||||||
fun queueDownloadChapterRequest(mangaId: Long, chapterIndex: Int) =
|
|
||||||
"/api/v1/download/$mangaId/chapter/$chapterIndex"
|
|
||||||
|
|
||||||
@Delete
|
|
||||||
fun stopDownloadingChapterRequest(mangaId: Long, chapterIndex: Int) =
|
|
||||||
"/api/v1/download/$mangaId/chapter/$chapterIndex"
|
|
||||||
|
|
||||||
@Patch
|
|
||||||
fun updateChapterMetaRequest(mangaId: Long, chapterIndex: Int) =
|
|
||||||
"/api/v1/manga/$mangaId/chapter/$chapterIndex/meta"
|
|
||||||
@@ -1,23 +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.server.model.requests
|
|
||||||
|
|
||||||
@WS
|
|
||||||
fun downloadsQuery() =
|
|
||||||
"/api/v1/downloads"
|
|
||||||
|
|
||||||
@Get
|
|
||||||
fun downloadsStartRequest() =
|
|
||||||
"/api/v1/downloads/start"
|
|
||||||
|
|
||||||
@Get
|
|
||||||
fun downloadsStopRequest() =
|
|
||||||
"/api/v1/downloads/stop"
|
|
||||||
|
|
||||||
@Get
|
|
||||||
fun downloadsClearRequest() =
|
|
||||||
"/api/v1/downloads/clear"
|
|
||||||
@@ -1,27 +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.server.model.requests
|
|
||||||
|
|
||||||
@Get
|
|
||||||
fun extensionListQuery() =
|
|
||||||
"/api/v1/extension/list"
|
|
||||||
|
|
||||||
@Get
|
|
||||||
fun apkInstallQuery(pkgName: String) =
|
|
||||||
"/api/v1/extension/install/$pkgName"
|
|
||||||
|
|
||||||
@Get
|
|
||||||
fun apkUpdateQuery(pkgName: String) =
|
|
||||||
"/api/v1/extension/update/$pkgName"
|
|
||||||
|
|
||||||
@Get
|
|
||||||
fun apkUninstallQuery(pkgName: String) =
|
|
||||||
"/api/v1/extension/uninstall/$pkgName"
|
|
||||||
|
|
||||||
@Get
|
|
||||||
fun apkIconQuery(apkName: String) =
|
|
||||||
"/api/v1/extension/icon/$apkName"
|
|
||||||
@@ -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.server.model.requests
|
|
||||||
|
|
||||||
@Get
|
|
||||||
fun addMangaToLibraryQuery(mangaId: Long) =
|
|
||||||
"/api/v1/manga/$mangaId/library"
|
|
||||||
|
|
||||||
@Delete
|
|
||||||
fun removeMangaFromLibraryRequest(mangaId: Long) =
|
|
||||||
"/api/v1/manga/$mangaId/library"
|
|
||||||
@@ -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.server.model.requests
|
|
||||||
|
|
||||||
@Get
|
|
||||||
fun mangaQuery(mangaId: Long) =
|
|
||||||
"/api/v1/manga/$mangaId/"
|
|
||||||
|
|
||||||
@Get
|
|
||||||
fun mangaThumbnailQuery(mangaId: Long) =
|
|
||||||
"/api/v1/manga/$mangaId/thumbnail"
|
|
||||||
|
|
||||||
@Post
|
|
||||||
fun updateMangaMetaRequest(mangaId: Long) =
|
|
||||||
"/api/v1/manga/$mangaId/meta"
|
|
||||||
@@ -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.server.model.requests
|
|
||||||
|
|
||||||
@Get
|
|
||||||
fun aboutQuery() =
|
|
||||||
"/api/v1/settings/about"
|
|
||||||
|
|
||||||
@Get
|
|
||||||
fun checkUpdateQuery() =
|
|
||||||
"/api/v1/settings/check-update"
|
|
||||||
@@ -1,47 +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.server.model.requests
|
|
||||||
|
|
||||||
@Get
|
|
||||||
fun sourceListQuery() =
|
|
||||||
"/api/v1/source/list"
|
|
||||||
|
|
||||||
@Get
|
|
||||||
fun sourceInfoQuery(sourceId: Long) =
|
|
||||||
"/api/v1/source/$sourceId"
|
|
||||||
|
|
||||||
@Get
|
|
||||||
fun sourcePopularQuery(sourceId: Long, pageNum: Int) =
|
|
||||||
"/api/v1/source/$sourceId/popular/$pageNum"
|
|
||||||
|
|
||||||
@Get
|
|
||||||
fun sourceLatestQuery(sourceId: Long, pageNum: Int) =
|
|
||||||
"/api/v1/source/$sourceId/latest/$pageNum"
|
|
||||||
|
|
||||||
@Get
|
|
||||||
fun globalSearchQuery() =
|
|
||||||
"/api/v1/source/all/search"
|
|
||||||
|
|
||||||
@Get
|
|
||||||
fun sourceSearchQuery(sourceId: Long) =
|
|
||||||
"/api/v1/source/$sourceId/search"
|
|
||||||
|
|
||||||
@Get
|
|
||||||
fun getFilterListQuery(sourceId: Long) =
|
|
||||||
"/api/v1/source/$sourceId/filters"
|
|
||||||
|
|
||||||
@Post
|
|
||||||
fun setFilterRequest(sourceId: Long) =
|
|
||||||
"/api/v1/source/$sourceId/filters"
|
|
||||||
|
|
||||||
@Get
|
|
||||||
fun getSourceSettingsQuery(sourceId: Long) =
|
|
||||||
"/api/v1/source/$sourceId/preferences"
|
|
||||||
|
|
||||||
@Post
|
|
||||||
fun updateSourceSettingQuery(sourceId: Long) =
|
|
||||||
"/api/v1/source/$sourceId/preferences"
|
|
||||||
@@ -1,23 +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.server.model.requests
|
|
||||||
|
|
||||||
@Get
|
|
||||||
fun recentUpdatesQuery(pageNum: Int) =
|
|
||||||
"/api/v1/update/recentChapters/$pageNum"
|
|
||||||
|
|
||||||
@Post
|
|
||||||
fun fetchUpdatesRequest() =
|
|
||||||
"/api/v1/update/fetch"
|
|
||||||
|
|
||||||
@Get
|
|
||||||
fun updatesSummaryQuery() =
|
|
||||||
"/api/v1/update/summary"
|
|
||||||
|
|
||||||
@WS
|
|
||||||
fun updatesQuery() =
|
|
||||||
"/api/v1/update"
|
|
||||||
@@ -7,10 +7,15 @@
|
|||||||
package ca.gosyer.jui.domain.settings.service
|
package ca.gosyer.jui.domain.settings.service
|
||||||
|
|
||||||
import ca.gosyer.jui.domain.settings.model.About
|
import ca.gosyer.jui.domain.settings.model.About
|
||||||
|
import de.jensklingenberg.ktorfit.http.GET
|
||||||
|
import de.jensklingenberg.ktorfit.http.POST
|
||||||
import io.ktor.client.statement.HttpResponse
|
import io.ktor.client.statement.HttpResponse
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
interface SettingsRepository {
|
interface SettingsRepository {
|
||||||
|
@GET("api/v1/settings/about")
|
||||||
fun aboutServer(): Flow<About>
|
fun aboutServer(): Flow<About>
|
||||||
|
|
||||||
|
@POST("api/v1/settings/check-update")
|
||||||
fun checkUpdate(): Flow<HttpResponse>
|
fun checkUpdate(): Flow<HttpResponse>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,20 +12,62 @@ import ca.gosyer.jui.domain.source.model.sourcefilters.SourceFilter
|
|||||||
import ca.gosyer.jui.domain.source.model.sourcefilters.SourceFilterChange
|
import ca.gosyer.jui.domain.source.model.sourcefilters.SourceFilterChange
|
||||||
import ca.gosyer.jui.domain.source.model.sourcepreference.SourcePreference
|
import ca.gosyer.jui.domain.source.model.sourcepreference.SourcePreference
|
||||||
import ca.gosyer.jui.domain.source.model.sourcepreference.SourcePreferenceChange
|
import ca.gosyer.jui.domain.source.model.sourcepreference.SourcePreferenceChange
|
||||||
|
import de.jensklingenberg.ktorfit.http.Body
|
||||||
|
import de.jensklingenberg.ktorfit.http.GET
|
||||||
|
import de.jensklingenberg.ktorfit.http.POST
|
||||||
|
import de.jensklingenberg.ktorfit.http.Path
|
||||||
|
import de.jensklingenberg.ktorfit.http.Query
|
||||||
import io.ktor.client.statement.HttpResponse
|
import io.ktor.client.statement.HttpResponse
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
interface SourceRepository {
|
interface SourceRepository {
|
||||||
|
@GET("api/v1/source/list")
|
||||||
fun getSourceList(): Flow<List<Source>>
|
fun getSourceList(): Flow<List<Source>>
|
||||||
fun getSourceInfo(sourceId: Long): Flow<Source>
|
|
||||||
fun getPopularManga(sourceId: Long, pageNum: Int): Flow<MangaPage>
|
@GET("api/v1/source/{sourceId}")
|
||||||
fun getLatestManga(sourceId: Long, pageNum: Int): Flow<MangaPage>
|
fun getSourceInfo(
|
||||||
fun getSearchResults(sourceId: Long, searchTerm: String, pageNum: Int): Flow<MangaPage>
|
@Path("sourceId") sourceId: Long
|
||||||
fun getFilterList(sourceId: Long, reset: Boolean = false): Flow<List<SourceFilter>>
|
): Flow<Source>
|
||||||
fun setFilter(sourceId: Long, sourceFilter: SourceFilterChange): Flow<HttpResponse>
|
|
||||||
fun setFilter(sourceId: Long, position: Int, value: Any): Flow<HttpResponse>
|
@GET("api/v1/source/{sourceId}/popular/{pageNum}")
|
||||||
fun setFilter(sourceId: Long, parentPosition: Int, childPosition: Int, value: Any): Flow<HttpResponse>
|
fun getPopularManga(
|
||||||
fun getSourceSettings(sourceId: Long): Flow<List<SourcePreference>>
|
@Path("sourceId") sourceId: Long,
|
||||||
fun setSourceSetting(sourceId: Long, sourcePreference: SourcePreferenceChange): Flow<HttpResponse>
|
@Path("pageNum") pageNum: Int
|
||||||
fun setSourceSetting(sourceId: Long, position: Int, value: Any): Flow<HttpResponse>
|
): Flow<MangaPage>
|
||||||
|
|
||||||
|
@GET("api/v1/source/{sourceId}/latest/{pageNum}")
|
||||||
|
fun getLatestManga(
|
||||||
|
@Path("sourceId") sourceId: Long,
|
||||||
|
@Path("pageNum") pageNum: Int
|
||||||
|
): Flow<MangaPage>
|
||||||
|
|
||||||
|
@GET("api/v1/source/{sourceId}/search")
|
||||||
|
fun getSearchResults(
|
||||||
|
@Path("sourceId") sourceId: Long,
|
||||||
|
@Query("searchTerm") searchTerm: String?,
|
||||||
|
@Query("pageNum") pageNum: Int
|
||||||
|
): Flow<MangaPage>
|
||||||
|
|
||||||
|
@GET("api/v1/source/{sourceId}/filters")
|
||||||
|
fun getFilterList(
|
||||||
|
@Path("sourceId") sourceId: Long,
|
||||||
|
@Query("reset") reset: Boolean = false
|
||||||
|
): Flow<List<SourceFilter>>
|
||||||
|
|
||||||
|
@POST("api/v1/source/{sourceId}/filters")
|
||||||
|
fun setFilter(
|
||||||
|
@Path("sourceId") sourceId: Long,
|
||||||
|
@Body sourceFilter: SourceFilterChange
|
||||||
|
): Flow<HttpResponse>
|
||||||
|
|
||||||
|
@GET("api/v1/source/{sourceId}/preferences")
|
||||||
|
fun getSourceSettings(
|
||||||
|
@Path("sourceId") sourceId: Long
|
||||||
|
): Flow<List<SourcePreference>>
|
||||||
|
|
||||||
|
@POST("api/v1/source/{sourceId}/preferences")
|
||||||
|
fun setSourceSetting(
|
||||||
|
@Path("sourceId") sourceId: Long,
|
||||||
|
@Body sourcePreference: SourcePreferenceChange
|
||||||
|
): Flow<HttpResponse>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,11 +7,26 @@
|
|||||||
package ca.gosyer.jui.domain.updates.service
|
package ca.gosyer.jui.domain.updates.service
|
||||||
|
|
||||||
import ca.gosyer.jui.domain.updates.model.Updates
|
import ca.gosyer.jui.domain.updates.model.Updates
|
||||||
|
import de.jensklingenberg.ktorfit.http.Field
|
||||||
|
import de.jensklingenberg.ktorfit.http.FormUrlEncoded
|
||||||
|
import de.jensklingenberg.ktorfit.http.GET
|
||||||
|
import de.jensklingenberg.ktorfit.http.POST
|
||||||
|
import de.jensklingenberg.ktorfit.http.Path
|
||||||
import io.ktor.client.statement.HttpResponse
|
import io.ktor.client.statement.HttpResponse
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
interface UpdatesRepository {
|
interface UpdatesRepository {
|
||||||
fun getRecentUpdates(pageNum: Int): Flow<Updates>
|
@GET("api/v1/update/recentChapters/{pageNum}/")
|
||||||
|
fun getRecentUpdates(
|
||||||
|
@Path("pageNum") pageNum: Int
|
||||||
|
): Flow<Updates>
|
||||||
|
|
||||||
|
@POST("api/v1/update/fetch/")
|
||||||
fun updateLibrary(): Flow<HttpResponse>
|
fun updateLibrary(): Flow<HttpResponse>
|
||||||
fun updateCategory(categoryId: Long): Flow<HttpResponse>
|
|
||||||
|
@POST("api/v1/update/fetch/")
|
||||||
|
@FormUrlEncoded
|
||||||
|
fun updateCategory(
|
||||||
|
@Field("category") categoryId: Long
|
||||||
|
): Flow<HttpResponse>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ kotlinInject = "0.5.1"
|
|||||||
|
|
||||||
# Network
|
# Network
|
||||||
ktor = "2.1.1"
|
ktor = "2.1.1"
|
||||||
|
ktorfit = "1.0.0-beta14"
|
||||||
|
|
||||||
# Logging
|
# Logging
|
||||||
slf4j = "1.7.36"
|
slf4j = "1.7.36"
|
||||||
@@ -133,6 +134,8 @@ ktor-serialization-json = { module = "io.ktor:ktor-serialization-kotlinx-json",
|
|||||||
ktor-logging = { module = "io.ktor:ktor-client-logging", version.ref = "ktor" }
|
ktor-logging = { module = "io.ktor:ktor-client-logging", version.ref = "ktor" }
|
||||||
ktor-websockets = { module = "io.ktor:ktor-client-websockets", version.ref = "ktor" }
|
ktor-websockets = { module = "io.ktor:ktor-client-websockets", version.ref = "ktor" }
|
||||||
ktor-auth = { module = "io.ktor:ktor-client-auth", version.ref = "ktor" }
|
ktor-auth = { module = "io.ktor:ktor-client-auth", version.ref = "ktor" }
|
||||||
|
ktorfit-lib = { module = "de.jensklingenberg.ktorfit:ktorfit-lib", version.ref = "ktorfit" }
|
||||||
|
ktorfit-ksp = { module = "de.jensklingenberg.ktorfit:ktorfit-ksp", version.ref = "ktorfit" }
|
||||||
|
|
||||||
# Logging
|
# Logging
|
||||||
logging-slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
|
logging-slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" }
|
||||||
|
|||||||
@@ -18,7 +18,9 @@ import ca.gosyer.jui.domain.chapter.interactor.GetChapters
|
|||||||
import ca.gosyer.jui.domain.chapter.interactor.QueueChapterDownload
|
import ca.gosyer.jui.domain.chapter.interactor.QueueChapterDownload
|
||||||
import ca.gosyer.jui.domain.chapter.interactor.RefreshChapters
|
import ca.gosyer.jui.domain.chapter.interactor.RefreshChapters
|
||||||
import ca.gosyer.jui.domain.chapter.interactor.StopChapterDownload
|
import ca.gosyer.jui.domain.chapter.interactor.StopChapterDownload
|
||||||
import ca.gosyer.jui.domain.chapter.interactor.UpdateChapterFlags
|
import ca.gosyer.jui.domain.chapter.interactor.UpdateChapterBookmarked
|
||||||
|
import ca.gosyer.jui.domain.chapter.interactor.UpdateChapterMarkPreviousRead
|
||||||
|
import ca.gosyer.jui.domain.chapter.interactor.UpdateChapterRead
|
||||||
import ca.gosyer.jui.domain.chapter.model.Chapter
|
import ca.gosyer.jui.domain.chapter.model.Chapter
|
||||||
import ca.gosyer.jui.domain.download.service.DownloadService
|
import ca.gosyer.jui.domain.download.service.DownloadService
|
||||||
import ca.gosyer.jui.domain.library.interactor.AddMangaToLibrary
|
import ca.gosyer.jui.domain.library.interactor.AddMangaToLibrary
|
||||||
@@ -55,7 +57,9 @@ class MangaScreenViewModel @Inject constructor(
|
|||||||
private val refreshManga: RefreshManga,
|
private val refreshManga: RefreshManga,
|
||||||
private val getChapters: GetChapters,
|
private val getChapters: GetChapters,
|
||||||
private val refreshChapters: RefreshChapters,
|
private val refreshChapters: RefreshChapters,
|
||||||
private val updateChapterFlags: UpdateChapterFlags,
|
private val updateChapterRead: UpdateChapterRead,
|
||||||
|
private val updateChapterBookmarked: UpdateChapterBookmarked,
|
||||||
|
private val updateChapterMarkPreviousRead: UpdateChapterMarkPreviousRead,
|
||||||
private val queueChapterDownload: QueueChapterDownload,
|
private val queueChapterDownload: QueueChapterDownload,
|
||||||
private val stopChapterDownload: StopChapterDownload,
|
private val stopChapterDownload: StopChapterDownload,
|
||||||
private val deleteChapterDownload: DeleteChapterDownload,
|
private val deleteChapterDownload: DeleteChapterDownload,
|
||||||
@@ -222,7 +226,7 @@ class MangaScreenViewModel @Inject constructor(
|
|||||||
val chapter = findChapter(index) ?: return
|
val chapter = findChapter(index) ?: return
|
||||||
scope.launch {
|
scope.launch {
|
||||||
manga.value.item?.let { manga ->
|
manga.value.item?.let { manga ->
|
||||||
updateChapterFlags.await(manga, index, read = chapter.read.not())
|
updateChapterRead.await(manga, index, read = chapter.read.not())
|
||||||
refreshChaptersAsync(manga.id).await()
|
refreshChaptersAsync(manga.id).await()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -232,7 +236,7 @@ class MangaScreenViewModel @Inject constructor(
|
|||||||
val chapter = findChapter(index) ?: return
|
val chapter = findChapter(index) ?: return
|
||||||
scope.launch {
|
scope.launch {
|
||||||
manga.value.item?.let { manga ->
|
manga.value.item?.let { manga ->
|
||||||
updateChapterFlags.await(manga, index, bookmarked = chapter.bookmarked.not())
|
updateChapterBookmarked.await(manga, index, bookmarked = chapter.bookmarked.not())
|
||||||
refreshChaptersAsync(manga.id).await()
|
refreshChaptersAsync(manga.id).await()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -241,7 +245,7 @@ class MangaScreenViewModel @Inject constructor(
|
|||||||
fun markPreviousRead(index: Int) {
|
fun markPreviousRead(index: Int) {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
manga.value.item?.let { manga ->
|
manga.value.item?.let { manga ->
|
||||||
updateChapterFlags.await(manga, index, markPreviousRead = true)
|
updateChapterMarkPreviousRead.await(manga, index)
|
||||||
refreshChaptersAsync(manga.id).await()
|
refreshChaptersAsync(manga.id).await()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,9 @@ import ca.gosyer.jui.core.prefs.getAsFlow
|
|||||||
import ca.gosyer.jui.domain.chapter.interactor.GetChapter
|
import ca.gosyer.jui.domain.chapter.interactor.GetChapter
|
||||||
import ca.gosyer.jui.domain.chapter.interactor.GetChapterPage
|
import ca.gosyer.jui.domain.chapter.interactor.GetChapterPage
|
||||||
import ca.gosyer.jui.domain.chapter.interactor.GetChapters
|
import ca.gosyer.jui.domain.chapter.interactor.GetChapters
|
||||||
import ca.gosyer.jui.domain.chapter.interactor.UpdateChapterFlags
|
import ca.gosyer.jui.domain.chapter.interactor.UpdateChapterLastPageRead
|
||||||
import ca.gosyer.jui.domain.chapter.interactor.UpdateChapterMeta
|
import ca.gosyer.jui.domain.chapter.interactor.UpdateChapterMeta
|
||||||
|
import ca.gosyer.jui.domain.chapter.interactor.UpdateChapterRead
|
||||||
import ca.gosyer.jui.domain.chapter.model.Chapter
|
import ca.gosyer.jui.domain.chapter.model.Chapter
|
||||||
import ca.gosyer.jui.domain.manga.interactor.GetManga
|
import ca.gosyer.jui.domain.manga.interactor.GetManga
|
||||||
import ca.gosyer.jui.domain.manga.interactor.UpdateMangaMeta
|
import ca.gosyer.jui.domain.manga.interactor.UpdateMangaMeta
|
||||||
@@ -66,7 +67,8 @@ class ReaderMenuViewModel @Inject constructor(
|
|||||||
private val getChapters: GetChapters,
|
private val getChapters: GetChapters,
|
||||||
private val getChapter: GetChapter,
|
private val getChapter: GetChapter,
|
||||||
private val getChapterPage: GetChapterPage,
|
private val getChapterPage: GetChapterPage,
|
||||||
private val updateChapterFlags: UpdateChapterFlags,
|
private val updateChapterRead: UpdateChapterRead,
|
||||||
|
private val updateChapterLastPageRead: UpdateChapterLastPageRead,
|
||||||
private val updateMangaMeta: UpdateMangaMeta,
|
private val updateMangaMeta: UpdateMangaMeta,
|
||||||
private val updateChapterMeta: UpdateChapterMeta,
|
private val updateChapterMeta: UpdateChapterMeta,
|
||||||
private val chapterCache: ChapterCache,
|
private val chapterCache: ChapterCache,
|
||||||
@@ -305,14 +307,14 @@ class ReaderMenuViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun markChapterRead(chapter: ReaderChapter) {
|
private fun markChapterRead(chapter: ReaderChapter) {
|
||||||
scope.launch { updateChapterFlags.await(chapter.chapter, read = true) }
|
scope.launch { updateChapterRead.await(chapter.chapter, read = true) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(DelicateCoroutinesApi::class)
|
@OptIn(DelicateCoroutinesApi::class)
|
||||||
fun sendProgress(chapter: Chapter? = this.chapter.value?.chapter, lastPageRead: Int = currentPage.value) {
|
fun sendProgress(chapter: Chapter? = this.chapter.value?.chapter, lastPageRead: Int = currentPage.value) {
|
||||||
chapter ?: return
|
chapter ?: return
|
||||||
if (chapter.read) return
|
if (chapter.read) return
|
||||||
GlobalScope.launch { updateChapterFlags.await(chapter, lastPageRead = lastPageRead) }
|
GlobalScope.launch { updateChapterLastPageRead.await(chapter, lastPageRead = lastPageRead) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateLastPageReadOffset(offset: Int) {
|
fun updateLastPageReadOffset(offset: Int) {
|
||||||
|
|||||||
@@ -6,13 +6,13 @@
|
|||||||
|
|
||||||
package ca.gosyer.jui.ui.sources.browse
|
package ca.gosyer.jui.ui.sources.browse
|
||||||
|
|
||||||
import ca.gosyer.jui.data.source.SourceRepositoryImpl
|
|
||||||
import ca.gosyer.jui.domain.library.model.DisplayMode
|
import ca.gosyer.jui.domain.library.model.DisplayMode
|
||||||
import ca.gosyer.jui.domain.library.service.LibraryPreferences
|
import ca.gosyer.jui.domain.library.service.LibraryPreferences
|
||||||
import ca.gosyer.jui.domain.manga.model.Manga
|
import ca.gosyer.jui.domain.manga.model.Manga
|
||||||
import ca.gosyer.jui.domain.source.model.MangaPage
|
import ca.gosyer.jui.domain.source.model.MangaPage
|
||||||
import ca.gosyer.jui.domain.source.model.Source
|
import ca.gosyer.jui.domain.source.model.Source
|
||||||
import ca.gosyer.jui.domain.source.service.CatalogPreferences
|
import ca.gosyer.jui.domain.source.service.CatalogPreferences
|
||||||
|
import ca.gosyer.jui.domain.source.service.SourceRepository
|
||||||
import ca.gosyer.jui.ui.base.model.StableHolder
|
import ca.gosyer.jui.ui.base.model.StableHolder
|
||||||
import ca.gosyer.jui.uicore.vm.ContextWrapper
|
import ca.gosyer.jui.uicore.vm.ContextWrapper
|
||||||
import ca.gosyer.jui.uicore.vm.ViewModel
|
import ca.gosyer.jui.uicore.vm.ViewModel
|
||||||
@@ -32,7 +32,7 @@ import org.lighthousegames.logging.logging
|
|||||||
|
|
||||||
class SourceScreenViewModel(
|
class SourceScreenViewModel(
|
||||||
private val source: Source,
|
private val source: Source,
|
||||||
private val sourceHandler: SourceRepositoryImpl,
|
private val sourceHandler: SourceRepository,
|
||||||
private val catalogPreferences: CatalogPreferences,
|
private val catalogPreferences: CatalogPreferences,
|
||||||
private val libraryPreferences: LibraryPreferences,
|
private val libraryPreferences: LibraryPreferences,
|
||||||
contextWrapper: ContextWrapper,
|
contextWrapper: ContextWrapper,
|
||||||
@@ -40,7 +40,7 @@ class SourceScreenViewModel(
|
|||||||
) : ViewModel(contextWrapper) {
|
) : ViewModel(contextWrapper) {
|
||||||
|
|
||||||
@Inject constructor(
|
@Inject constructor(
|
||||||
sourceHandler: SourceRepositoryImpl,
|
sourceHandler: SourceRepository,
|
||||||
catalogPreferences: CatalogPreferences,
|
catalogPreferences: CatalogPreferences,
|
||||||
libraryPreferences: LibraryPreferences,
|
libraryPreferences: LibraryPreferences,
|
||||||
contextWrapper: ContextWrapper,
|
contextWrapper: ContextWrapper,
|
||||||
@@ -128,7 +128,11 @@ class SourceScreenViewModel(
|
|||||||
private suspend fun getPage(): MangaPage? {
|
private suspend fun getPage(): MangaPage? {
|
||||||
return when {
|
return when {
|
||||||
isLatest.value -> sourceHandler.getLatestManga(source.id, pageNum.value)
|
isLatest.value -> sourceHandler.getLatestManga(source.id, pageNum.value)
|
||||||
_query.value != null || _usingFilters.value -> sourceHandler.getSearchResults(source.id, _query.value.orEmpty(), pageNum.value)
|
_query.value != null || _usingFilters.value -> sourceHandler.getSearchResults(
|
||||||
|
source.id,
|
||||||
|
_query.value?.ifBlank { null },
|
||||||
|
pageNum.value
|
||||||
|
)
|
||||||
else -> sourceHandler.getPopularManga(source.id, pageNum.value)
|
else -> sourceHandler.getPopularManga(source.id, pageNum.value)
|
||||||
}
|
}
|
||||||
.catch {
|
.catch {
|
||||||
|
|||||||
@@ -6,8 +6,9 @@
|
|||||||
|
|
||||||
package ca.gosyer.jui.ui.sources.browse.filter
|
package ca.gosyer.jui.ui.sources.browse.filter
|
||||||
|
|
||||||
import ca.gosyer.jui.data.source.SourceRepositoryImpl
|
|
||||||
import ca.gosyer.jui.domain.source.model.sourcefilters.SourceFilter
|
import ca.gosyer.jui.domain.source.model.sourcefilters.SourceFilter
|
||||||
|
import ca.gosyer.jui.domain.source.model.sourcefilters.SourceFilterChange
|
||||||
|
import ca.gosyer.jui.domain.source.service.SourceRepository
|
||||||
import ca.gosyer.jui.ui.base.model.StableHolder
|
import ca.gosyer.jui.ui.base.model.StableHolder
|
||||||
import ca.gosyer.jui.ui.sources.browse.filter.model.SourceFiltersView
|
import ca.gosyer.jui.ui.sources.browse.filter.model.SourceFiltersView
|
||||||
import ca.gosyer.jui.uicore.vm.ContextWrapper
|
import ca.gosyer.jui.uicore.vm.ContextWrapper
|
||||||
@@ -25,16 +26,18 @@ import kotlinx.coroutines.flow.launchIn
|
|||||||
import kotlinx.coroutines.flow.mapLatest
|
import kotlinx.coroutines.flow.mapLatest
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.supervisorScope
|
import kotlinx.coroutines.supervisorScope
|
||||||
|
import kotlinx.serialization.encodeToString
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
import me.tatarka.inject.annotations.Inject
|
import me.tatarka.inject.annotations.Inject
|
||||||
import org.lighthousegames.logging.logging
|
import org.lighthousegames.logging.logging
|
||||||
|
|
||||||
class SourceFiltersViewModel(
|
class SourceFiltersViewModel(
|
||||||
private val sourceId: Long,
|
private val sourceId: Long,
|
||||||
private val sourceHandler: SourceRepositoryImpl,
|
private val sourceHandler: SourceRepository,
|
||||||
contextWrapper: ContextWrapper
|
contextWrapper: ContextWrapper
|
||||||
) : ViewModel(contextWrapper) {
|
) : ViewModel(contextWrapper) {
|
||||||
@Inject constructor(
|
@Inject constructor(
|
||||||
sourceHandler: SourceRepositoryImpl,
|
sourceHandler: SourceRepository,
|
||||||
contextWrapper: ContextWrapper,
|
contextWrapper: ContextWrapper,
|
||||||
params: Params
|
params: Params
|
||||||
) : this(
|
) : this(
|
||||||
@@ -69,11 +72,11 @@ class SourceFiltersViewModel(
|
|||||||
.onEach {
|
.onEach {
|
||||||
sourceHandler.setFilter(
|
sourceHandler.setFilter(
|
||||||
sourceId,
|
sourceId,
|
||||||
filter.index,
|
SourceFilterChange(
|
||||||
childFilter.index,
|
filter.index,
|
||||||
it
|
Json.encodeToString(SourceFilterChange(childFilter.index, it))
|
||||||
)
|
)
|
||||||
.collect()
|
).collect()
|
||||||
getFilters()
|
getFilters()
|
||||||
}
|
}
|
||||||
.launchIn(this)
|
.launchIn(this)
|
||||||
@@ -81,7 +84,7 @@ class SourceFiltersViewModel(
|
|||||||
} else {
|
} else {
|
||||||
filter.state.drop(1).filterNotNull()
|
filter.state.drop(1).filterNotNull()
|
||||||
.onEach {
|
.onEach {
|
||||||
sourceHandler.setFilter(sourceId, filter.index, it)
|
sourceHandler.setFilter(sourceId, SourceFilterChange(filter.index, it))
|
||||||
.collect()
|
.collect()
|
||||||
getFilters()
|
getFilters()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,10 +7,10 @@
|
|||||||
package ca.gosyer.jui.ui.sources.globalsearch
|
package ca.gosyer.jui.ui.sources.globalsearch
|
||||||
|
|
||||||
import androidx.compose.runtime.snapshots.SnapshotStateMap
|
import androidx.compose.runtime.snapshots.SnapshotStateMap
|
||||||
import ca.gosyer.jui.data.source.SourceRepositoryImpl
|
|
||||||
import ca.gosyer.jui.domain.manga.model.Manga
|
import ca.gosyer.jui.domain.manga.model.Manga
|
||||||
import ca.gosyer.jui.domain.source.model.Source
|
import ca.gosyer.jui.domain.source.model.Source
|
||||||
import ca.gosyer.jui.domain.source.service.CatalogPreferences
|
import ca.gosyer.jui.domain.source.service.CatalogPreferences
|
||||||
|
import ca.gosyer.jui.domain.source.service.SourceRepository
|
||||||
import ca.gosyer.jui.i18n.MR
|
import ca.gosyer.jui.i18n.MR
|
||||||
import ca.gosyer.jui.ui.base.model.StableHolder
|
import ca.gosyer.jui.ui.base.model.StableHolder
|
||||||
import ca.gosyer.jui.uicore.vm.ContextWrapper
|
import ca.gosyer.jui.uicore.vm.ContextWrapper
|
||||||
@@ -40,7 +40,7 @@ import me.tatarka.inject.annotations.Inject
|
|||||||
import org.lighthousegames.logging.logging
|
import org.lighthousegames.logging.logging
|
||||||
|
|
||||||
class GlobalSearchViewModel @Inject constructor(
|
class GlobalSearchViewModel @Inject constructor(
|
||||||
private val sourceHandler: SourceRepositoryImpl,
|
private val sourceHandler: SourceRepository,
|
||||||
catalogPreferences: CatalogPreferences,
|
catalogPreferences: CatalogPreferences,
|
||||||
contextWrapper: ContextWrapper,
|
contextWrapper: ContextWrapper,
|
||||||
params: Params
|
params: Params
|
||||||
|
|||||||
@@ -9,9 +9,9 @@ package ca.gosyer.jui.ui.sources.home
|
|||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
import androidx.compose.ui.text.intl.Locale
|
import androidx.compose.ui.text.intl.Locale
|
||||||
import ca.gosyer.jui.core.lang.displayName
|
import ca.gosyer.jui.core.lang.displayName
|
||||||
import ca.gosyer.jui.data.source.SourceRepositoryImpl
|
|
||||||
import ca.gosyer.jui.domain.source.model.Source
|
import ca.gosyer.jui.domain.source.model.Source
|
||||||
import ca.gosyer.jui.domain.source.service.CatalogPreferences
|
import ca.gosyer.jui.domain.source.service.CatalogPreferences
|
||||||
|
import ca.gosyer.jui.domain.source.service.SourceRepository
|
||||||
import ca.gosyer.jui.i18n.MR
|
import ca.gosyer.jui.i18n.MR
|
||||||
import ca.gosyer.jui.uicore.vm.ContextWrapper
|
import ca.gosyer.jui.uicore.vm.ContextWrapper
|
||||||
import ca.gosyer.jui.uicore.vm.ViewModel
|
import ca.gosyer.jui.uicore.vm.ViewModel
|
||||||
@@ -32,7 +32,7 @@ import me.tatarka.inject.annotations.Inject
|
|||||||
import org.lighthousegames.logging.logging
|
import org.lighthousegames.logging.logging
|
||||||
|
|
||||||
class SourceHomeScreenViewModel @Inject constructor(
|
class SourceHomeScreenViewModel @Inject constructor(
|
||||||
private val sourceHandler: SourceRepositoryImpl,
|
private val sourceHandler: SourceRepository,
|
||||||
catalogPreferences: CatalogPreferences,
|
catalogPreferences: CatalogPreferences,
|
||||||
contextWrapper: ContextWrapper
|
contextWrapper: ContextWrapper
|
||||||
) : ViewModel(contextWrapper) {
|
) : ViewModel(contextWrapper) {
|
||||||
|
|||||||
@@ -6,8 +6,9 @@
|
|||||||
|
|
||||||
package ca.gosyer.jui.ui.sources.settings
|
package ca.gosyer.jui.ui.sources.settings
|
||||||
|
|
||||||
import ca.gosyer.jui.data.source.SourceRepositoryImpl
|
|
||||||
import ca.gosyer.jui.domain.source.model.sourcepreference.SourcePreference
|
import ca.gosyer.jui.domain.source.model.sourcepreference.SourcePreference
|
||||||
|
import ca.gosyer.jui.domain.source.model.sourcepreference.SourcePreferenceChange
|
||||||
|
import ca.gosyer.jui.domain.source.service.SourceRepository
|
||||||
import ca.gosyer.jui.ui.base.model.StableHolder
|
import ca.gosyer.jui.ui.base.model.StableHolder
|
||||||
import ca.gosyer.jui.ui.sources.settings.model.SourceSettingsView
|
import ca.gosyer.jui.ui.sources.settings.model.SourceSettingsView
|
||||||
import ca.gosyer.jui.uicore.vm.ContextWrapper
|
import ca.gosyer.jui.uicore.vm.ContextWrapper
|
||||||
@@ -29,7 +30,7 @@ import me.tatarka.inject.annotations.Inject
|
|||||||
import org.lighthousegames.logging.logging
|
import org.lighthousegames.logging.logging
|
||||||
|
|
||||||
class SourceSettingsScreenViewModel @Inject constructor(
|
class SourceSettingsScreenViewModel @Inject constructor(
|
||||||
private val sourceHandler: SourceRepositoryImpl,
|
private val sourceHandler: SourceRepository,
|
||||||
contextWrapper: ContextWrapper,
|
contextWrapper: ContextWrapper,
|
||||||
private val params: Params
|
private val params: Params
|
||||||
) : ViewModel(contextWrapper) {
|
) : ViewModel(contextWrapper) {
|
||||||
@@ -47,7 +48,7 @@ class SourceSettingsScreenViewModel @Inject constructor(
|
|||||||
setting.state.drop(1)
|
setting.state.drop(1)
|
||||||
.filterNotNull()
|
.filterNotNull()
|
||||||
.onEach {
|
.onEach {
|
||||||
sourceHandler.setSourceSetting(params.sourceId, setting.index, it)
|
sourceHandler.setSourceSetting(params.sourceId, SourcePreferenceChange(setting.index, it))
|
||||||
.catch {
|
.catch {
|
||||||
log.warn(it) { "Error setting source setting" }
|
log.warn(it) { "Error setting source setting" }
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user