diff --git a/src/main/kotlin/ca/gosyer/data/server/interactions/BackupInteractionHandler.kt b/src/main/kotlin/ca/gosyer/data/server/interactions/BackupInteractionHandler.kt index 085ba609..3f3f46ed 100644 --- a/src/main/kotlin/ca/gosyer/data/server/interactions/BackupInteractionHandler.kt +++ b/src/main/kotlin/ca/gosyer/data/server/interactions/BackupInteractionHandler.kt @@ -17,6 +17,8 @@ import ca.gosyer.util.lang.withIOContext 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.client.request.post import io.ktor.client.statement.HttpResponse import io.ktor.http.ContentType import io.ktor.http.Headers @@ -47,7 +49,7 @@ class BackupInteractionHandler @Inject constructor( } suspend fun importBackup(backup: Backup, block: HttpRequestBuilder.() -> Unit = {}) = withIOContext { - client.postRepeat( + client.post( serverUrl + backupImportRequest() ) { contentType(ContentType.Application.Json) @@ -57,14 +59,14 @@ class BackupInteractionHandler @Inject constructor( } suspend fun exportBackupFile(block: HttpRequestBuilder.() -> Unit = {}) = withIOContext { - client.getRepeat( + client.get( serverUrl + backupFileExportRequest(), block ) } suspend fun exportBackup(block: HttpRequestBuilder.() -> Unit = {}) = withIOContext { - client.getRepeat( + client.get( serverUrl + backupExportRequest(), block ) diff --git a/src/main/kotlin/ca/gosyer/data/server/interactions/BaseInteractionHandler.kt b/src/main/kotlin/ca/gosyer/data/server/interactions/BaseInteractionHandler.kt index 4f928b15..85e369be 100644 --- a/src/main/kotlin/ca/gosyer/data/server/interactions/BaseInteractionHandler.kt +++ b/src/main/kotlin/ca/gosyer/data/server/interactions/BaseInteractionHandler.kt @@ -6,17 +6,8 @@ package ca.gosyer.data.server.interactions -import androidx.compose.ui.graphics.ImageBitmap import ca.gosyer.data.server.Http import ca.gosyer.data.server.ServerPreferences -import ca.gosyer.util.lang.throwIfCancellation -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.client.request.patch -import io.ktor.client.request.post -import io.ktor.http.Parameters open class BaseInteractionHandler( protected val client: Http, @@ -24,72 +15,4 @@ open class BaseInteractionHandler( ) { private val _serverUrl = serverPreferences.serverUrl() val serverUrl get() = _serverUrl.get() - - protected inline fun repeat(block: () -> T): T { - var attempt = 1 - var lastException: Exception - do { - try { - return block() - } catch (e: Exception) { - e.throwIfCancellation() - lastException = e - } - attempt++ - } while (attempt <= 3) - throw lastException - } - - protected suspend inline fun Http.getRepeat( - urlString: String, - noinline block: HttpRequestBuilder.() -> Unit = {} - ): T { - return repeat { - get(urlString, block) - } - } - - protected suspend inline fun Http.deleteRepeat( - urlString: String, - noinline block: HttpRequestBuilder.() -> Unit = {} - ): T { - return repeat { - delete(urlString, block) - } - } - - protected suspend inline fun Http.patchRepeat( - urlString: String, - noinline block: HttpRequestBuilder.() -> Unit = {} - ): T { - return repeat { - patch(urlString, block) - } - } - - protected suspend inline fun Http.postRepeat( - urlString: String, - noinline block: HttpRequestBuilder.() -> Unit = {} - ): T { - return repeat { - post(urlString, block) - } - } - - protected suspend inline fun Http.submitFormRepeat( - urlString: String, - formParameters: Parameters = Parameters.Empty, - encodeInQuery: Boolean = false, - block: HttpRequestBuilder.() -> Unit = {} - ): T { - return repeat { - submitForm(urlString, formParameters, encodeInQuery, block) - } - } - - suspend fun imageFromUrl(client: Http, imageUrl: String, block: HttpRequestBuilder.() -> Unit): ImageBitmap { - return repeat { - ca.gosyer.util.compose.imageFromUrl(client, imageUrl, block) - } - } } diff --git a/src/main/kotlin/ca/gosyer/data/server/interactions/CategoryInteractionHandler.kt b/src/main/kotlin/ca/gosyer/data/server/interactions/CategoryInteractionHandler.kt index 915ce389..a75a1635 100644 --- a/src/main/kotlin/ca/gosyer/data/server/interactions/CategoryInteractionHandler.kt +++ b/src/main/kotlin/ca/gosyer/data/server/interactions/CategoryInteractionHandler.kt @@ -20,6 +20,9 @@ import ca.gosyer.data.server.requests.getMangaCategoriesQuery import ca.gosyer.data.server.requests.getMangaInCategoryQuery import ca.gosyer.data.server.requests.removeMangaFromCategoryRequest import ca.gosyer.util.lang.withIOContext +import io.ktor.client.request.delete +import io.ktor.client.request.forms.submitForm +import io.ktor.client.request.get import io.ktor.client.statement.HttpResponse import io.ktor.http.HttpMethod import io.ktor.http.Parameters @@ -31,7 +34,7 @@ class CategoryInteractionHandler @Inject constructor( ) : BaseInteractionHandler(client, serverPreferences) { suspend fun getMangaCategories(mangaId: Long) = withIOContext { - client.getRepeat>( + client.get>( serverUrl + getMangaCategoriesQuery(mangaId) ) } @@ -39,7 +42,7 @@ class CategoryInteractionHandler @Inject constructor( suspend fun getMangaCategories(manga: Manga) = getMangaCategories(manga.id) suspend fun addMangaToCategory(mangaId: Long, categoryId: Long) = withIOContext { - client.getRepeat( + client.get( serverUrl + addMangaToCategoryQuery(mangaId, categoryId) ) } @@ -48,7 +51,7 @@ class CategoryInteractionHandler @Inject constructor( suspend fun addMangaToCategory(mangaId: Long, category: Category) = addMangaToCategory(mangaId, category.id) suspend fun removeMangaFromCategory(mangaId: Long, categoryId: Long) = withIOContext { - client.deleteRepeat( + client.delete( serverUrl + removeMangaFromCategoryRequest(mangaId, categoryId) ) } @@ -57,13 +60,13 @@ class CategoryInteractionHandler @Inject constructor( suspend fun removeMangaFromCategory(mangaId: Long, category: Category) = removeMangaFromCategory(mangaId, category.id) suspend fun getCategories() = withIOContext { - client.getRepeat>( + client.get>( serverUrl + getCategoriesQuery() ) } suspend fun createCategory(name: String) = withIOContext { - client.submitFormRepeat( + client.submitForm( serverUrl + createCategoryRequest(), formParameters = Parameters.build { append("name", name) @@ -72,7 +75,7 @@ class CategoryInteractionHandler @Inject constructor( } suspend fun modifyCategory(categoryId: Long, name: String? = null, isLanding: Boolean? = null) = withIOContext { - client.submitFormRepeat( + client.submitForm( serverUrl + categoryModifyRequest(categoryId), formParameters = Parameters.build { if (name != null) { @@ -89,7 +92,7 @@ class CategoryInteractionHandler @Inject constructor( suspend fun modifyCategory(category: Category, name: String? = null, isLanding: Boolean? = null) = modifyCategory(category.id, name, isLanding) suspend fun reorderCategory(categoryId: Long, to: Int, from: Int) = withIOContext { - client.submitFormRepeat( + client.submitForm( serverUrl + categoryReorderRequest(categoryId), formParameters = Parameters.build { append("to", to.toString()) @@ -102,14 +105,14 @@ class CategoryInteractionHandler @Inject constructor( suspend fun reorderCategory(category: Category, to: Int, from: Int) = reorderCategory(category.id, to, from) suspend fun deleteCategory(categoryId: Long) = withIOContext { - client.deleteRepeat( + client.delete( serverUrl + categoryDeleteRequest(categoryId) ) } suspend fun deleteCategory(category: Category) = deleteCategory(category.id) suspend fun getMangaFromCategory(categoryId: Long) = withIOContext { - client.getRepeat>( + client.get>( serverUrl + getMangaInCategoryQuery(categoryId) ) } diff --git a/src/main/kotlin/ca/gosyer/data/server/interactions/ChapterInteractionHandler.kt b/src/main/kotlin/ca/gosyer/data/server/interactions/ChapterInteractionHandler.kt index 05e9e3e3..252f6359 100644 --- a/src/main/kotlin/ca/gosyer/data/server/interactions/ChapterInteractionHandler.kt +++ b/src/main/kotlin/ca/gosyer/data/server/interactions/ChapterInteractionHandler.kt @@ -17,8 +17,12 @@ import ca.gosyer.data.server.requests.getPageQuery import ca.gosyer.data.server.requests.queueDownloadChapterRequest import ca.gosyer.data.server.requests.updateChapterMetaRequest import ca.gosyer.data.server.requests.updateChapterRequest +import ca.gosyer.util.compose.imageFromUrl import ca.gosyer.util.lang.withIOContext 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.client.request.parameter import io.ktor.client.statement.HttpResponse import io.ktor.http.HttpMethod @@ -31,7 +35,7 @@ class ChapterInteractionHandler @Inject constructor( ) : BaseInteractionHandler(client, serverPreferences) { suspend fun getChapters(mangaId: Long, refresh: Boolean = false) = withIOContext { - client.getRepeat>( + client.get>( serverUrl + getMangaChaptersQuery(mangaId) ) { url { @@ -45,7 +49,7 @@ class ChapterInteractionHandler @Inject constructor( suspend fun getChapters(manga: Manga, refresh: Boolean = false) = getChapters(manga.id, refresh) suspend fun getChapter(mangaId: Long, chapterIndex: Int) = withIOContext { - client.getRepeat( + client.get( serverUrl + getChapterQuery(mangaId, chapterIndex) ) } @@ -64,7 +68,7 @@ class ChapterInteractionHandler @Inject constructor( lastPageRead: Int? = null, markPreviousRead: Boolean? = null ) = withIOContext { - client.submitFormRepeat( + client.submitForm( serverUrl + updateChapterRequest(mangaId, chapterIndex), formParameters = Parameters.build { if (read != null) { @@ -132,7 +136,7 @@ class ChapterInteractionHandler @Inject constructor( suspend fun getPage(manga: Manga, chapter: Chapter, pageNum: Int, block: HttpRequestBuilder.() -> Unit) = getPage(manga.id, chapter.index, pageNum, block) suspend fun queueChapterDownload(mangaId: Long, chapterIndex: Int) = withIOContext { - client.getRepeat( + client.get( serverUrl + queueDownloadChapterRequest(mangaId, chapterIndex) ) } @@ -144,7 +148,7 @@ class ChapterInteractionHandler @Inject constructor( suspend fun queueChapterDownload(manga: Manga, chapter: Chapter) = queueChapterDownload(manga.id, chapter.index) suspend fun deleteChapterDownload(mangaId: Long, chapterIndex: Int) = withIOContext { - client.deleteRepeat( + client.delete( serverUrl + deleteDownloadChapterRequest(mangaId, chapterIndex) ) } @@ -156,7 +160,7 @@ class ChapterInteractionHandler @Inject constructor( suspend fun deleteChapterDownload(manga: Manga, chapter: Chapter) = deleteChapterDownload(manga.id, chapter.index) suspend fun updateChapterMeta(mangaId: Long, chapterIndex: Int, key: String, value: String) = withIOContext { - client.submitFormRepeat( + client.submitForm( serverUrl + updateChapterMetaRequest(mangaId, chapterIndex), formParameters = Parameters.build { append("key", key) diff --git a/src/main/kotlin/ca/gosyer/data/server/interactions/DownloadInteractionHandler.kt b/src/main/kotlin/ca/gosyer/data/server/interactions/DownloadInteractionHandler.kt index 61067450..02c7664c 100644 --- a/src/main/kotlin/ca/gosyer/data/server/interactions/DownloadInteractionHandler.kt +++ b/src/main/kotlin/ca/gosyer/data/server/interactions/DownloadInteractionHandler.kt @@ -12,6 +12,7 @@ import ca.gosyer.data.server.requests.downloadsClearRequest import ca.gosyer.data.server.requests.downloadsStartRequest import ca.gosyer.data.server.requests.downloadsStopRequest import ca.gosyer.util.lang.withIOContext +import io.ktor.client.request.get import io.ktor.client.statement.HttpResponse import javax.inject.Inject @@ -21,19 +22,19 @@ class DownloadInteractionHandler @Inject constructor( ) : BaseInteractionHandler(client, serverPreferences) { suspend fun startDownloading() = withIOContext { - client.getRepeat( + client.get( serverUrl + downloadsStartRequest() ) } suspend fun stopDownloading() = withIOContext { - client.getRepeat( + client.get( serverUrl + downloadsStopRequest() ) } suspend fun clearDownloadQueue() = withIOContext { - client.getRepeat( + client.get( serverUrl + downloadsClearRequest() ) } diff --git a/src/main/kotlin/ca/gosyer/data/server/interactions/ExtensionInteractionHandler.kt b/src/main/kotlin/ca/gosyer/data/server/interactions/ExtensionInteractionHandler.kt index 32771eb6..56f17b12 100644 --- a/src/main/kotlin/ca/gosyer/data/server/interactions/ExtensionInteractionHandler.kt +++ b/src/main/kotlin/ca/gosyer/data/server/interactions/ExtensionInteractionHandler.kt @@ -14,8 +14,10 @@ import ca.gosyer.data.server.requests.apkInstallQuery import ca.gosyer.data.server.requests.apkUninstallQuery import ca.gosyer.data.server.requests.apkUpdateQuery import ca.gosyer.data.server.requests.extensionListQuery +import ca.gosyer.util.compose.imageFromUrl import ca.gosyer.util.lang.withIOContext import io.ktor.client.request.HttpRequestBuilder +import io.ktor.client.request.get import io.ktor.client.statement.HttpResponse import javax.inject.Inject @@ -25,25 +27,25 @@ class ExtensionInteractionHandler @Inject constructor( ) : BaseInteractionHandler(client, serverPreferences) { suspend fun getExtensionList() = withIOContext { - client.getRepeat>( + client.get>( serverUrl + extensionListQuery() ) } suspend fun installExtension(extension: Extension) = withIOContext { - client.getRepeat( + client.get( serverUrl + apkInstallQuery(extension.pkgName) ) } suspend fun updateExtension(extension: Extension) = withIOContext { - client.getRepeat( + client.get( serverUrl + apkUpdateQuery(extension.pkgName) ) } suspend fun uninstallExtension(extension: Extension) = withIOContext { - client.getRepeat( + client.get( serverUrl + apkUninstallQuery(extension.pkgName) ) } diff --git a/src/main/kotlin/ca/gosyer/data/server/interactions/LibraryInteractionHandler.kt b/src/main/kotlin/ca/gosyer/data/server/interactions/LibraryInteractionHandler.kt index 8027d0e4..96e21aab 100644 --- a/src/main/kotlin/ca/gosyer/data/server/interactions/LibraryInteractionHandler.kt +++ b/src/main/kotlin/ca/gosyer/data/server/interactions/LibraryInteractionHandler.kt @@ -13,6 +13,8 @@ import ca.gosyer.data.server.requests.addMangaToLibraryQuery import ca.gosyer.data.server.requests.getLibraryQuery import ca.gosyer.data.server.requests.removeMangaFromLibraryRequest import ca.gosyer.util.lang.withIOContext +import io.ktor.client.request.delete +import io.ktor.client.request.get import io.ktor.client.statement.HttpResponse import javax.inject.Inject @@ -22,13 +24,13 @@ class LibraryInteractionHandler @Inject constructor( ) : BaseInteractionHandler(client, serverPreferences) { suspend fun getLibraryManga() = withIOContext { - client.getRepeat>( + client.get>( serverUrl + getLibraryQuery() ) } suspend fun addMangaToLibrary(mangaId: Long) = withIOContext { - client.getRepeat( + client.get( serverUrl + addMangaToLibraryQuery(mangaId) ) } @@ -36,7 +38,7 @@ class LibraryInteractionHandler @Inject constructor( suspend fun addMangaToLibrary(manga: Manga) = addMangaToLibrary(manga.id) suspend fun removeMangaFromLibrary(mangaId: Long) = withIOContext { - client.deleteRepeat( + client.delete( serverUrl + removeMangaFromLibraryRequest(mangaId) ) } diff --git a/src/main/kotlin/ca/gosyer/data/server/interactions/MangaInteractionHandler.kt b/src/main/kotlin/ca/gosyer/data/server/interactions/MangaInteractionHandler.kt index b5b0c4c9..321c8285 100644 --- a/src/main/kotlin/ca/gosyer/data/server/interactions/MangaInteractionHandler.kt +++ b/src/main/kotlin/ca/gosyer/data/server/interactions/MangaInteractionHandler.kt @@ -12,8 +12,11 @@ import ca.gosyer.data.server.ServerPreferences import ca.gosyer.data.server.requests.mangaQuery import ca.gosyer.data.server.requests.mangaThumbnailQuery import ca.gosyer.data.server.requests.updateMangaMetaRequest +import ca.gosyer.util.compose.imageFromUrl import ca.gosyer.util.lang.withIOContext import io.ktor.client.request.HttpRequestBuilder +import io.ktor.client.request.forms.submitForm +import io.ktor.client.request.get import io.ktor.client.request.parameter import io.ktor.client.statement.HttpResponse import io.ktor.http.HttpMethod @@ -26,7 +29,7 @@ class MangaInteractionHandler @Inject constructor( ) : BaseInteractionHandler(client, serverPreferences) { suspend fun getManga(mangaId: Long, refresh: Boolean = false) = withIOContext { - client.getRepeat( + client.get( serverUrl + mangaQuery(mangaId) ) { url { @@ -48,7 +51,7 @@ class MangaInteractionHandler @Inject constructor( } suspend fun updateMangaMeta(mangaId: Long, key: String, value: String) = withIOContext { - client.submitFormRepeat( + client.submitForm( serverUrl + updateMangaMetaRequest(mangaId), formParameters = Parameters.build { append("key", key) diff --git a/src/main/kotlin/ca/gosyer/data/server/interactions/SourceInteractionHandler.kt b/src/main/kotlin/ca/gosyer/data/server/interactions/SourceInteractionHandler.kt index 65de9765..cbbe1b06 100644 --- a/src/main/kotlin/ca/gosyer/data/server/interactions/SourceInteractionHandler.kt +++ b/src/main/kotlin/ca/gosyer/data/server/interactions/SourceInteractionHandler.kt @@ -18,6 +18,7 @@ import ca.gosyer.data.server.requests.sourceListQuery import ca.gosyer.data.server.requests.sourcePopularQuery import ca.gosyer.data.server.requests.sourceSearchQuery import ca.gosyer.util.lang.withIOContext +import io.ktor.client.request.get import io.ktor.client.statement.HttpResponse import javax.inject.Inject @@ -27,13 +28,13 @@ class SourceInteractionHandler @Inject constructor( ) : BaseInteractionHandler(client, serverPreferences) { suspend fun getSourceList() = withIOContext { - client.getRepeat>( + client.get>( serverUrl + sourceListQuery() ) } suspend fun getSourceInfo(sourceId: Long) = withIOContext { - client.getRepeat( + client.get( serverUrl + sourceInfoQuery(sourceId) ) } @@ -41,7 +42,7 @@ class SourceInteractionHandler @Inject constructor( suspend fun getSourceInfo(source: Source) = getSourceInfo(source.id) suspend fun getPopularManga(sourceId: Long, pageNum: Int) = withIOContext { - client.getRepeat( + client.get( serverUrl + sourcePopularQuery(sourceId, pageNum) ) } @@ -52,7 +53,7 @@ class SourceInteractionHandler @Inject constructor( ) suspend fun getLatestManga(sourceId: Long, pageNum: Int) = withIOContext { - client.getRepeat( + client.get( serverUrl + sourceLatestQuery(sourceId, pageNum) ) } @@ -64,13 +65,13 @@ class SourceInteractionHandler @Inject constructor( // TODO: 2021-03-14 suspend fun getGlobalSearchResults(searchTerm: String) = withIOContext { - client.getRepeat( + client.get( serverUrl + globalSearchQuery(searchTerm) ) } suspend fun getSearchResults(sourceId: Long, searchTerm: String, pageNum: Int) = withIOContext { - client.getRepeat( + client.get( serverUrl + sourceSearchQuery(sourceId, searchTerm, pageNum) ) } @@ -83,7 +84,7 @@ class SourceInteractionHandler @Inject constructor( // TODO: 2021-03-14 suspend fun getFilterList(sourceId: Long) = withIOContext { - client.getRepeat( + client.get( serverUrl + getFilterListQuery(sourceId) ) } diff --git a/src/main/kotlin/ca/gosyer/ui/base/components/KtorImage.kt b/src/main/kotlin/ca/gosyer/ui/base/components/KtorImage.kt index 2bddbc4a..e4760b98 100644 --- a/src/main/kotlin/ca/gosyer/ui/base/components/KtorImage.kt +++ b/src/main/kotlin/ca/gosyer/ui/base/components/KtorImage.kt @@ -11,7 +11,6 @@ import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Alignment @@ -23,14 +22,15 @@ import androidx.compose.ui.layout.ContentScale import ca.gosyer.common.di.AppScope import ca.gosyer.data.server.Http import ca.gosyer.util.compose.imageFromUrl -import ca.gosyer.util.lang.throwIfCancellation +import ca.gosyer.util.system.kLogger import io.ktor.client.features.onDownload -import io.ktor.client.request.HttpRequestBuilder import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch +val logger = kLogger {} + @OptIn(DelicateCoroutinesApi::class) @Composable fun KtorImage( @@ -42,22 +42,22 @@ fun KtorImage( contentScale: ContentScale = ContentScale.Fit, alpha: Float = DefaultAlpha, colorFilter: ColorFilter? = null, - retries: Int = 3, client: Http = remember { AppScope.getInstance() } ) { BoxWithConstraints { - val drawable: MutableState = remember { mutableStateOf(null) } - val loading: MutableState = remember { mutableStateOf(true) } - val progress: MutableState = remember { mutableStateOf(0.0F) } - val error: MutableState = remember { mutableStateOf(null) } + val drawable = remember { mutableStateOf(null) } + val loading = remember { mutableStateOf(true) } + val progress = remember { mutableStateOf(0.0F) } + val error = remember { mutableStateOf(null) } DisposableEffect(imageUrl) { val handler = CoroutineExceptionHandler { _, throwable -> + logger.error(throwable) { "Error loading image $imageUrl" } loading.value = false error.value = throwable.message } val job = GlobalScope.launch(handler) { if (drawable.value == null) { - drawable.value = getImage(client, imageUrl, retries) { + drawable.value = imageFromUrl(client, imageUrl) { onDownload { bytesSentTotal, contentLength -> progress.value = (bytesSentTotal.toFloat() / contentLength).coerceAtMost(1.0F) } @@ -88,18 +88,3 @@ fun KtorImage( } } } - -private suspend fun getImage(client: Http, imageUrl: String, retries: Int = 3, block: HttpRequestBuilder.() -> Unit): ImageBitmap { - var attempt = 1 - var lastException: Exception - do { - try { - return imageFromUrl(client, imageUrl, block) - } catch (e: Exception) { - e.throwIfCancellation() - lastException = e - } - attempt++ - } while (attempt <= retries) - throw lastException -}