diff --git a/android/build.gradle.kts b/android/build.gradle.kts index 07529d87..db617811 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -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) diff --git a/android/src/main/kotlin/ca/gosyer/jui/android/data/download/AndroidDownloadService.kt b/android/src/main/kotlin/ca/gosyer/jui/android/data/download/AndroidDownloadService.kt index 5b41cce1..be0710a9 100644 --- a/android/src/main/kotlin/ca/gosyer/jui/android/data/download/AndroidDownloadService.kt +++ b/android/src/main/kotlin/ca/gosyer/jui/android/data/download/AndroidDownloadService.kt @@ -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 diff --git a/core/src/commonMain/kotlin/ca/gosyer/jui/core/lang/String.kt b/core/src/commonMain/kotlin/ca/gosyer/jui/core/lang/String.kt index 6d88fb0e..d23b93bc 100644 --- a/core/src/commonMain/kotlin/ca/gosyer/jui/core/lang/String.kt +++ b/core/src/commonMain/kotlin/ca/gosyer/jui/core/lang/String.kt @@ -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 +} \ No newline at end of file diff --git a/data/src/commonMain/kotlin/ca/gosyer/jui/data/DataComponent.kt b/data/src/commonMain/kotlin/ca/gosyer/jui/data/DataComponent.kt index 133405e5..85637a6a 100644 --- a/data/src/commonMain/kotlin/ca/gosyer/jui/data/DataComponent.kt +++ b/data/src/commonMain/kotlin/ca/gosyer/jui/data/DataComponent.kt @@ -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() - val ChapterRepositoryImpl.bind: ChapterRepository - @Provides get() = this + @Provides + fun categoryRepository(ktorfit: Ktorfit) = ktorfit.createIt() - val DownloadRepositoryImpl.bind: DownloadRepository - @Provides get() = this + @Provides + fun chapterRepository(ktorfit: Ktorfit) = ktorfit.createIt() - val ExtensionRepositoryImpl.bind: ExtensionRepository - @Provides get() = this + @Provides + fun downloadRepository(ktorfit: Ktorfit) = ktorfit.createIt() - val LibraryRepositoryImpl.bind: LibraryRepository - @Provides get() = this + @Provides + fun extensionRepository(ktorfit: Ktorfit) = ktorfit.createIt() - val MangaRepositoryImpl.bind: MangaRepository - @Provides get() = this + @Provides + fun libraryRepository(ktorfit: Ktorfit) = ktorfit.createIt() - val SettingsRepositoryImpl.bind: SettingsRepository - @Provides get() = this + @Provides + fun mangaRepository(ktorfit: Ktorfit) = ktorfit.createIt() - val SourceRepositoryImpl.bind: SourceRepository - @Provides get() = this + @Provides + fun settingsRepository(ktorfit: Ktorfit) = ktorfit.createIt() - val UpdatesRepositoryImpl.bind: UpdatesRepository - @Provides get() = this + @Provides + fun sourceRepository(ktorfit: Ktorfit) = ktorfit.createIt() + + @Provides + fun updatesRepository(ktorfit: Ktorfit) = ktorfit.createIt() } diff --git a/data/src/commonMain/kotlin/ca/gosyer/jui/data/FlowIORequestConverter.kt b/data/src/commonMain/kotlin/ca/gosyer/jui/data/FlowIORequestConverter.kt new file mode 100644 index 00000000..0bc7f640 --- /dev/null +++ b/data/src/commonMain/kotlin/ca/gosyer/jui/data/FlowIORequestConverter.kt @@ -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 convertRequest( + typeData: TypeData, + requestFunction: suspend () -> Pair, + 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) + } +} \ No newline at end of file diff --git a/data/src/commonMain/kotlin/ca/gosyer/jui/data/backup/BackupRepositoryImpl.kt b/data/src/commonMain/kotlin/ca/gosyer/jui/data/backup/BackupRepositoryImpl.kt deleted file mode 100644 index 9d2a2af3..00000000 --- a/data/src/commonMain/kotlin/ca/gosyer/jui/data/backup/BackupRepositoryImpl.kt +++ /dev/null @@ -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() - 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) -} diff --git a/data/src/commonMain/kotlin/ca/gosyer/jui/data/base/BaseRepository.kt b/data/src/commonMain/kotlin/ca/gosyer/jui/data/base/BaseRepository.kt deleted file mode 100644 index 88f4955a..00000000 --- a/data/src/commonMain/kotlin/ca/gosyer/jui/data/base/BaseRepository.kt +++ /dev/null @@ -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()) -} diff --git a/data/src/commonMain/kotlin/ca/gosyer/jui/data/category/CategoryRepositoryImpl.kt b/data/src/commonMain/kotlin/ca/gosyer/jui/data/category/CategoryRepositoryImpl.kt deleted file mode 100644 index d5a06de6..00000000 --- a/data/src/commonMain/kotlin/ca/gosyer/jui/data/category/CategoryRepositoryImpl.kt +++ /dev/null @@ -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>() - 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>().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>() - emit(response) - }.flowOn(Dispatchers.IO) -} diff --git a/data/src/commonMain/kotlin/ca/gosyer/jui/data/chapter/ChapterRepositoryImpl.kt b/data/src/commonMain/kotlin/ca/gosyer/jui/data/chapter/ChapterRepositoryImpl.kt deleted file mode 100644 index f6bfda7a..00000000 --- a/data/src/commonMain/kotlin/ca/gosyer/jui/data/chapter/ChapterRepositoryImpl.kt +++ /dev/null @@ -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>() - 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() - 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) -} diff --git a/data/src/commonMain/kotlin/ca/gosyer/jui/data/download/DownloadRepositoryImpl.kt b/data/src/commonMain/kotlin/ca/gosyer/jui/data/download/DownloadRepositoryImpl.kt deleted file mode 100644 index 08acba3d..00000000 --- a/data/src/commonMain/kotlin/ca/gosyer/jui/data/download/DownloadRepositoryImpl.kt +++ /dev/null @@ -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) -} diff --git a/data/src/commonMain/kotlin/ca/gosyer/jui/data/extension/ExtensionRepositoryImpl.kt b/data/src/commonMain/kotlin/ca/gosyer/jui/data/extension/ExtensionRepositoryImpl.kt deleted file mode 100644 index 0ba1fa62..00000000 --- a/data/src/commonMain/kotlin/ca/gosyer/jui/data/extension/ExtensionRepositoryImpl.kt +++ /dev/null @@ -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>() - 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) -} diff --git a/data/src/commonMain/kotlin/ca/gosyer/jui/data/library/LibraryRepositoryImpl.kt b/data/src/commonMain/kotlin/ca/gosyer/jui/data/library/LibraryRepositoryImpl.kt deleted file mode 100644 index b7601909..00000000 --- a/data/src/commonMain/kotlin/ca/gosyer/jui/data/library/LibraryRepositoryImpl.kt +++ /dev/null @@ -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) -} diff --git a/data/src/commonMain/kotlin/ca/gosyer/jui/data/manga/MangaRepositoryImpl.kt b/data/src/commonMain/kotlin/ca/gosyer/jui/data/manga/MangaRepositoryImpl.kt deleted file mode 100644 index 7c31bc08..00000000 --- a/data/src/commonMain/kotlin/ca/gosyer/jui/data/manga/MangaRepositoryImpl.kt +++ /dev/null @@ -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() - 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) -} diff --git a/data/src/commonMain/kotlin/ca/gosyer/jui/data/settings/SettingsRepositoryImpl.kt b/data/src/commonMain/kotlin/ca/gosyer/jui/data/settings/SettingsRepositoryImpl.kt deleted file mode 100644 index 09d895dc..00000000 --- a/data/src/commonMain/kotlin/ca/gosyer/jui/data/settings/SettingsRepositoryImpl.kt +++ /dev/null @@ -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() - emit(response) - }.flowOn(Dispatchers.IO) - - override fun checkUpdate() = flow { - val response = client.post( - buildUrl { path(checkUpdateQuery()) } - ) { - expectSuccess = true - } - emit(response) - }.flowOn(Dispatchers.IO) -} diff --git a/data/src/commonMain/kotlin/ca/gosyer/jui/data/source/SourceRepositoryImpl.kt b/data/src/commonMain/kotlin/ca/gosyer/jui/data/source/SourceRepositoryImpl.kt deleted file mode 100644 index de62051b..00000000 --- a/data/src/commonMain/kotlin/ca/gosyer/jui/data/source/SourceRepositoryImpl.kt +++ /dev/null @@ -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>() - emit(response) - }.flowOn(Dispatchers.IO) - - override fun getSourceInfo(sourceId: Long) = flow { - val response = client.get( - buildUrl { path(sourceInfoQuery(sourceId)) } - ) { - expectSuccess = true - }.body() - 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() - 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() - 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() - 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>() - 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>() - 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) - ) -} diff --git a/data/src/commonMain/kotlin/ca/gosyer/jui/data/updates/UpdatesRepositoryImpl.kt b/data/src/commonMain/kotlin/ca/gosyer/jui/data/updates/UpdatesRepositoryImpl.kt deleted file mode 100644 index ef0e36bf..00000000 --- a/data/src/commonMain/kotlin/ca/gosyer/jui/data/updates/UpdatesRepositoryImpl.kt +++ /dev/null @@ -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() - 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) -} diff --git a/desktop/build.gradle.kts b/desktop/build.gradle.kts index 51c3444b..27f2761e 100644 --- a/desktop/build.gradle.kts +++ b/desktop/build.gradle.kts @@ -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) diff --git a/domain/build.gradle.kts b/domain/build.gradle.kts index 87ced5f3..0a86e02c 100644 --- a/domain/build.gradle.kts +++ b/domain/build.gradle.kts @@ -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 { diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/server/model/requests/RestRequests.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/Ktorfit.kt similarity index 56% rename from domain/src/commonMain/kotlin/ca/gosyer/jui/domain/server/model/requests/RestRequests.kt rename to domain/src/commonMain/kotlin/ca/gosyer/jui/domain/Ktorfit.kt index c3f98d71..f029eb49 100644 --- a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/server/model/requests/RestRequests.kt +++ b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/Ktorfit.kt @@ -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 Ktorfit.createIt(): T = create() \ No newline at end of file diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/backup/interactor/ImportBackupFile.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/backup/interactor/ImportBackupFile.kt index 316fe4bd..ec4eec7c 100644 --- a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/backup/interactor/ImportBackupFile.kt +++ b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/backup/interactor/ImportBackupFile.kt @@ -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() diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/backup/interactor/ValidateBackupFile.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/backup/interactor/ValidateBackupFile.kt index 6cf3b13d..e8117765 100644 --- a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/backup/interactor/ValidateBackupFile.kt +++ b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/backup/interactor/ValidateBackupFile.kt @@ -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() diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/backup/service/BackupRepository.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/backup/service/BackupRepository.kt index 6255e085..85e08ee5 100644 --- a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/backup/service/BackupRepository.kt +++ b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/backup/service/BackupRepository.kt @@ -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 - fun validateBackupFile(file: Path, block: HttpRequestBuilder.() -> Unit = {}): Flow - fun exportBackupFile(block: HttpRequestBuilder.() -> Unit = {}): Flow + + @Multipart + @POST("api/v1/backup/import/file") + fun importBackupFile( + @Part("") formData: List, + @ReqBuilder block: HttpRequestBuilder.() -> Unit = {} + ): Flow + + @Multipart + @POST("api/v1/backup/validate/file") + fun validateBackupFile( + @Part("") formData: List, + @ReqBuilder block: HttpRequestBuilder.() -> Unit = {} + ): Flow + + @POST("api/v1/backup/export/file") + fun exportBackupFile( + @ReqBuilder block: HttpRequestBuilder.() -> Unit = {} + ): Flow + + 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") + } + ) + } + } } diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/category/interactor/GetCategories.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/category/interactor/GetCategories.kt index e1c8e900..485ca8a8 100644 --- a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/category/interactor/GetCategories.kt +++ b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/category/interactor/GetCategories.kt @@ -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() diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/category/interactor/ModifyCategory.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/category/interactor/ModifyCategory.kt index d6cffa8a..c044d2b8 100644 --- a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/category/interactor/ModifyCategory.kt +++ b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/category/interactor/ModifyCategory.kt @@ -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 { diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/category/service/CategoryRepository.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/category/service/CategoryRepository.kt index 8a0fafc4..1791282d 100644 --- a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/category/service/CategoryRepository.kt +++ b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/category/service/CategoryRepository.kt @@ -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> - fun addMangaToCategory(mangaId: Long, categoryId: Long): Flow - fun removeMangaFromCategory(mangaId: Long, categoryId: Long): Flow - fun getCategories(dropDefault: Boolean = false): Flow> - fun createCategory(name: String): Flow - fun modifyCategory(categoryId: Long, name: String? = null, isLanding: Boolean? = null): Flow - fun reorderCategory(to: Int, from: Int): Flow - fun deleteCategory(categoryId: Long): Flow - fun getMangaFromCategory(categoryId: Long): Flow> + @GET("api/v1/manga/{mangaId}/category/") + fun getMangaCategories( + @Path("mangaId") mangaId: Long + ): Flow> + + @GET("api/v1/manga/{mangaId}/category/{categoryId}") + fun addMangaToCategory( + @Path("mangaId") mangaId: Long, + @Path("categoryId") categoryId: Long + ): Flow + + @DELETE("api/v1/manga/{mangaId}/category/{categoryId}") + fun removeMangaFromCategory( + @Path("mangaId") mangaId: Long, + @Path("categoryId") categoryId: Long + ): Flow + + @GET("api/v1/category/") + fun getCategories(): Flow> + + @FormUrlEncoded + @POST("api/v1/category/") + fun createCategory( + @Field("name") name: String + ): Flow + + @FormUrlEncoded + @PATCH("api/v1/category/{categoryId}") + fun modifyCategory( + @Path("categoryId") categoryId: Long, + @Field("name") name: String + ): Flow + + @FormUrlEncoded + @PATCH("api/v1/category/reorder") + fun reorderCategory( + @Field("to") to: Int, + @Field("from") from: Int + ): Flow + + @DELETE("api/v1/category/{categoryId}") + fun deleteCategory( + @Path("categoryId") categoryId: Long + ): Flow + + @GET("api/v1/category/{categoryId}") + fun getMangaFromCategory( + @Path("categoryId") categoryId: Long + ): Flow> } diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/UpdateChapterBookmarked.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/UpdateChapterBookmarked.kt new file mode 100644 index 00000000..1774e9ee --- /dev/null +++ b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/UpdateChapterBookmarked.kt @@ -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() + } +} diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/UpdateChapterFlags.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/UpdateChapterFlags.kt deleted file mode 100644 index 809bf954..00000000 --- a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/UpdateChapterFlags.kt +++ /dev/null @@ -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() - } -} diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/UpdateChapterLastPageRead.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/UpdateChapterLastPageRead.kt new file mode 100644 index 00000000..1c753341 --- /dev/null +++ b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/UpdateChapterLastPageRead.kt @@ -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() + } +} diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/UpdateChapterMarkPreviousRead.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/UpdateChapterMarkPreviousRead.kt new file mode 100644 index 00000000..e5d2e691 --- /dev/null +++ b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/UpdateChapterMarkPreviousRead.kt @@ -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() + } +} diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/UpdateChapterRead.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/UpdateChapterRead.kt new file mode 100644 index 00000000..db892149 --- /dev/null +++ b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/interactor/UpdateChapterRead.kt @@ -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() + } +} diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/service/ChapterRepository.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/service/ChapterRepository.kt index 098c7b1d..92a8e038 100644 --- a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/service/ChapterRepository.kt +++ b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/chapter/service/ChapterRepository.kt @@ -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> - fun getChapter(mangaId: Long, chapterIndex: Int): Flow + + @GET("api/v1/manga/{mangaId}/chapters") + fun getChapters( + @Path("mangaId") mangaId: Long, + @Query("onlineFetch") refresh: Boolean = false + ): Flow> + + @GET("api/v1/manga/{mangaId}/chapter/{chapterIndex}") + fun getChapter( + @Path("mangaId") mangaId: Long, + @Path("chapterIndex") chapterIndex: Int + ): Flow + + /* 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*/ + + //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 + @FormUrlEncoded + @PATCH("api/v1/manga/{mangaId}/chapter/{chapterIndex}") + fun updateChapterBookmarked( + @Path("mangaId") mangaId: Long, + @Path("chapterIndex") chapterIndex: Int, + @Field("bookmarked") bookmarked: Boolean, + ): Flow + + @FormUrlEncoded + @PATCH("api/v1/manga/{mangaId}/chapter/{chapterIndex}") + fun updateChapterLastPageRead( + @Path("mangaId") mangaId: Long, + @Path("chapterIndex") chapterIndex: Int, + @Field("lastPageRead") lastPageRead: Int, + ): Flow + + @FormUrlEncoded + @PATCH("api/v1/manga/{mangaId}/chapter/{chapterIndex}") + fun updateChapterMarkPrevRead( + @Path("mangaId") mangaId: Long, + @Path("chapterIndex") chapterIndex: Int, + @Field("markPrevRead") markPreviousRead: Boolean = true + ): Flow + + @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 - fun deleteChapterDownload(mangaId: Long, chapterIndex: Int): Flow - fun queueChapterDownload(mangaId: Long, chapterIndex: Int): Flow - fun stopChapterDownload(mangaId: Long, chapterIndex: Int): Flow - fun updateChapterMeta(mangaId: Long, chapterIndex: Int, key: String, value: String): Flow + @DELETE("api/v1/manga/{mangaId}/chapter/{chapterIndex}") + fun deleteChapterDownload( + @Path("mangaId") mangaId: Long, + @Path("chapterIndex") chapterIndex: Int + ): Flow + + @GET("api/v1/download/{mangaId}/chapter/{chapterIndex}") + fun queueChapterDownload( + @Path("mangaId") mangaId: Long, + @Path("chapterIndex") chapterIndex: Int + ): Flow + + @DELETE("api/v1/download/{mangaId}/chapter/{chapterIndex}") + fun stopChapterDownload( + @Path("mangaId") mangaId: Long, + @Path("chapterIndex") chapterIndex: Int + ): Flow + + @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 } diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/download/service/DownloadRepository.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/download/service/DownloadRepository.kt index 03755eb7..984639f6 100644 --- a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/download/service/DownloadRepository.kt +++ b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/download/service/DownloadRepository.kt @@ -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 + + @GET("api/v1/downloads/stop") fun stopDownloading(): Flow + + @GET("api/v1/downloads/clear") fun clearDownloadQueue(): Flow } diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/download/service/DownloadService.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/download/service/DownloadService.kt index d8b58958..e41a63ee 100644 --- a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/download/service/DownloadService.kt +++ b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/download/service/DownloadService.kt @@ -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(frame.readText()) diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/extension/interactor/InstallExtension.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/extension/interactor/InstallExtension.kt index 93288ae3..a50ff09b 100644 --- a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/extension/interactor/InstallExtension.kt +++ b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/extension/interactor/InstallExtension.kt @@ -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() diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/extension/interactor/UninstallExtension.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/extension/interactor/UninstallExtension.kt index bad56785..3bcf4d68 100644 --- a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/extension/interactor/UninstallExtension.kt +++ b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/extension/interactor/UninstallExtension.kt @@ -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() diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/extension/interactor/UpdateExtension.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/extension/interactor/UpdateExtension.kt index bcf525fa..61b2299f 100644 --- a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/extension/interactor/UpdateExtension.kt +++ b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/extension/interactor/UpdateExtension.kt @@ -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() diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/extension/service/ExtensionRepository.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/extension/service/ExtensionRepository.kt index 723dfd29..4aec748e 100644 --- a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/extension/service/ExtensionRepository.kt +++ b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/extension/service/ExtensionRepository.kt @@ -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> - fun installExtension(extension: Extension): Flow - fun updateExtension(extension: Extension): Flow - fun uninstallExtension(extension: Extension): Flow - fun getApkIcon(extension: Extension, block: HttpRequestBuilder.() -> Unit): Flow + + @GET("api/v1/extension/install/{pkgName}") + fun installExtension( + @Path("pkgName") pkgName: String + ): Flow + + @GET("api/v1/extension/update/{pkgName}") + fun updateExtension( + @Path("pkgName") pkgName: String + ): Flow + + @GET("api/v1/extension/uninstall/{pkgName}") + fun uninstallExtension( + @Path("pkgName") pkgName: String + ): Flow + + @GET("api/v1/extension/icon/{apkName}") + fun getApkIcon( + @Path("apkName") apkName: String, + @ReqBuilder block: HttpRequestBuilder.() -> Unit + ): Flow } diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/library/service/LibraryRepository.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/library/service/LibraryRepository.kt index 3a72ec21..0eaada9a 100644 --- a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/library/service/LibraryRepository.kt +++ b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/library/service/LibraryRepository.kt @@ -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 - fun removeMangaFromLibrary(mangaId: Long): Flow + + @GET("api/v1/manga/{mangaId}/library") + fun addMangaToLibrary( + @Path("mangaId") mangaId: Long + ): Flow + + @DELETE("api/v1/manga/{mangaId}/library") + fun removeMangaFromLibrary( + @Path("mangaId") mangaId: Long + ): Flow } diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/library/service/LibraryUpdateService.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/library/service/LibraryUpdateService.kt index ef2dd8be..9641d55b 100644 --- a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/library/service/LibraryUpdateService.kt +++ b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/library/service/LibraryUpdateService.kt @@ -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 = MutableStateFlow(Status.STARTING) override val query: String - get() = updatesQuery() + get() = "/api/v1/update" override suspend fun onReceived(frame: Frame.Text) { val status = json.decodeFromString(frame.readText()) diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/manga/service/MangaRepository.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/manga/service/MangaRepository.kt index b1df47ca..e3302fad 100644 --- a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/manga/service/MangaRepository.kt +++ b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/manga/service/MangaRepository.kt @@ -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 - fun getMangaThumbnail(mangaId: Long, block: HttpRequestBuilder.() -> Unit): Flow - fun updateMangaMeta(mangaId: Long, key: String, value: String): Flow + @GET("api/v1/manga/{mangaId}/") + fun getManga( + @Path("mangaId") mangaId: Long, + @Query("onlineFetch") refresh: Boolean = false + ): Flow + + @GET("api/v1/manga/{mangaId}/thumbnail") + fun getMangaThumbnail( + @Path("mangaId") mangaId: Long, + @ReqBuilder block: HttpRequestBuilder.() -> Unit + ): Flow + + @PATCH("api/v1/manga/{mangaId}/meta") + @FormUrlEncoded + fun updateMangaMeta( + @Path("mangaId") mangaId: Long, + @Field("key") key: String, + @Field("value") value: String + ): Flow } diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/server/model/requests/Backup.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/server/model/requests/Backup.kt deleted file mode 100644 index e852d8ab..00000000 --- a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/server/model/requests/Backup.kt +++ /dev/null @@ -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" diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/server/model/requests/Category.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/server/model/requests/Category.kt deleted file mode 100644 index 409e3c72..00000000 --- a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/server/model/requests/Category.kt +++ /dev/null @@ -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" diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/server/model/requests/Chapters.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/server/model/requests/Chapters.kt deleted file mode 100644 index e02b858a..00000000 --- a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/server/model/requests/Chapters.kt +++ /dev/null @@ -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" diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/server/model/requests/Downloads.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/server/model/requests/Downloads.kt deleted file mode 100644 index 927f3425..00000000 --- a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/server/model/requests/Downloads.kt +++ /dev/null @@ -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" diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/server/model/requests/Extensions.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/server/model/requests/Extensions.kt deleted file mode 100644 index a252b3e7..00000000 --- a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/server/model/requests/Extensions.kt +++ /dev/null @@ -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" diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/server/model/requests/Library.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/server/model/requests/Library.kt deleted file mode 100644 index 2c871047..00000000 --- a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/server/model/requests/Library.kt +++ /dev/null @@ -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" diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/server/model/requests/Manga.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/server/model/requests/Manga.kt deleted file mode 100644 index 5a572384..00000000 --- a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/server/model/requests/Manga.kt +++ /dev/null @@ -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" diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/server/model/requests/Settings.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/server/model/requests/Settings.kt deleted file mode 100644 index 2b16a172..00000000 --- a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/server/model/requests/Settings.kt +++ /dev/null @@ -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" diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/server/model/requests/Sources.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/server/model/requests/Sources.kt deleted file mode 100644 index 11b13f1f..00000000 --- a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/server/model/requests/Sources.kt +++ /dev/null @@ -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" diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/server/model/requests/Updates.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/server/model/requests/Updates.kt deleted file mode 100644 index d6360b26..00000000 --- a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/server/model/requests/Updates.kt +++ /dev/null @@ -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" diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/settings/service/SettingsRepository.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/settings/service/SettingsRepository.kt index 29e46f1c..c9a2b17a 100644 --- a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/settings/service/SettingsRepository.kt +++ b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/settings/service/SettingsRepository.kt @@ -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 + + @POST("api/v1/settings/check-update") fun checkUpdate(): Flow } diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/source/service/SourceRepository.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/source/service/SourceRepository.kt index 371e3b2d..2f375713 100644 --- a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/source/service/SourceRepository.kt +++ b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/source/service/SourceRepository.kt @@ -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> - fun getSourceInfo(sourceId: Long): Flow - fun getPopularManga(sourceId: Long, pageNum: Int): Flow - fun getLatestManga(sourceId: Long, pageNum: Int): Flow - fun getSearchResults(sourceId: Long, searchTerm: String, pageNum: Int): Flow - fun getFilterList(sourceId: Long, reset: Boolean = false): Flow> - fun setFilter(sourceId: Long, sourceFilter: SourceFilterChange): Flow - fun setFilter(sourceId: Long, position: Int, value: Any): Flow - fun setFilter(sourceId: Long, parentPosition: Int, childPosition: Int, value: Any): Flow - fun getSourceSettings(sourceId: Long): Flow> - fun setSourceSetting(sourceId: Long, sourcePreference: SourcePreferenceChange): Flow - fun setSourceSetting(sourceId: Long, position: Int, value: Any): Flow + + @GET("api/v1/source/{sourceId}") + fun getSourceInfo( + @Path("sourceId") sourceId: Long + ): Flow + + @GET("api/v1/source/{sourceId}/popular/{pageNum}") + fun getPopularManga( + @Path("sourceId") sourceId: Long, + @Path("pageNum") pageNum: Int + ): Flow + + @GET("api/v1/source/{sourceId}/latest/{pageNum}") + fun getLatestManga( + @Path("sourceId") sourceId: Long, + @Path("pageNum") pageNum: Int + ): Flow + + @GET("api/v1/source/{sourceId}/search") + fun getSearchResults( + @Path("sourceId") sourceId: Long, + @Query("searchTerm") searchTerm: String?, + @Query("pageNum") pageNum: Int + ): Flow + + @GET("api/v1/source/{sourceId}/filters") + fun getFilterList( + @Path("sourceId") sourceId: Long, + @Query("reset") reset: Boolean = false + ): Flow> + + @POST("api/v1/source/{sourceId}/filters") + fun setFilter( + @Path("sourceId") sourceId: Long, + @Body sourceFilter: SourceFilterChange + ): Flow + + @GET("api/v1/source/{sourceId}/preferences") + fun getSourceSettings( + @Path("sourceId") sourceId: Long + ): Flow> + + @POST("api/v1/source/{sourceId}/preferences") + fun setSourceSetting( + @Path("sourceId") sourceId: Long, + @Body sourcePreference: SourcePreferenceChange + ): Flow } diff --git a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/updates/service/UpdatesRepository.kt b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/updates/service/UpdatesRepository.kt index 24e27023..25a8d74c 100644 --- a/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/updates/service/UpdatesRepository.kt +++ b/domain/src/commonMain/kotlin/ca/gosyer/jui/domain/updates/service/UpdatesRepository.kt @@ -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 + @GET("api/v1/update/recentChapters/{pageNum}/") + fun getRecentUpdates( + @Path("pageNum") pageNum: Int + ): Flow + + @POST("api/v1/update/fetch/") fun updateLibrary(): Flow - fun updateCategory(categoryId: Long): Flow + + @POST("api/v1/update/fetch/") + @FormUrlEncoded + fun updateCategory( + @Field("category") categoryId: Long + ): Flow } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b39a4b82..bd6a6809 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -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" } diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/manga/MangaScreenViewModel.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/manga/MangaScreenViewModel.kt index cfb20f37..75db4d68 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/manga/MangaScreenViewModel.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/manga/MangaScreenViewModel.kt @@ -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() } } diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/ReaderMenuViewModel.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/ReaderMenuViewModel.kt index 23c2902d..efe66c82 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/ReaderMenuViewModel.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/reader/ReaderMenuViewModel.kt @@ -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) { diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/sources/browse/SourceScreenViewModel.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/sources/browse/SourceScreenViewModel.kt index 5f9e6238..588295bd 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/sources/browse/SourceScreenViewModel.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/sources/browse/SourceScreenViewModel.kt @@ -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 { diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/sources/browse/filter/SourceFiltersViewModel.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/sources/browse/filter/SourceFiltersViewModel.kt index a9d07d42..f8cb1c92 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/sources/browse/filter/SourceFiltersViewModel.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/sources/browse/filter/SourceFiltersViewModel.kt @@ -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() } diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/sources/globalsearch/GlobalSearchViewModel.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/sources/globalsearch/GlobalSearchViewModel.kt index bae22df8..5776e18d 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/sources/globalsearch/GlobalSearchViewModel.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/sources/globalsearch/GlobalSearchViewModel.kt @@ -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 diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/sources/home/SourceHomeScreenViewModel.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/sources/home/SourceHomeScreenViewModel.kt index 3e79e42d..0a8afa62 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/sources/home/SourceHomeScreenViewModel.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/sources/home/SourceHomeScreenViewModel.kt @@ -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) { diff --git a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/sources/settings/SourceSettingsScreenViewModel.kt b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/sources/settings/SourceSettingsScreenViewModel.kt index 83f4da85..67eae1e3 100644 --- a/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/sources/settings/SourceSettingsScreenViewModel.kt +++ b/presentation/src/commonMain/kotlin/ca/gosyer/jui/ui/sources/settings/SourceSettingsScreenViewModel.kt @@ -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" } }