From 29629cab9a3e7282c628d5e89cafbb8af8a4e5f6 Mon Sep 17 00:00:00 2001 From: Syer10 Date: Wed, 26 May 2021 19:25:55 -0400 Subject: [PATCH] Use coroutines instead of SwingUtilities --- build.gradle.kts | 6 ++- .../ca/gosyer/common/io/OkioExtensions.kt | 7 ++- .../ca/gosyer/data/server/ServerService.kt | 5 +-- .../interactions/BackupInteractionHandler.kt | 11 +++-- .../CategoryInteractionHandler.kt | 21 +++++---- .../interactions/ChapterInteractionHandler.kt | 11 +++-- .../ExtensionInteractionHandler.kt | 13 +++--- .../interactions/LibraryInteractionHandler.kt | 9 ++-- .../interactions/MangaInteractionHandler.kt | 7 ++- .../interactions/SourceInteractionHandler.kt | 17 ++++--- .../kotlin/ca/gosyer/ui/base/WindowDialog.kt | 9 ++-- src/main/kotlin/ca/gosyer/ui/main/main.kt | 7 +-- .../ca/gosyer/ui/manga/MangaMenuViewModel.kt | 7 ++- .../kotlin/ca/gosyer/ui/reader/ReaderMenu.kt | 7 +-- .../ca/gosyer/util/compose/ContextMenu.kt | 10 +++-- .../gosyer/util/lang/CoroutineExtensions.kt | 45 +++++++++++++++++++ src/main/kotlin/ca/gosyer/util/lang/String.kt | 3 +- src/main/kotlin/ca/gosyer/util/system/File.kt | 6 ++- 18 files changed, 124 insertions(+), 77 deletions(-) create mode 100644 src/main/kotlin/ca/gosyer/util/lang/CoroutineExtensions.kt diff --git a/build.gradle.kts b/build.gradle.kts index ba7e56c4..aae3d669 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -34,7 +34,9 @@ dependencies { implementation("com.github.weisj:darklaf-core:2.5.5") // Threading - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0") + val coroutinesVersion = "1.5.0" + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-swing:$coroutinesVersion") // Json implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.1") @@ -71,7 +73,7 @@ dependencies { // Testing testImplementation(kotlin("test-junit5")) - testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.5.0") + testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutinesVersion") } tasks { diff --git a/src/main/kotlin/ca/gosyer/common/io/OkioExtensions.kt b/src/main/kotlin/ca/gosyer/common/io/OkioExtensions.kt index 065cb15c..0a23c11a 100644 --- a/src/main/kotlin/ca/gosyer/common/io/OkioExtensions.kt +++ b/src/main/kotlin/ca/gosyer/common/io/OkioExtensions.kt @@ -6,8 +6,7 @@ package ca.gosyer.common.io -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext +import ca.gosyer.util.lang.withIOContext import okio.BufferedSink import okio.Source import okio.buffer @@ -15,7 +14,7 @@ import okio.sink import java.io.File suspend fun Source.saveTo(file: File) { - withContext(Dispatchers.IO) { + withIOContext { use { source -> file.sink().buffer().use { it.writeAll(source) } } @@ -23,7 +22,7 @@ suspend fun Source.saveTo(file: File) { } suspend fun Source.copyTo(sink: BufferedSink) { - withContext(Dispatchers.IO) { + withIOContext { use { source -> sink.use { it.writeAll(source) } } diff --git a/src/main/kotlin/ca/gosyer/data/server/ServerService.kt b/src/main/kotlin/ca/gosyer/data/server/ServerService.kt index bfe6e449..59e34507 100644 --- a/src/main/kotlin/ca/gosyer/data/server/ServerService.kt +++ b/src/main/kotlin/ca/gosyer/data/server/ServerService.kt @@ -7,16 +7,15 @@ package ca.gosyer.data.server import ca.gosyer.BuildConfig +import ca.gosyer.util.lang.withIOContext import ca.gosyer.util.system.CKLogger import ca.gosyer.util.system.userDataDir import kotlinx.coroutines.DelicateCoroutinesApi -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import mu.KotlinLogging import java.io.BufferedReader import java.io.File @@ -75,7 +74,7 @@ class ServerService @Inject constructor( copyJar(jarFile) } else { try { - val jarVersion = withContext(Dispatchers.IO) { + val jarVersion = withIOContext { JarInputStream(jarFile.inputStream()).use { jar -> jar.manifest?.mainAttributes?.getValue("Specification-Version") } 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 e8d11e7b..5f0e5c5a 100644 --- a/src/main/kotlin/ca/gosyer/data/server/interactions/BackupInteractionHandler.kt +++ b/src/main/kotlin/ca/gosyer/data/server/interactions/BackupInteractionHandler.kt @@ -13,6 +13,7 @@ import ca.gosyer.data.server.requests.backupExportRequest import ca.gosyer.data.server.requests.backupFileExportRequest import ca.gosyer.data.server.requests.backupFileImportRequest import ca.gosyer.data.server.requests.backupImportRequest +import ca.gosyer.util.lang.withIOContext import io.ktor.client.request.forms.formData import io.ktor.client.request.forms.submitFormWithBinaryData import io.ktor.client.statement.HttpResponse @@ -21,8 +22,6 @@ import io.ktor.http.Headers import io.ktor.http.HttpHeaders import io.ktor.http.content.MultiPartData import io.ktor.http.contentType -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext import java.io.File import javax.inject.Inject @@ -31,7 +30,7 @@ class BackupInteractionHandler @Inject constructor( serverPreferences: ServerPreferences ) : BaseInteractionHandler(client, serverPreferences) { - suspend fun importBackupFile(file: File) = withContext(Dispatchers.IO) { + suspend fun importBackupFile(file: File) = withIOContext { client.submitFormWithBinaryData( serverUrl + backupFileImportRequest(), formData = formData { @@ -46,7 +45,7 @@ class BackupInteractionHandler @Inject constructor( ) } - suspend fun importBackup(backup: Backup) = withContext(Dispatchers.IO) { + suspend fun importBackup(backup: Backup) = withIOContext { client.postRepeat( serverUrl + backupImportRequest() ) { @@ -55,13 +54,13 @@ class BackupInteractionHandler @Inject constructor( } } - suspend fun exportBackupFile() = withContext(Dispatchers.IO) { + suspend fun exportBackupFile() = withIOContext { client.getRepeat( serverUrl + backupFileExportRequest() ) } - suspend fun exportBackup() = withContext(Dispatchers.IO) { + suspend fun exportBackup() = withIOContext { client.getRepeat( serverUrl + backupExportRequest() ) 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 46db157d..915ce389 100644 --- a/src/main/kotlin/ca/gosyer/data/server/interactions/CategoryInteractionHandler.kt +++ b/src/main/kotlin/ca/gosyer/data/server/interactions/CategoryInteractionHandler.kt @@ -19,11 +19,10 @@ import ca.gosyer.data.server.requests.getCategoriesQuery 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.statement.HttpResponse import io.ktor.http.HttpMethod import io.ktor.http.Parameters -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext import javax.inject.Inject class CategoryInteractionHandler @Inject constructor( @@ -31,7 +30,7 @@ class CategoryInteractionHandler @Inject constructor( serverPreferences: ServerPreferences ) : BaseInteractionHandler(client, serverPreferences) { - suspend fun getMangaCategories(mangaId: Long) = withContext(Dispatchers.IO) { + suspend fun getMangaCategories(mangaId: Long) = withIOContext { client.getRepeat>( serverUrl + getMangaCategoriesQuery(mangaId) ) @@ -39,7 +38,7 @@ class CategoryInteractionHandler @Inject constructor( suspend fun getMangaCategories(manga: Manga) = getMangaCategories(manga.id) - suspend fun addMangaToCategory(mangaId: Long, categoryId: Long) = withContext(Dispatchers.IO) { + suspend fun addMangaToCategory(mangaId: Long, categoryId: Long) = withIOContext { client.getRepeat( serverUrl + addMangaToCategoryQuery(mangaId, categoryId) ) @@ -48,7 +47,7 @@ class CategoryInteractionHandler @Inject constructor( suspend fun addMangaToCategory(manga: Manga, categoryId: Long) = addMangaToCategory(manga.id, categoryId) suspend fun addMangaToCategory(mangaId: Long, category: Category) = addMangaToCategory(mangaId, category.id) - suspend fun removeMangaFromCategory(mangaId: Long, categoryId: Long) = withContext(Dispatchers.IO) { + suspend fun removeMangaFromCategory(mangaId: Long, categoryId: Long) = withIOContext { client.deleteRepeat( serverUrl + removeMangaFromCategoryRequest(mangaId, categoryId) ) @@ -57,13 +56,13 @@ class CategoryInteractionHandler @Inject constructor( suspend fun removeMangaFromCategory(manga: Manga, categoryId: Long) = removeMangaFromCategory(manga.id, categoryId) suspend fun removeMangaFromCategory(mangaId: Long, category: Category) = removeMangaFromCategory(mangaId, category.id) - suspend fun getCategories() = withContext(Dispatchers.IO) { + suspend fun getCategories() = withIOContext { client.getRepeat>( serverUrl + getCategoriesQuery() ) } - suspend fun createCategory(name: String) = withContext(Dispatchers.IO) { + suspend fun createCategory(name: String) = withIOContext { client.submitFormRepeat( serverUrl + createCategoryRequest(), formParameters = Parameters.build { @@ -72,7 +71,7 @@ class CategoryInteractionHandler @Inject constructor( ) } - suspend fun modifyCategory(categoryId: Long, name: String? = null, isLanding: Boolean? = null) = withContext(Dispatchers.IO) { + suspend fun modifyCategory(categoryId: Long, name: String? = null, isLanding: Boolean? = null) = withIOContext { client.submitFormRepeat( serverUrl + categoryModifyRequest(categoryId), formParameters = Parameters.build { @@ -89,7 +88,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) = withContext(Dispatchers.IO) { + suspend fun reorderCategory(categoryId: Long, to: Int, from: Int) = withIOContext { client.submitFormRepeat( serverUrl + categoryReorderRequest(categoryId), formParameters = Parameters.build { @@ -102,14 +101,14 @@ class CategoryInteractionHandler @Inject constructor( } suspend fun reorderCategory(category: Category, to: Int, from: Int) = reorderCategory(category.id, to, from) - suspend fun deleteCategory(categoryId: Long) = withContext(Dispatchers.IO) { + suspend fun deleteCategory(categoryId: Long) = withIOContext { client.deleteRepeat( serverUrl + categoryDeleteRequest(categoryId) ) } suspend fun deleteCategory(category: Category) = deleteCategory(category.id) - suspend fun getMangaFromCategory(categoryId: Long) = withContext(Dispatchers.IO) { + suspend fun getMangaFromCategory(categoryId: Long) = withIOContext { client.getRepeat>( 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 5c712fc8..8e015c10 100644 --- a/src/main/kotlin/ca/gosyer/data/server/interactions/ChapterInteractionHandler.kt +++ b/src/main/kotlin/ca/gosyer/data/server/interactions/ChapterInteractionHandler.kt @@ -14,12 +14,11 @@ import ca.gosyer.data.server.requests.getChapterQuery import ca.gosyer.data.server.requests.getMangaChaptersQuery import ca.gosyer.data.server.requests.getPageQuery import ca.gosyer.data.server.requests.updateChapterRequest +import ca.gosyer.util.lang.withIOContext import io.ktor.client.request.parameter import io.ktor.client.statement.HttpResponse import io.ktor.http.HttpMethod import io.ktor.http.Parameters -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext import javax.inject.Inject class ChapterInteractionHandler @Inject constructor( @@ -27,7 +26,7 @@ class ChapterInteractionHandler @Inject constructor( serverPreferences: ServerPreferences ) : BaseInteractionHandler(client, serverPreferences) { - suspend fun getChapters(mangaId: Long, refresh: Boolean = false) = withContext(Dispatchers.IO) { + suspend fun getChapters(mangaId: Long, refresh: Boolean = false) = withIOContext { client.getRepeat>( serverUrl + getMangaChaptersQuery(mangaId) ) { @@ -41,7 +40,7 @@ class ChapterInteractionHandler @Inject constructor( suspend fun getChapters(manga: Manga, refresh: Boolean = false) = getChapters(manga.id, refresh) - suspend fun getChapter(mangaId: Long, chapterIndex: Int) = withContext(Dispatchers.IO) { + suspend fun getChapter(mangaId: Long, chapterIndex: Int) = withIOContext { client.getRepeat( serverUrl + getChapterQuery(mangaId, chapterIndex) ) @@ -60,7 +59,7 @@ class ChapterInteractionHandler @Inject constructor( bookmarked: Boolean? = null, lastPageRead: Int? = null, markPreviousRead: Boolean? = null - ) = withContext(Dispatchers.IO) { + ) = withIOContext { client.submitFormRepeat( serverUrl + updateChapterRequest(mangaId, chapterIndex), formParameters = Parameters.build { @@ -114,7 +113,7 @@ class ChapterInteractionHandler @Inject constructor( markPreviousRead ) - suspend fun getPage(mangaId: Long, chapterIndex: Int, pageNum: Int) = withContext(Dispatchers.IO) { + suspend fun getPage(mangaId: Long, chapterIndex: Int, pageNum: Int) = withIOContext { imageFromUrl( client, serverUrl + getPageQuery(mangaId, chapterIndex, pageNum) 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 1815997e..c417c717 100644 --- a/src/main/kotlin/ca/gosyer/data/server/interactions/ExtensionInteractionHandler.kt +++ b/src/main/kotlin/ca/gosyer/data/server/interactions/ExtensionInteractionHandler.kt @@ -14,9 +14,8 @@ 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.lang.withIOContext import io.ktor.client.statement.HttpResponse -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext import javax.inject.Inject class ExtensionInteractionHandler @Inject constructor( @@ -24,31 +23,31 @@ class ExtensionInteractionHandler @Inject constructor( serverPreferences: ServerPreferences ) : BaseInteractionHandler(client, serverPreferences) { - suspend fun getExtensionList() = withContext(Dispatchers.IO) { + suspend fun getExtensionList() = withIOContext { client.getRepeat>( serverUrl + extensionListQuery() ) } - suspend fun installExtension(extension: Extension) = withContext(Dispatchers.IO) { + suspend fun installExtension(extension: Extension) = withIOContext { client.getRepeat( serverUrl + apkInstallQuery(extension.pkgName) ) } - suspend fun updateExtension(extension: Extension) = withContext(Dispatchers.IO) { + suspend fun updateExtension(extension: Extension) = withIOContext { client.getRepeat( serverUrl + apkUpdateQuery(extension.pkgName) ) } - suspend fun uninstallExtension(extension: Extension) = withContext(Dispatchers.IO) { + suspend fun uninstallExtension(extension: Extension) = withIOContext { client.getRepeat( serverUrl + apkUninstallQuery(extension.pkgName) ) } - suspend fun getApkIcon(extension: Extension) = withContext(Dispatchers.IO) { + suspend fun getApkIcon(extension: Extension) = withIOContext { imageFromUrl( client, serverUrl + apkIconQuery(extension.apkName) 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 5a89eae8..8027d0e4 100644 --- a/src/main/kotlin/ca/gosyer/data/server/interactions/LibraryInteractionHandler.kt +++ b/src/main/kotlin/ca/gosyer/data/server/interactions/LibraryInteractionHandler.kt @@ -12,9 +12,8 @@ import ca.gosyer.data.server.ServerPreferences 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.statement.HttpResponse -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext import javax.inject.Inject class LibraryInteractionHandler @Inject constructor( @@ -22,13 +21,13 @@ class LibraryInteractionHandler @Inject constructor( serverPreferences: ServerPreferences ) : BaseInteractionHandler(client, serverPreferences) { - suspend fun getLibraryManga() = withContext(Dispatchers.IO) { + suspend fun getLibraryManga() = withIOContext { client.getRepeat>( serverUrl + getLibraryQuery() ) } - suspend fun addMangaToLibrary(mangaId: Long) = withContext(Dispatchers.IO) { + suspend fun addMangaToLibrary(mangaId: Long) = withIOContext { client.getRepeat( serverUrl + addMangaToLibraryQuery(mangaId) ) @@ -36,7 +35,7 @@ class LibraryInteractionHandler @Inject constructor( suspend fun addMangaToLibrary(manga: Manga) = addMangaToLibrary(manga.id) - suspend fun removeMangaFromLibrary(mangaId: Long) = withContext(Dispatchers.IO) { + suspend fun removeMangaFromLibrary(mangaId: Long) = withIOContext { client.deleteRepeat( 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 0f08e367..187074e3 100644 --- a/src/main/kotlin/ca/gosyer/data/server/interactions/MangaInteractionHandler.kt +++ b/src/main/kotlin/ca/gosyer/data/server/interactions/MangaInteractionHandler.kt @@ -11,9 +11,8 @@ import ca.gosyer.data.server.Http import ca.gosyer.data.server.ServerPreferences import ca.gosyer.data.server.requests.mangaQuery import ca.gosyer.data.server.requests.mangaThumbnailQuery +import ca.gosyer.util.lang.withIOContext import io.ktor.client.request.parameter -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext import javax.inject.Inject class MangaInteractionHandler @Inject constructor( @@ -21,7 +20,7 @@ class MangaInteractionHandler @Inject constructor( serverPreferences: ServerPreferences ) : BaseInteractionHandler(client, serverPreferences) { - suspend fun getManga(mangaId: Long, refresh: Boolean = false) = withContext(Dispatchers.IO) { + suspend fun getManga(mangaId: Long, refresh: Boolean = false) = withIOContext { client.getRepeat( serverUrl + mangaQuery(mangaId) ) { @@ -35,7 +34,7 @@ class MangaInteractionHandler @Inject constructor( suspend fun getManga(manga: Manga, refresh: Boolean = false) = getManga(manga.id, refresh) - suspend fun getMangaThumbnail(mangaId: Long) = withContext(Dispatchers.IO) { + suspend fun getMangaThumbnail(mangaId: Long) = withIOContext { imageFromUrl( client, serverUrl + mangaThumbnailQuery(mangaId) 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 45f20a47..65de9765 100644 --- a/src/main/kotlin/ca/gosyer/data/server/interactions/SourceInteractionHandler.kt +++ b/src/main/kotlin/ca/gosyer/data/server/interactions/SourceInteractionHandler.kt @@ -17,9 +17,8 @@ import ca.gosyer.data.server.requests.sourceLatestQuery 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.statement.HttpResponse -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext import javax.inject.Inject class SourceInteractionHandler @Inject constructor( @@ -27,13 +26,13 @@ class SourceInteractionHandler @Inject constructor( serverPreferences: ServerPreferences ) : BaseInteractionHandler(client, serverPreferences) { - suspend fun getSourceList() = withContext(Dispatchers.IO) { + suspend fun getSourceList() = withIOContext { client.getRepeat>( serverUrl + sourceListQuery() ) } - suspend fun getSourceInfo(sourceId: Long) = withContext(Dispatchers.IO) { + suspend fun getSourceInfo(sourceId: Long) = withIOContext { client.getRepeat( serverUrl + sourceInfoQuery(sourceId) ) @@ -41,7 +40,7 @@ class SourceInteractionHandler @Inject constructor( suspend fun getSourceInfo(source: Source) = getSourceInfo(source.id) - suspend fun getPopularManga(sourceId: Long, pageNum: Int) = withContext(Dispatchers.IO) { + suspend fun getPopularManga(sourceId: Long, pageNum: Int) = withIOContext { client.getRepeat( serverUrl + sourcePopularQuery(sourceId, pageNum) ) @@ -52,7 +51,7 @@ class SourceInteractionHandler @Inject constructor( pageNum ) - suspend fun getLatestManga(sourceId: Long, pageNum: Int) = withContext(Dispatchers.IO) { + suspend fun getLatestManga(sourceId: Long, pageNum: Int) = withIOContext { client.getRepeat( serverUrl + sourceLatestQuery(sourceId, pageNum) ) @@ -64,13 +63,13 @@ class SourceInteractionHandler @Inject constructor( ) // TODO: 2021-03-14 - suspend fun getGlobalSearchResults(searchTerm: String) = withContext(Dispatchers.IO) { + suspend fun getGlobalSearchResults(searchTerm: String) = withIOContext { client.getRepeat( serverUrl + globalSearchQuery(searchTerm) ) } - suspend fun getSearchResults(sourceId: Long, searchTerm: String, pageNum: Int) = withContext(Dispatchers.IO) { + suspend fun getSearchResults(sourceId: Long, searchTerm: String, pageNum: Int) = withIOContext { client.getRepeat( serverUrl + sourceSearchQuery(sourceId, searchTerm, pageNum) ) @@ -83,7 +82,7 @@ class SourceInteractionHandler @Inject constructor( ) // TODO: 2021-03-14 - suspend fun getFilterList(sourceId: Long) = withContext(Dispatchers.IO) { + suspend fun getFilterList(sourceId: Long) = withIOContext { client.getRepeat( serverUrl + getFilterListQuery(sourceId) ) diff --git a/src/main/kotlin/ca/gosyer/ui/base/WindowDialog.kt b/src/main/kotlin/ca/gosyer/ui/base/WindowDialog.kt index 274f4472..9ec1dddf 100644 --- a/src/main/kotlin/ca/gosyer/ui/base/WindowDialog.kt +++ b/src/main/kotlin/ca/gosyer/ui/base/WindowDialog.kt @@ -28,8 +28,10 @@ import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.IntSize import androidx.compose.ui.unit.dp import ca.gosyer.ui.base.theme.AppTheme -import javax.swing.SwingUtilities +import ca.gosyer.util.lang.launchUI +import kotlinx.coroutines.DelicateCoroutinesApi +@OptIn(DelicateCoroutinesApi::class) @Suppress("FunctionName") fun WindowDialog( title: String = "Dialog", @@ -43,7 +45,7 @@ fun WindowDialog( onPositiveButton: (() -> Unit)? = null, keyboardShortcuts: List = emptyList(), row: @Composable (RowScope.() -> Unit) -) = SwingUtilities.invokeLater { +) = launchUI { val window = AppWindow( title = title, size = size, @@ -96,6 +98,7 @@ fun WindowDialog( } } +@OptIn(DelicateCoroutinesApi::class) fun WindowDialog( title: String = "Dialog", size: IntSize = IntSize(400, 200), @@ -104,7 +107,7 @@ fun WindowDialog( keyboardShortcuts: List = emptyList(), buttons: @Composable (AppWindow) -> Unit, content: @Composable (AppWindow) -> Unit -) = SwingUtilities.invokeLater { +) = launchUI { val window = AppWindow( title = title, size = size, diff --git a/src/main/kotlin/ca/gosyer/ui/main/main.kt b/src/main/kotlin/ca/gosyer/ui/main/main.kt index e2bc1e44..30226e8d 100644 --- a/src/main/kotlin/ca/gosyer/ui/main/main.kt +++ b/src/main/kotlin/ca/gosyer/ui/main/main.kt @@ -21,6 +21,8 @@ import ca.gosyer.data.ui.model.ThemeMode import ca.gosyer.data.ui.model.WindowSettings import ca.gosyer.ui.base.components.LoadingScreen import ca.gosyer.ui.base.theme.AppTheme +import ca.gosyer.util.lang.launchUI +import ca.gosyer.util.lang.withUIContext import ca.gosyer.util.system.getAsFlow import com.github.weisj.darklaf.LafManager import com.github.weisj.darklaf.theme.DarculaTheme @@ -35,7 +37,6 @@ import org.apache.logging.log4j.core.config.Configurator import toothpick.configuration.Configuration import toothpick.ktp.KTP import toothpick.ktp.extension.getInstance -import javax.swing.SwingUtilities @OptIn(DelicateCoroutinesApi::class) fun main() { @@ -72,7 +73,7 @@ fun main() { ThemeMode.Dark -> DarculaTheme() } LafManager.enableLogging(BuildConfig.DEBUG) - SwingUtilities.invokeLater { + withUIContext { LafManager.install(theme) } } @@ -85,7 +86,7 @@ fun main() { maximized ) = windowSettings.get().get() - SwingUtilities.invokeLater { + launchUI { val window = AppWindow( title = BuildConfig.NAME, size = size, diff --git a/src/main/kotlin/ca/gosyer/ui/manga/MangaMenuViewModel.kt b/src/main/kotlin/ca/gosyer/ui/manga/MangaMenuViewModel.kt index 7a836d80..46e14e04 100644 --- a/src/main/kotlin/ca/gosyer/ui/manga/MangaMenuViewModel.kt +++ b/src/main/kotlin/ca/gosyer/ui/manga/MangaMenuViewModel.kt @@ -14,14 +14,13 @@ import ca.gosyer.data.server.interactions.LibraryInteractionHandler import ca.gosyer.data.server.interactions.MangaInteractionHandler import ca.gosyer.data.ui.UiPreferences import ca.gosyer.ui.base.vm.ViewModel +import ca.gosyer.util.lang.withIOContext import kotlinx.coroutines.CancellationException -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import java.text.DateFormat import java.text.SimpleDateFormat import java.util.Locale @@ -59,7 +58,7 @@ class MangaMenuViewModel @Inject constructor( } } - private suspend fun refreshMangaAsync(mangaId: Long) = withContext(Dispatchers.IO) { + private suspend fun refreshMangaAsync(mangaId: Long) = withIOContext { async { try { _manga.value = mangaHandler.getManga(mangaId) @@ -69,7 +68,7 @@ class MangaMenuViewModel @Inject constructor( } } - private suspend fun refreshChaptersAsync(mangaId: Long) = withContext(Dispatchers.IO) { + private suspend fun refreshChaptersAsync(mangaId: Long) = withIOContext { async { try { _chapters.value = chapterHandler.getChapters(mangaId) diff --git a/src/main/kotlin/ca/gosyer/ui/reader/ReaderMenu.kt b/src/main/kotlin/ca/gosyer/ui/reader/ReaderMenu.kt index 6226f66b..0b76e079 100644 --- a/src/main/kotlin/ca/gosyer/ui/reader/ReaderMenu.kt +++ b/src/main/kotlin/ca/gosyer/ui/reader/ReaderMenu.kt @@ -44,12 +44,13 @@ import ca.gosyer.ui.base.theme.AppTheme import ca.gosyer.ui.base.vm.viewModel import ca.gosyer.ui.reader.model.ReaderChapter import ca.gosyer.ui.reader.model.ReaderPage +import ca.gosyer.util.lang.launchUI import com.google.accompanist.pager.HorizontalPager import com.google.accompanist.pager.VerticalPager import com.google.accompanist.pager.rememberPagerState -import toothpick.ktp.extension.getInstance -import javax.swing.SwingUtilities +import kotlinx.coroutines.DelicateCoroutinesApi +@OptIn(DelicateCoroutinesApi::class) fun openReaderMenu(chapterIndex: Int, mangaId: Long) { val windowSettings = AppScope.getInstance() .readerWindow() @@ -59,7 +60,7 @@ fun openReaderMenu(chapterIndex: Int, mangaId: Long) { maximized ) = windowSettings.get().get() - SwingUtilities.invokeLater { + launchUI { val window = AppWindow( "TachideskJUI - Reader", size = size, diff --git a/src/main/kotlin/ca/gosyer/util/compose/ContextMenu.kt b/src/main/kotlin/ca/gosyer/util/compose/ContextMenu.kt index a9f4b914..54f23647 100644 --- a/src/main/kotlin/ca/gosyer/util/compose/ContextMenu.kt +++ b/src/main/kotlin/ca/gosyer/util/compose/ContextMenu.kt @@ -8,16 +8,18 @@ package ca.gosyer.util.compose import androidx.compose.desktop.AppManager import androidx.compose.ui.unit.IntOffset +import ca.gosyer.util.lang.launchUI import com.github.weisj.darklaf.listener.MouseClickListener +import kotlinx.coroutines.DelicateCoroutinesApi import javax.swing.Icon import javax.swing.JMenuItem import javax.swing.JPopupMenu import javax.swing.JSeparator -import javax.swing.SwingUtilities class ContextMenu internal constructor() { internal val items = mutableListOf Unit)?>>() + @OptIn(DelicateCoroutinesApi::class) internal fun popupMenu() = JPopupMenu().apply { val window = AppManager.focusedWindow var mouseListener: MouseClickListener? = null @@ -26,14 +28,14 @@ class ContextMenu internal constructor() { mouseListener?.let { window?.removeMouseListener(it) } } fun (() -> Unit)?.andClose() { - SwingUtilities.invokeLater { + launchUI { close() - this?.invoke() + this@andClose?.invoke() } } mouseListener = MouseClickListener { - SwingUtilities.invokeLater { + launchUI { close() } } diff --git a/src/main/kotlin/ca/gosyer/util/lang/CoroutineExtensions.kt b/src/main/kotlin/ca/gosyer/util/lang/CoroutineExtensions.kt new file mode 100644 index 00000000..bddb1fe4 --- /dev/null +++ b/src/main/kotlin/ca/gosyer/util/lang/CoroutineExtensions.kt @@ -0,0 +1,45 @@ +/* + * 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.util.lang + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.CoroutineStart +import kotlinx.coroutines.DelicateCoroutinesApi +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext + +@DelicateCoroutinesApi +fun launch( + start: CoroutineStart = CoroutineStart.DEFAULT, + block: suspend CoroutineScope.() -> Unit +) = GlobalScope.launch(Dispatchers.Default, start, block) + +@DelicateCoroutinesApi +fun launchUI( + start: CoroutineStart = CoroutineStart.DEFAULT, + block: suspend CoroutineScope.() -> Unit +) = GlobalScope.launch(Dispatchers.Main, start, block) + +@DelicateCoroutinesApi +fun launchIO( + start: CoroutineStart = CoroutineStart.DEFAULT, + block: suspend CoroutineScope.() -> Unit +) = GlobalScope.launch(Dispatchers.IO, start, block) + +suspend fun withDefaultContext( + block: suspend CoroutineScope.() -> T +) = withContext(Dispatchers.Default, block) + +suspend fun withUIContext( + block: suspend CoroutineScope.() -> T +) = withContext(Dispatchers.Main, block) + +suspend fun withIOContext( + block: suspend CoroutineScope.() -> T +) = withContext(Dispatchers.IO, block) diff --git a/src/main/kotlin/ca/gosyer/util/lang/String.kt b/src/main/kotlin/ca/gosyer/util/lang/String.kt index ae428904..12aa11e5 100644 --- a/src/main/kotlin/ca/gosyer/util/lang/String.kt +++ b/src/main/kotlin/ca/gosyer/util/lang/String.kt @@ -8,4 +8,5 @@ package ca.gosyer.util.lang import java.util.Locale -fun String.capitalize() = replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() } +fun String.capitalize(locale: Locale = Locale.getDefault()) = + replaceFirstChar { if (it.isLowerCase()) it.titlecase(locale) else it.toString() } diff --git a/src/main/kotlin/ca/gosyer/util/system/File.kt b/src/main/kotlin/ca/gosyer/util/system/File.kt index bfbd9176..cae7ce41 100644 --- a/src/main/kotlin/ca/gosyer/util/system/File.kt +++ b/src/main/kotlin/ca/gosyer/util/system/File.kt @@ -7,11 +7,12 @@ package ca.gosyer.util.system import ca.gosyer.BuildConfig +import ca.gosyer.util.lang.launchUI +import kotlinx.coroutines.DelicateCoroutinesApi import net.harawata.appdirs.AppDirs import net.harawata.appdirs.AppDirsFactory import java.io.File import javax.swing.JFileChooser -import javax.swing.SwingUtilities import javax.swing.filechooser.FileNameExtensionFilter val appDirs: AppDirs by lazy { @@ -58,6 +59,7 @@ fun fileSaver( * @param onError the listener that is called when picking a file exited with a error * @param onApprove the listener that is called when picking a file is completed */ +@OptIn(DelicateCoroutinesApi::class) private fun fileChooser( saving: Boolean = false, builder: JFileChooser.() -> Unit = {}, @@ -66,7 +68,7 @@ private fun fileChooser( onApprove: (JFileChooser) -> Unit, extension: String? = null, defaultFileName: String = "" -) = SwingUtilities.invokeLater { +) = launchUI { val fileChooser = JFileChooser() .apply { val details = actionMap.get("viewTypeDetails")