mirror of
https://github.com/Suwayomi/TachideskJUI.git
synced 2025-12-10 06:42:05 +01:00
Integrate Ktorfit
This commit is contained in:
@@ -62,6 +62,10 @@ dependencies {
|
||||
implementation(libs.ktor.websockets)
|
||||
implementation(libs.ktor.auth)
|
||||
|
||||
// Ktorfit
|
||||
implementation(libs.ktorfit.lib)
|
||||
ksp(libs.ktorfit.ksp)
|
||||
|
||||
// Logging
|
||||
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.service.DownloadService
|
||||
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 dev.icerock.moko.resources.desc.desc
|
||||
import dev.icerock.moko.resources.format
|
||||
@@ -146,7 +145,7 @@ class AndroidDownloadService : Service() {
|
||||
client.ws(
|
||||
host = serverUrl.host,
|
||||
port = serverUrl.port,
|
||||
path = serverUrl.encodedPath + downloadsQuery()
|
||||
path = serverUrl.encodedPath + "/api/v1/downloads"
|
||||
) {
|
||||
errorConnectionCount = 0
|
||||
status.value = Status.RUNNING
|
||||
|
||||
@@ -17,3 +17,10 @@ fun String.chop(count: Int, replacement: String = "…"): String {
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun String.addSuffix(char: Char): String {
|
||||
return if (endsWith(char)) {
|
||||
this
|
||||
} else this + char
|
||||
}
|
||||
@@ -6,57 +6,60 @@
|
||||
|
||||
package ca.gosyer.jui.data
|
||||
|
||||
import ca.gosyer.jui.data.backup.BackupRepositoryImpl
|
||||
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.core.lang.addSuffix
|
||||
import ca.gosyer.jui.domain.backup.service.BackupRepository
|
||||
import ca.gosyer.jui.domain.category.service.CategoryRepository
|
||||
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.extension.service.ExtensionRepository
|
||||
import ca.gosyer.jui.domain.library.service.LibraryRepository
|
||||
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.source.service.SourceRepository
|
||||
import ca.gosyer.jui.domain.updates.service.UpdatesRepository
|
||||
import de.jensklingenberg.ktorfit.Ktorfit
|
||||
import me.tatarka.inject.annotations.Provides
|
||||
|
||||
interface DataComponent {
|
||||
|
||||
val BackupRepositoryImpl.bind: BackupRepository
|
||||
@Provides get() = this
|
||||
@Provides
|
||||
fun ktorfit(http: Http, serverPreferences: ServerPreferences) = Ktorfit
|
||||
.Builder()
|
||||
.httpClient(http)
|
||||
.requestConverter(FlowIORequestConverter())
|
||||
.baseUrl(serverPreferences.serverUrl().get().toString().addSuffix('/'))
|
||||
.build()
|
||||
|
||||
val CategoryRepositoryImpl.bind: CategoryRepository
|
||||
@Provides get() = this
|
||||
@Provides
|
||||
fun backupRepository(ktorfit: Ktorfit) = ktorfit.createIt<BackupRepository>()
|
||||
|
||||
val ChapterRepositoryImpl.bind: ChapterRepository
|
||||
@Provides get() = this
|
||||
@Provides
|
||||
fun categoryRepository(ktorfit: Ktorfit) = ktorfit.createIt<CategoryRepository>()
|
||||
|
||||
val DownloadRepositoryImpl.bind: DownloadRepository
|
||||
@Provides get() = this
|
||||
@Provides
|
||||
fun chapterRepository(ktorfit: Ktorfit) = ktorfit.createIt<ChapterRepository>()
|
||||
|
||||
val ExtensionRepositoryImpl.bind: ExtensionRepository
|
||||
@Provides get() = this
|
||||
@Provides
|
||||
fun downloadRepository(ktorfit: Ktorfit) = ktorfit.createIt<DownloadRepository>()
|
||||
|
||||
val LibraryRepositoryImpl.bind: LibraryRepository
|
||||
@Provides get() = this
|
||||
@Provides
|
||||
fun extensionRepository(ktorfit: Ktorfit) = ktorfit.createIt<ExtensionRepository>()
|
||||
|
||||
val MangaRepositoryImpl.bind: MangaRepository
|
||||
@Provides get() = this
|
||||
@Provides
|
||||
fun libraryRepository(ktorfit: Ktorfit) = ktorfit.createIt<LibraryRepository>()
|
||||
|
||||
val SettingsRepositoryImpl.bind: SettingsRepository
|
||||
@Provides get() = this
|
||||
@Provides
|
||||
fun mangaRepository(ktorfit: Ktorfit) = ktorfit.createIt<MangaRepository>()
|
||||
|
||||
val SourceRepositoryImpl.bind: SourceRepository
|
||||
@Provides get() = this
|
||||
@Provides
|
||||
fun settingsRepository(ktorfit: Ktorfit) = ktorfit.createIt<SettingsRepository>()
|
||||
|
||||
val UpdatesRepositoryImpl.bind: UpdatesRepository
|
||||
@Provides get() = this
|
||||
@Provides
|
||||
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.auth)
|
||||
|
||||
// Ktorfit
|
||||
implementation(libs.ktorfit.lib)
|
||||
ksp(libs.ktorfit.ksp)
|
||||
|
||||
// Logging
|
||||
implementation(libs.logging.slf4j.api)
|
||||
implementation(libs.logging.slf4j.jul)
|
||||
|
||||
@@ -32,6 +32,7 @@ kotlin {
|
||||
languageSettings {
|
||||
optIn("kotlin.RequiresOptIn")
|
||||
optIn("kotlinx.coroutines.ExperimentalCoroutinesApi")
|
||||
optIn("de.jensklingenberg.ktorfit.internal.InternalKtorfitApi")
|
||||
}
|
||||
}
|
||||
val commonMain by getting {
|
||||
@@ -48,6 +49,7 @@ kotlin {
|
||||
api(libs.ktor.auth)
|
||||
api(libs.ktor.logging)
|
||||
api(libs.ktor.websockets)
|
||||
api(libs.ktorfit.lib)
|
||||
api(libs.okio)
|
||||
api(libs.dateTime)
|
||||
api(projects.core)
|
||||
@@ -113,6 +115,9 @@ kotlin {
|
||||
dependencies {
|
||||
add("kspDesktop", libs.kotlinInject.compiler)
|
||||
add("kspAndroid", libs.kotlinInject.compiler)
|
||||
add("kspCommonMainMetadata", libs.ktorfit.ksp)
|
||||
add("kspDesktop", libs.ktorfit.ksp)
|
||||
add("kspAndroid", libs.ktorfit.ksp)
|
||||
}
|
||||
|
||||
buildkonfig {
|
||||
|
||||
@@ -4,14 +4,9 @@
|
||||
* 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
|
||||
|
||||
annotation class Delete
|
||||
|
||||
annotation class Patch
|
||||
|
||||
annotation class WS
|
||||
inline fun <reified T> Ktorfit.createIt(): T = create()
|
||||
@@ -21,7 +21,7 @@ class ImportBackupFile @Inject constructor(private val backupRepository: BackupR
|
||||
.singleOrNull()
|
||||
|
||||
fun asFlow(file: Path, block: HttpRequestBuilder.() -> Unit = {}) =
|
||||
backupRepository.importBackupFile(file, block)
|
||||
backupRepository.importBackupFile(BackupRepository.buildBackupFormData(file), block)
|
||||
|
||||
companion object {
|
||||
private val log = logging()
|
||||
|
||||
@@ -21,7 +21,7 @@ class ValidateBackupFile @Inject constructor(private val backupRepository: Backu
|
||||
.singleOrNull()
|
||||
|
||||
fun asFlow(file: Path, block: HttpRequestBuilder.() -> Unit = {}) =
|
||||
backupRepository.validateBackupFile(file, block)
|
||||
backupRepository.validateBackupFile(BackupRepository.buildBackupFormData(file), block)
|
||||
|
||||
companion object {
|
||||
private val log = logging()
|
||||
|
||||
@@ -6,14 +6,55 @@
|
||||
|
||||
package ca.gosyer.jui.domain.backup.service
|
||||
|
||||
import ca.gosyer.jui.core.io.SYSTEM
|
||||
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.forms.formData
|
||||
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 okio.FileSystem
|
||||
import okio.Path
|
||||
import okio.buffer
|
||||
|
||||
interface BackupRepository {
|
||||
fun importBackupFile(file: Path, block: HttpRequestBuilder.() -> Unit = {}): Flow<HttpResponse>
|
||||
fun validateBackupFile(file: Path, block: HttpRequestBuilder.() -> Unit = {}): Flow<BackupValidationResult>
|
||||
fun exportBackupFile(block: HttpRequestBuilder.() -> Unit = {}): Flow<HttpResponse>
|
||||
|
||||
@Multipart
|
||||
@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 kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.singleOrNull
|
||||
import me.tatarka.inject.annotations.Inject
|
||||
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" } }
|
||||
.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 {
|
||||
private val log = logging()
|
||||
|
||||
@@ -15,28 +15,24 @@ import org.lighthousegames.logging.logging
|
||||
|
||||
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,
|
||||
name = name,
|
||||
isLanding = isLanding
|
||||
).catch { log.warn(it) { "Failed to modify category $categoryId with options: name=$name,isLanding=$isLanding" } }.collect()
|
||||
name = name
|
||||
).catch { log.warn(it) { "Failed to modify category $categoryId with options: name=$name" } }.collect()
|
||||
|
||||
suspend fun await(category: Category, name: String? = null, isLanding: Boolean? = null) = asFlow(
|
||||
suspend fun await(category: Category, name: String? = null) = asFlow(
|
||||
category = category,
|
||||
name = name,
|
||||
isLanding = isLanding
|
||||
).catch { log.warn(it) { "Failed to modify category ${category.name} with options: name=$name,isLanding=$isLanding" } }.collect()
|
||||
name = name
|
||||
).catch { log.warn(it) { "Failed to modify category ${category.name} with options: name=$name" } }.collect()
|
||||
|
||||
fun asFlow(categoryId: Long, name: String? = null, isLanding: Boolean? = null) = categoryRepository.modifyCategory(
|
||||
fun asFlow(categoryId: Long, name: String) = categoryRepository.modifyCategory(
|
||||
categoryId = categoryId,
|
||||
name = name,
|
||||
isLanding = isLanding
|
||||
name = name
|
||||
)
|
||||
|
||||
fun asFlow(category: Category, name: String? = null, isLanding: Boolean? = null) = categoryRepository.modifyCategory(
|
||||
fun asFlow(category: Category, name: String? = null) = categoryRepository.modifyCategory(
|
||||
categoryId = category.id,
|
||||
name = name,
|
||||
isLanding = isLanding
|
||||
name = name ?: category.name
|
||||
)
|
||||
|
||||
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.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 kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface CategoryRepository {
|
||||
fun getMangaCategories(mangaId: Long): Flow<List<Category>>
|
||||
fun addMangaToCategory(mangaId: Long, categoryId: Long): Flow<HttpResponse>
|
||||
fun removeMangaFromCategory(mangaId: Long, categoryId: Long): Flow<HttpResponse>
|
||||
fun getCategories(dropDefault: Boolean = false): Flow<List<Category>>
|
||||
fun createCategory(name: String): Flow<HttpResponse>
|
||||
fun modifyCategory(categoryId: Long, name: String? = null, isLanding: Boolean? = null): Flow<HttpResponse>
|
||||
fun reorderCategory(to: Int, from: Int): Flow<HttpResponse>
|
||||
fun deleteCategory(categoryId: Long): Flow<HttpResponse>
|
||||
fun getMangaFromCategory(categoryId: Long): Flow<List<Manga>>
|
||||
@GET("api/v1/manga/{mangaId}/category/")
|
||||
fun getMangaCategories(
|
||||
@Path("mangaId") mangaId: Long
|
||||
): Flow<List<Category>>
|
||||
|
||||
@GET("api/v1/manga/{mangaId}/category/{categoryId}")
|
||||
fun addMangaToCategory(
|
||||
@Path("mangaId") mangaId: Long,
|
||||
@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
|
||||
|
||||
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.statement.HttpResponse
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
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(
|
||||
mangaId: Long,
|
||||
chapterIndex: Int,
|
||||
read: Boolean? = null,
|
||||
bookmarked: Boolean? = null,
|
||||
lastPageRead: Int? = null,
|
||||
markPreviousRead: Boolean? = null
|
||||
@Path("mangaId") mangaId: Long,
|
||||
@Path("chapterIndex") chapterIndex: Int,
|
||||
@Field("read") read: Boolean? = null,
|
||||
@Field("bookmarked") bookmarked: Boolean? = null,
|
||||
@Field("lastPageRead") lastPageRead: Int? = 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>
|
||||
|
||||
@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(
|
||||
mangaId: Long,
|
||||
chapterIndex: Int,
|
||||
pageNum: Int,
|
||||
block: HttpRequestBuilder.() -> Unit
|
||||
@Path("mangaId") mangaId: Long,
|
||||
@Path("chapterIndex") chapterIndex: Int,
|
||||
@Path("pageNum") pageNum: Int,
|
||||
@ReqBuilder block: HttpRequestBuilder.() -> Unit
|
||||
): Flow<HttpResponse>
|
||||
|
||||
fun deleteChapterDownload(mangaId: Long, chapterIndex: Int): Flow<HttpResponse>
|
||||
fun queueChapterDownload(mangaId: Long, chapterIndex: Int): Flow<HttpResponse>
|
||||
fun stopChapterDownload(mangaId: Long, chapterIndex: Int): Flow<HttpResponse>
|
||||
fun updateChapterMeta(mangaId: Long, chapterIndex: Int, key: String, value: String): Flow<HttpResponse>
|
||||
@DELETE("api/v1/manga/{mangaId}/chapter/{chapterIndex}")
|
||||
fun deleteChapterDownload(
|
||||
@Path("mangaId") mangaId: Long,
|
||||
@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
|
||||
|
||||
import de.jensklingenberg.ktorfit.http.GET
|
||||
import io.ktor.client.statement.HttpResponse
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface DownloadRepository {
|
||||
@GET("api/v1/downloads/start")
|
||||
fun startDownloading(): Flow<HttpResponse>
|
||||
|
||||
@GET("api/v1/downloads/stop")
|
||||
fun stopDownloading(): Flow<HttpResponse>
|
||||
|
||||
@GET("api/v1/downloads/clear")
|
||||
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.DownloaderStatus
|
||||
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 io.ktor.websocket.Frame
|
||||
import io.ktor.websocket.readText
|
||||
@@ -28,7 +27,7 @@ class DownloadService @Inject constructor(
|
||||
get() = status
|
||||
|
||||
override val query: String
|
||||
get() = downloadsQuery()
|
||||
get() = "/api/v1/downloads"
|
||||
|
||||
override suspend fun onReceived(frame: Frame.Text) {
|
||||
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}" } }
|
||||
.collect()
|
||||
|
||||
fun asFlow(extension: Extension) = extensionRepository.installExtension(extension)
|
||||
fun asFlow(extension: Extension) = extensionRepository.installExtension(extension.pkgName)
|
||||
|
||||
companion object {
|
||||
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}" } }
|
||||
.collect()
|
||||
|
||||
fun asFlow(extension: Extension) = extensionRepository.uninstallExtension(extension)
|
||||
fun asFlow(extension: Extension) = extensionRepository.uninstallExtension(extension.pkgName)
|
||||
|
||||
companion object {
|
||||
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}" } }
|
||||
.collect()
|
||||
|
||||
fun asFlow(extension: Extension) = extensionRepository.updateExtension(extension)
|
||||
fun asFlow(extension: Extension) = extensionRepository.updateExtension(extension.pkgName)
|
||||
|
||||
companion object {
|
||||
private val log = logging()
|
||||
|
||||
@@ -7,15 +7,36 @@
|
||||
package ca.gosyer.jui.domain.extension.service
|
||||
|
||||
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.statement.HttpResponse
|
||||
import io.ktor.utils.io.ByteReadChannel
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface ExtensionRepository {
|
||||
@GET("api/v1/extension/list")
|
||||
fun getExtensionList(): Flow<List<Extension>>
|
||||
fun installExtension(extension: Extension): Flow<HttpResponse>
|
||||
fun updateExtension(extension: Extension): Flow<HttpResponse>
|
||||
fun uninstallExtension(extension: Extension): Flow<HttpResponse>
|
||||
fun getApkIcon(extension: Extension, block: HttpRequestBuilder.() -> Unit): Flow<ByteReadChannel>
|
||||
|
||||
@GET("api/v1/extension/install/{pkgName}")
|
||||
fun installExtension(
|
||||
@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
|
||||
|
||||
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 kotlinx.coroutines.flow.Flow
|
||||
|
||||
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.library.model.UpdateStatus
|
||||
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 io.ktor.websocket.Frame
|
||||
import io.ktor.websocket.readText
|
||||
@@ -26,7 +25,7 @@ class LibraryUpdateService @Inject constructor(
|
||||
override val _status: MutableStateFlow<Status> = MutableStateFlow(Status.STARTING)
|
||||
|
||||
override val query: String
|
||||
get() = updatesQuery()
|
||||
get() = "/api/v1/update"
|
||||
|
||||
override suspend fun onReceived(frame: Frame.Text) {
|
||||
val status = json.decodeFromString<UpdateStatus>(frame.readText())
|
||||
|
||||
@@ -7,13 +7,36 @@
|
||||
package ca.gosyer.jui.domain.manga.service
|
||||
|
||||
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.statement.HttpResponse
|
||||
import io.ktor.utils.io.ByteReadChannel
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface MangaRepository {
|
||||
fun getManga(mangaId: Long, refresh: Boolean = false): Flow<Manga>
|
||||
fun getMangaThumbnail(mangaId: Long, block: HttpRequestBuilder.() -> Unit): Flow<ByteReadChannel>
|
||||
fun updateMangaMeta(mangaId: Long, key: String, value: String): Flow<HttpResponse>
|
||||
@GET("api/v1/manga/{mangaId}/")
|
||||
fun getManga(
|
||||
@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
|
||||
|
||||
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 kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface SettingsRepository {
|
||||
@GET("api/v1/settings/about")
|
||||
fun aboutServer(): Flow<About>
|
||||
|
||||
@POST("api/v1/settings/check-update")
|
||||
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.sourcepreference.SourcePreference
|
||||
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 kotlinx.coroutines.flow.Flow
|
||||
|
||||
interface SourceRepository {
|
||||
@GET("api/v1/source/list")
|
||||
fun getSourceList(): Flow<List<Source>>
|
||||
fun getSourceInfo(sourceId: Long): Flow<Source>
|
||||
fun getPopularManga(sourceId: Long, pageNum: Int): Flow<MangaPage>
|
||||
fun getLatestManga(sourceId: Long, pageNum: Int): Flow<MangaPage>
|
||||
fun getSearchResults(sourceId: Long, searchTerm: String, pageNum: Int): Flow<MangaPage>
|
||||
fun getFilterList(sourceId: Long, reset: Boolean = false): Flow<List<SourceFilter>>
|
||||
fun setFilter(sourceId: Long, sourceFilter: SourceFilterChange): Flow<HttpResponse>
|
||||
fun setFilter(sourceId: Long, position: Int, value: Any): Flow<HttpResponse>
|
||||
fun setFilter(sourceId: Long, parentPosition: Int, childPosition: Int, value: Any): Flow<HttpResponse>
|
||||
fun getSourceSettings(sourceId: Long): Flow<List<SourcePreference>>
|
||||
fun setSourceSetting(sourceId: Long, sourcePreference: SourcePreferenceChange): Flow<HttpResponse>
|
||||
fun setSourceSetting(sourceId: Long, position: Int, value: Any): Flow<HttpResponse>
|
||||
|
||||
@GET("api/v1/source/{sourceId}")
|
||||
fun getSourceInfo(
|
||||
@Path("sourceId") sourceId: Long
|
||||
): Flow<Source>
|
||||
|
||||
@GET("api/v1/source/{sourceId}/popular/{pageNum}")
|
||||
fun getPopularManga(
|
||||
@Path("sourceId") sourceId: Long,
|
||||
@Path("pageNum") pageNum: Int
|
||||
): 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
|
||||
|
||||
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 kotlinx.coroutines.flow.Flow
|
||||
|
||||
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 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
|
||||
ktor = "2.1.1"
|
||||
ktorfit = "1.0.0-beta14"
|
||||
|
||||
# Logging
|
||||
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-websockets = { module = "io.ktor:ktor-client-websockets", 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-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.RefreshChapters
|
||||
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.download.service.DownloadService
|
||||
import ca.gosyer.jui.domain.library.interactor.AddMangaToLibrary
|
||||
@@ -55,7 +57,9 @@ class MangaScreenViewModel @Inject constructor(
|
||||
private val refreshManga: RefreshManga,
|
||||
private val getChapters: GetChapters,
|
||||
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 stopChapterDownload: StopChapterDownload,
|
||||
private val deleteChapterDownload: DeleteChapterDownload,
|
||||
@@ -222,7 +226,7 @@ class MangaScreenViewModel @Inject constructor(
|
||||
val chapter = findChapter(index) ?: return
|
||||
scope.launch {
|
||||
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()
|
||||
}
|
||||
}
|
||||
@@ -232,7 +236,7 @@ class MangaScreenViewModel @Inject constructor(
|
||||
val chapter = findChapter(index) ?: return
|
||||
scope.launch {
|
||||
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()
|
||||
}
|
||||
}
|
||||
@@ -241,7 +245,7 @@ class MangaScreenViewModel @Inject constructor(
|
||||
fun markPreviousRead(index: Int) {
|
||||
scope.launch {
|
||||
manga.value.item?.let { manga ->
|
||||
updateChapterFlags.await(manga, index, markPreviousRead = true)
|
||||
updateChapterMarkPreviousRead.await(manga, index)
|
||||
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.GetChapterPage
|
||||
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.UpdateChapterRead
|
||||
import ca.gosyer.jui.domain.chapter.model.Chapter
|
||||
import ca.gosyer.jui.domain.manga.interactor.GetManga
|
||||
import ca.gosyer.jui.domain.manga.interactor.UpdateMangaMeta
|
||||
@@ -66,7 +67,8 @@ class ReaderMenuViewModel @Inject constructor(
|
||||
private val getChapters: GetChapters,
|
||||
private val getChapter: GetChapter,
|
||||
private val getChapterPage: GetChapterPage,
|
||||
private val updateChapterFlags: UpdateChapterFlags,
|
||||
private val updateChapterRead: UpdateChapterRead,
|
||||
private val updateChapterLastPageRead: UpdateChapterLastPageRead,
|
||||
private val updateMangaMeta: UpdateMangaMeta,
|
||||
private val updateChapterMeta: UpdateChapterMeta,
|
||||
private val chapterCache: ChapterCache,
|
||||
@@ -305,14 +307,14 @@ class ReaderMenuViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
private fun markChapterRead(chapter: ReaderChapter) {
|
||||
scope.launch { updateChapterFlags.await(chapter.chapter, read = true) }
|
||||
scope.launch { updateChapterRead.await(chapter.chapter, read = true) }
|
||||
}
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
fun sendProgress(chapter: Chapter? = this.chapter.value?.chapter, lastPageRead: Int = currentPage.value) {
|
||||
chapter ?: return
|
||||
if (chapter.read) return
|
||||
GlobalScope.launch { updateChapterFlags.await(chapter, lastPageRead = lastPageRead) }
|
||||
GlobalScope.launch { updateChapterLastPageRead.await(chapter, lastPageRead = lastPageRead) }
|
||||
}
|
||||
|
||||
fun updateLastPageReadOffset(offset: Int) {
|
||||
|
||||
@@ -6,13 +6,13 @@
|
||||
|
||||
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.service.LibraryPreferences
|
||||
import ca.gosyer.jui.domain.manga.model.Manga
|
||||
import ca.gosyer.jui.domain.source.model.MangaPage
|
||||
import ca.gosyer.jui.domain.source.model.Source
|
||||
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.uicore.vm.ContextWrapper
|
||||
import ca.gosyer.jui.uicore.vm.ViewModel
|
||||
@@ -32,7 +32,7 @@ import org.lighthousegames.logging.logging
|
||||
|
||||
class SourceScreenViewModel(
|
||||
private val source: Source,
|
||||
private val sourceHandler: SourceRepositoryImpl,
|
||||
private val sourceHandler: SourceRepository,
|
||||
private val catalogPreferences: CatalogPreferences,
|
||||
private val libraryPreferences: LibraryPreferences,
|
||||
contextWrapper: ContextWrapper,
|
||||
@@ -40,7 +40,7 @@ class SourceScreenViewModel(
|
||||
) : ViewModel(contextWrapper) {
|
||||
|
||||
@Inject constructor(
|
||||
sourceHandler: SourceRepositoryImpl,
|
||||
sourceHandler: SourceRepository,
|
||||
catalogPreferences: CatalogPreferences,
|
||||
libraryPreferences: LibraryPreferences,
|
||||
contextWrapper: ContextWrapper,
|
||||
@@ -128,7 +128,11 @@ class SourceScreenViewModel(
|
||||
private suspend fun getPage(): MangaPage? {
|
||||
return when {
|
||||
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)
|
||||
}
|
||||
.catch {
|
||||
|
||||
@@ -6,8 +6,9 @@
|
||||
|
||||
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.SourceFilterChange
|
||||
import ca.gosyer.jui.domain.source.service.SourceRepository
|
||||
import ca.gosyer.jui.ui.base.model.StableHolder
|
||||
import ca.gosyer.jui.ui.sources.browse.filter.model.SourceFiltersView
|
||||
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.onEach
|
||||
import kotlinx.coroutines.supervisorScope
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import me.tatarka.inject.annotations.Inject
|
||||
import org.lighthousegames.logging.logging
|
||||
|
||||
class SourceFiltersViewModel(
|
||||
private val sourceId: Long,
|
||||
private val sourceHandler: SourceRepositoryImpl,
|
||||
private val sourceHandler: SourceRepository,
|
||||
contextWrapper: ContextWrapper
|
||||
) : ViewModel(contextWrapper) {
|
||||
@Inject constructor(
|
||||
sourceHandler: SourceRepositoryImpl,
|
||||
sourceHandler: SourceRepository,
|
||||
contextWrapper: ContextWrapper,
|
||||
params: Params
|
||||
) : this(
|
||||
@@ -69,11 +72,11 @@ class SourceFiltersViewModel(
|
||||
.onEach {
|
||||
sourceHandler.setFilter(
|
||||
sourceId,
|
||||
filter.index,
|
||||
childFilter.index,
|
||||
it
|
||||
)
|
||||
.collect()
|
||||
SourceFilterChange(
|
||||
filter.index,
|
||||
Json.encodeToString(SourceFilterChange(childFilter.index, it))
|
||||
)
|
||||
).collect()
|
||||
getFilters()
|
||||
}
|
||||
.launchIn(this)
|
||||
@@ -81,7 +84,7 @@ class SourceFiltersViewModel(
|
||||
} else {
|
||||
filter.state.drop(1).filterNotNull()
|
||||
.onEach {
|
||||
sourceHandler.setFilter(sourceId, filter.index, it)
|
||||
sourceHandler.setFilter(sourceId, SourceFilterChange(filter.index, it))
|
||||
.collect()
|
||||
getFilters()
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
package ca.gosyer.jui.ui.sources.globalsearch
|
||||
|
||||
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.source.model.Source
|
||||
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.ui.base.model.StableHolder
|
||||
import ca.gosyer.jui.uicore.vm.ContextWrapper
|
||||
@@ -40,7 +40,7 @@ import me.tatarka.inject.annotations.Inject
|
||||
import org.lighthousegames.logging.logging
|
||||
|
||||
class GlobalSearchViewModel @Inject constructor(
|
||||
private val sourceHandler: SourceRepositoryImpl,
|
||||
private val sourceHandler: SourceRepository,
|
||||
catalogPreferences: CatalogPreferences,
|
||||
contextWrapper: ContextWrapper,
|
||||
params: Params
|
||||
|
||||
@@ -9,9 +9,9 @@ package ca.gosyer.jui.ui.sources.home
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.ui.text.intl.Locale
|
||||
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.service.CatalogPreferences
|
||||
import ca.gosyer.jui.domain.source.service.SourceRepository
|
||||
import ca.gosyer.jui.i18n.MR
|
||||
import ca.gosyer.jui.uicore.vm.ContextWrapper
|
||||
import ca.gosyer.jui.uicore.vm.ViewModel
|
||||
@@ -32,7 +32,7 @@ import me.tatarka.inject.annotations.Inject
|
||||
import org.lighthousegames.logging.logging
|
||||
|
||||
class SourceHomeScreenViewModel @Inject constructor(
|
||||
private val sourceHandler: SourceRepositoryImpl,
|
||||
private val sourceHandler: SourceRepository,
|
||||
catalogPreferences: CatalogPreferences,
|
||||
contextWrapper: ContextWrapper
|
||||
) : ViewModel(contextWrapper) {
|
||||
|
||||
@@ -6,8 +6,9 @@
|
||||
|
||||
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.SourcePreferenceChange
|
||||
import ca.gosyer.jui.domain.source.service.SourceRepository
|
||||
import ca.gosyer.jui.ui.base.model.StableHolder
|
||||
import ca.gosyer.jui.ui.sources.settings.model.SourceSettingsView
|
||||
import ca.gosyer.jui.uicore.vm.ContextWrapper
|
||||
@@ -29,7 +30,7 @@ import me.tatarka.inject.annotations.Inject
|
||||
import org.lighthousegames.logging.logging
|
||||
|
||||
class SourceSettingsScreenViewModel @Inject constructor(
|
||||
private val sourceHandler: SourceRepositoryImpl,
|
||||
private val sourceHandler: SourceRepository,
|
||||
contextWrapper: ContextWrapper,
|
||||
private val params: Params
|
||||
) : ViewModel(contextWrapper) {
|
||||
@@ -47,7 +48,7 @@ class SourceSettingsScreenViewModel @Inject constructor(
|
||||
setting.state.drop(1)
|
||||
.filterNotNull()
|
||||
.onEach {
|
||||
sourceHandler.setSourceSetting(params.sourceId, setting.index, it)
|
||||
sourceHandler.setSourceSetting(params.sourceId, SourcePreferenceChange(setting.index, it))
|
||||
.catch {
|
||||
log.warn(it) { "Error setting source setting" }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user