Avoid using global scope where appropriate

Also fixes the crash in tracking when an exception is thrown during a refresh.
This commit is contained in:
arkon 2021-01-08 18:05:51 -05:00
parent 96b8beb9cd
commit 2ffbee3db2
14 changed files with 88 additions and 81 deletions

View File

@ -8,8 +8,7 @@ import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.await import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.network.jsonMime import eu.kanade.tachiyomi.network.jsonMime
import eu.kanade.tachiyomi.network.parseAs import eu.kanade.tachiyomi.network.parseAs
import kotlinx.coroutines.Dispatchers import eu.kanade.tachiyomi.util.lang.withIOContext
import kotlinx.coroutines.withContext
import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.buildJsonObject import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.contentOrNull import kotlinx.serialization.json.contentOrNull
@ -30,7 +29,7 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
private val authClient = client.newBuilder().addInterceptor(interceptor).build() private val authClient = client.newBuilder().addInterceptor(interceptor).build()
suspend fun addLibManga(track: Track): Track { suspend fun addLibManga(track: Track): Track {
return withContext(Dispatchers.IO) { return withIOContext {
val query = val query =
""" """
|mutation AddManga(${'$'}mangaId: Int, ${'$'}progress: Int, ${'$'}status: MediaListStatus) { |mutation AddManga(${'$'}mangaId: Int, ${'$'}progress: Int, ${'$'}status: MediaListStatus) {
@ -65,7 +64,7 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
} }
suspend fun updateLibManga(track: Track): Track { suspend fun updateLibManga(track: Track): Track {
return withContext(Dispatchers.IO) { return withIOContext {
val query = val query =
""" """
|mutation UpdateManga(${'$'}listId: Int, ${'$'}progress: Int, ${'$'}status: MediaListStatus, ${'$'}score: Int) { |mutation UpdateManga(${'$'}listId: Int, ${'$'}progress: Int, ${'$'}status: MediaListStatus, ${'$'}score: Int) {
@ -92,7 +91,7 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
} }
suspend fun search(search: String): List<TrackSearch> { suspend fun search(search: String): List<TrackSearch> {
return withContext(Dispatchers.IO) { return withIOContext {
val query = val query =
""" """
|query Search(${'$'}query: String) { |query Search(${'$'}query: String) {
@ -143,7 +142,7 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
} }
suspend fun findLibManga(track: Track, userid: Int): Track? { suspend fun findLibManga(track: Track, userid: Int): Track? {
return withContext(Dispatchers.IO) { return withIOContext {
val query = val query =
""" """
|query (${'$'}id: Int!, ${'$'}manga_id: Int!) { |query (${'$'}id: Int!, ${'$'}manga_id: Int!) {
@ -209,7 +208,7 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
} }
suspend fun getCurrentUser(): Pair<Int, String> { suspend fun getCurrentUser(): Pair<Int, String> {
return withContext(Dispatchers.IO) { return withIOContext {
val query = val query =
""" """
|query User { |query User {

View File

@ -9,8 +9,7 @@ import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.await import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.network.parseAs import eu.kanade.tachiyomi.network.parseAs
import kotlinx.coroutines.Dispatchers import eu.kanade.tachiyomi.util.lang.withIOContext
import kotlinx.coroutines.withContext
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonObject
@ -33,7 +32,7 @@ class BangumiApi(private val client: OkHttpClient, interceptor: BangumiIntercept
private val authClient = client.newBuilder().addInterceptor(interceptor).build() private val authClient = client.newBuilder().addInterceptor(interceptor).build()
suspend fun addLibManga(track: Track): Track { suspend fun addLibManga(track: Track): Track {
return withContext(Dispatchers.IO) { return withIOContext {
val body = FormBody.Builder() val body = FormBody.Builder()
.add("rating", track.score.toInt().toString()) .add("rating", track.score.toInt().toString())
.add("status", track.toBangumiStatus()) .add("status", track.toBangumiStatus())
@ -45,7 +44,7 @@ class BangumiApi(private val client: OkHttpClient, interceptor: BangumiIntercept
} }
suspend fun updateLibManga(track: Track): Track { suspend fun updateLibManga(track: Track): Track {
return withContext(Dispatchers.IO) { return withIOContext {
// read status update // read status update
val sbody = FormBody.Builder() val sbody = FormBody.Builder()
.add("status", track.toBangumiStatus()) .add("status", track.toBangumiStatus())
@ -69,7 +68,7 @@ class BangumiApi(private val client: OkHttpClient, interceptor: BangumiIntercept
} }
suspend fun search(search: String): List<TrackSearch> { suspend fun search(search: String): List<TrackSearch> {
return withContext(Dispatchers.IO) { return withIOContext {
val url = "$apiUrl/search/subject/${URLEncoder.encode(search, Charsets.UTF_8.name())}" val url = "$apiUrl/search/subject/${URLEncoder.encode(search, Charsets.UTF_8.name())}"
.toUri() .toUri()
.buildUpon() .buildUpon()
@ -117,7 +116,7 @@ class BangumiApi(private val client: OkHttpClient, interceptor: BangumiIntercept
} }
suspend fun findLibManga(track: Track): Track? { suspend fun findLibManga(track: Track): Track? {
return withContext(Dispatchers.IO) { return withIOContext {
authClient.newCall(GET("$apiUrl/subject/${track.media_id}")) authClient.newCall(GET("$apiUrl/subject/${track.media_id}"))
.await() .await()
.parseAs<JsonObject>() .parseAs<JsonObject>()
@ -126,7 +125,7 @@ class BangumiApi(private val client: OkHttpClient, interceptor: BangumiIntercept
} }
suspend fun statusLibManga(track: Track): Track? { suspend fun statusLibManga(track: Track): Track? {
return withContext(Dispatchers.IO) { return withIOContext {
val urlUserRead = "$apiUrl/collection/${track.media_id}" val urlUserRead = "$apiUrl/collection/${track.media_id}"
val requestUserRead = Request.Builder() val requestUserRead = Request.Builder()
.url(urlUserRead) .url(urlUserRead)
@ -147,7 +146,7 @@ class BangumiApi(private val client: OkHttpClient, interceptor: BangumiIntercept
} }
suspend fun accessToken(code: String): OAuth { suspend fun accessToken(code: String): OAuth {
return withContext(Dispatchers.IO) { return withIOContext {
client.newCall(accessTokenRequest(code)) client.newCall(accessTokenRequest(code))
.await() .await()
.parseAs() .parseAs()

View File

@ -8,8 +8,7 @@ import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.await import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.network.jsonMime import eu.kanade.tachiyomi.network.jsonMime
import eu.kanade.tachiyomi.network.parseAs import eu.kanade.tachiyomi.network.parseAs
import kotlinx.coroutines.Dispatchers import eu.kanade.tachiyomi.util.lang.withIOContext
import kotlinx.coroutines.withContext
import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.buildJsonObject import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.int import kotlinx.serialization.json.int
@ -31,7 +30,7 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
private val authClient = client.newBuilder().addInterceptor(interceptor).build() private val authClient = client.newBuilder().addInterceptor(interceptor).build()
suspend fun addLibManga(track: Track, userId: String): Track { suspend fun addLibManga(track: Track, userId: String): Track {
return withContext(Dispatchers.IO) { return withIOContext {
val data = buildJsonObject { val data = buildJsonObject {
putJsonObject("data") { putJsonObject("data") {
put("type", "libraryEntries") put("type", "libraryEntries")
@ -76,7 +75,7 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
} }
suspend fun updateLibManga(track: Track): Track { suspend fun updateLibManga(track: Track): Track {
return withContext(Dispatchers.IO) { return withIOContext {
val data = buildJsonObject { val data = buildJsonObject {
putJsonObject("data") { putJsonObject("data") {
put("type", "libraryEntries") put("type", "libraryEntries")
@ -110,7 +109,7 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
} }
suspend fun search(query: String): List<TrackSearch> { suspend fun search(query: String): List<TrackSearch> {
return withContext(Dispatchers.IO) { return withIOContext {
authClient.newCall(GET(algoliaKeyUrl)) authClient.newCall(GET(algoliaKeyUrl))
.await() .await()
.parseAs<JsonObject>() .parseAs<JsonObject>()
@ -122,7 +121,7 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
} }
private suspend fun algoliaSearch(key: String, query: String): List<TrackSearch> { private suspend fun algoliaSearch(key: String, query: String): List<TrackSearch> {
return withContext(Dispatchers.IO) { return withIOContext {
val jsonObject = buildJsonObject { val jsonObject = buildJsonObject {
put("params", "query=$query$algoliaFilter") put("params", "query=$query$algoliaFilter")
} }
@ -151,7 +150,7 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
} }
suspend fun findLibManga(track: Track, userId: String): Track? { suspend fun findLibManga(track: Track, userId: String): Track? {
return withContext(Dispatchers.IO) { return withIOContext {
val url = "${baseUrl}library-entries".toUri().buildUpon() val url = "${baseUrl}library-entries".toUri().buildUpon()
.encodedQuery("filter[manga_id]=${track.media_id}&filter[user_id]=$userId") .encodedQuery("filter[manga_id]=${track.media_id}&filter[user_id]=$userId")
.appendQueryParameter("include", "manga") .appendQueryParameter("include", "manga")
@ -172,7 +171,7 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
} }
suspend fun getLibManga(track: Track): Track { suspend fun getLibManga(track: Track): Track {
return withContext(Dispatchers.IO) { return withIOContext {
val url = "${baseUrl}library-entries".toUri().buildUpon() val url = "${baseUrl}library-entries".toUri().buildUpon()
.encodedQuery("filter[id]=${track.media_id}") .encodedQuery("filter[id]=${track.media_id}")
.appendQueryParameter("include", "manga") .appendQueryParameter("include", "manga")
@ -193,7 +192,7 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
} }
suspend fun login(username: String, password: String): OAuth { suspend fun login(username: String, password: String): OAuth {
return withContext(Dispatchers.IO) { return withIOContext {
val formBody: RequestBody = FormBody.Builder() val formBody: RequestBody = FormBody.Builder()
.add("username", username) .add("username", username)
.add("password", password) .add("password", password)
@ -208,7 +207,7 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
} }
suspend fun getCurrentUser(): String { suspend fun getCurrentUser(): String {
return withContext(Dispatchers.IO) { return withIOContext {
val url = "${baseUrl}users".toUri().buildUpon() val url = "${baseUrl}users".toUri().buildUpon()
.encodedQuery("filter[self]=true") .encodedQuery("filter[self]=true")
.build() .build()

View File

@ -10,10 +10,9 @@ import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.await import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.network.parseAs import eu.kanade.tachiyomi.network.parseAs
import eu.kanade.tachiyomi.util.PkceUtil import eu.kanade.tachiyomi.util.PkceUtil
import kotlinx.coroutines.Dispatchers import eu.kanade.tachiyomi.util.lang.withIOContext
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.withContext
import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.boolean import kotlinx.serialization.json.boolean
import kotlinx.serialization.json.contentOrNull import kotlinx.serialization.json.contentOrNull
@ -33,7 +32,7 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
private val authClient = client.newBuilder().addInterceptor(interceptor).build() private val authClient = client.newBuilder().addInterceptor(interceptor).build()
suspend fun getAccessToken(authCode: String): OAuth { suspend fun getAccessToken(authCode: String): OAuth {
return withContext(Dispatchers.IO) { return withIOContext {
val formBody: RequestBody = FormBody.Builder() val formBody: RequestBody = FormBody.Builder()
.add("client_id", clientId) .add("client_id", clientId)
.add("code", authCode) .add("code", authCode)
@ -47,7 +46,7 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
} }
suspend fun getCurrentUser(): String { suspend fun getCurrentUser(): String {
return withContext(Dispatchers.IO) { return withIOContext {
val request = Request.Builder() val request = Request.Builder()
.url("$baseApiUrl/users/@me") .url("$baseApiUrl/users/@me")
.get() .get()
@ -60,7 +59,7 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
} }
suspend fun search(query: String): List<TrackSearch> { suspend fun search(query: String): List<TrackSearch> {
return withContext(Dispatchers.IO) { return withIOContext {
val url = "$baseApiUrl/manga".toUri().buildUpon() val url = "$baseApiUrl/manga".toUri().buildUpon()
.appendQueryParameter("q", query) .appendQueryParameter("q", query)
.appendQueryParameter("nsfw", "true") .appendQueryParameter("nsfw", "true")
@ -82,7 +81,7 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
} }
suspend fun getMangaDetails(id: Int): TrackSearch { suspend fun getMangaDetails(id: Int): TrackSearch {
return withContext(Dispatchers.IO) { return withIOContext {
val url = "$baseApiUrl/manga".toUri().buildUpon() val url = "$baseApiUrl/manga".toUri().buildUpon()
.appendPath(id.toString()) .appendPath(id.toString())
.appendQueryParameter("fields", "id,title,synopsis,num_chapters,main_picture,status,media_type,start_date") .appendQueryParameter("fields", "id,title,synopsis,num_chapters,main_picture,status,media_type,start_date")
@ -113,7 +112,7 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
} }
suspend fun getListItem(track: Track): Track { suspend fun getListItem(track: Track): Track {
return withContext(Dispatchers.IO) { return withIOContext {
val formBody: RequestBody = FormBody.Builder() val formBody: RequestBody = FormBody.Builder()
.add("status", track.toMyAnimeListStatus() ?: "reading") .add("status", track.toMyAnimeListStatus() ?: "reading")
.build() .build()
@ -129,7 +128,7 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
} }
suspend fun addItemToList(track: Track): Track { suspend fun addItemToList(track: Track): Track {
return withContext(Dispatchers.IO) { return withIOContext {
val formBody: RequestBody = FormBody.Builder() val formBody: RequestBody = FormBody.Builder()
.add("status", "reading") .add("status", "reading")
.add("score", "0") .add("score", "0")
@ -146,7 +145,7 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
} }
suspend fun updateItem(track: Track): Track { suspend fun updateItem(track: Track): Track {
return withContext(Dispatchers.IO) { return withIOContext {
val formBody: RequestBody = FormBody.Builder() val formBody: RequestBody = FormBody.Builder()
.add("status", track.toMyAnimeListStatus() ?: "reading") .add("status", track.toMyAnimeListStatus() ?: "reading")
.add("is_rereading", (track.status == MyAnimeList.REREADING).toString()) .add("is_rereading", (track.status == MyAnimeList.REREADING).toString())
@ -188,7 +187,7 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
} }
suspend fun findListItems(query: String, offset: Int = 0): List<TrackSearch> { suspend fun findListItems(query: String, offset: Int = 0): List<TrackSearch> {
return withContext(Dispatchers.IO) { return withIOContext {
val json = getListPage(offset) val json = getListPage(offset)
val obj = json.jsonObject val obj = json.jsonObject
@ -215,7 +214,7 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
} }
private suspend fun getListPage(offset: Int): JsonObject { private suspend fun getListPage(offset: Int): JsonObject {
return withContext(Dispatchers.IO) { return withIOContext {
val urlBuilder = "$baseApiUrl/users/@me/mangalist".toUri().buildUpon() val urlBuilder = "$baseApiUrl/users/@me/mangalist".toUri().buildUpon()
.appendQueryParameter("fields", "list_status") .appendQueryParameter("fields", "list_status")
.appendQueryParameter("limit", listPaginationAmount.toString()) .appendQueryParameter("limit", listPaginationAmount.toString())

View File

@ -9,9 +9,8 @@ import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.await import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.network.jsonMime import eu.kanade.tachiyomi.network.jsonMime
import eu.kanade.tachiyomi.network.parseAs import eu.kanade.tachiyomi.network.parseAs
import kotlinx.coroutines.Dispatchers import eu.kanade.tachiyomi.util.lang.withIOContext
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import kotlinx.serialization.json.JsonArray import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.buildJsonObject import kotlinx.serialization.json.buildJsonObject
@ -30,7 +29,7 @@ class ShikimoriApi(private val client: OkHttpClient, interceptor: ShikimoriInter
private val authClient = client.newBuilder().addInterceptor(interceptor).build() private val authClient = client.newBuilder().addInterceptor(interceptor).build()
suspend fun addLibManga(track: Track, user_id: String): Track { suspend fun addLibManga(track: Track, user_id: String): Track {
return withContext(Dispatchers.IO) { return withIOContext {
val payload = buildJsonObject { val payload = buildJsonObject {
putJsonObject("user_rate") { putJsonObject("user_rate") {
put("user_id", user_id) put("user_id", user_id)
@ -54,7 +53,7 @@ class ShikimoriApi(private val client: OkHttpClient, interceptor: ShikimoriInter
suspend fun updateLibManga(track: Track, user_id: String): Track = addLibManga(track, user_id) suspend fun updateLibManga(track: Track, user_id: String): Track = addLibManga(track, user_id)
suspend fun search(search: String): List<TrackSearch> { suspend fun search(search: String): List<TrackSearch> {
return withContext(Dispatchers.IO) { return withIOContext {
val url = "$apiUrl/mangas".toUri().buildUpon() val url = "$apiUrl/mangas".toUri().buildUpon()
.appendQueryParameter("order", "popularity") .appendQueryParameter("order", "popularity")
.appendQueryParameter("search", search) .appendQueryParameter("search", search)
@ -98,7 +97,7 @@ class ShikimoriApi(private val client: OkHttpClient, interceptor: ShikimoriInter
} }
suspend fun findLibManga(track: Track, user_id: String): Track? { suspend fun findLibManga(track: Track, user_id: String): Track? {
return withContext(Dispatchers.IO) { return withIOContext {
val urlMangas = "$apiUrl/mangas".toUri().buildUpon() val urlMangas = "$apiUrl/mangas".toUri().buildUpon()
.appendPath(track.media_id.toString()) .appendPath(track.media_id.toString())
.build() .build()
@ -138,7 +137,7 @@ class ShikimoriApi(private val client: OkHttpClient, interceptor: ShikimoriInter
} }
suspend fun accessToken(code: String): OAuth { suspend fun accessToken(code: String): OAuth {
return withContext(Dispatchers.IO) { return withIOContext {
client.newCall(accessTokenRequest(code)) client.newCall(accessTokenRequest(code))
.await() .await()
.parseAs() .parseAs()

View File

@ -6,8 +6,7 @@ import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.network.await import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.network.parseAs import eu.kanade.tachiyomi.network.parseAs
import kotlinx.coroutines.Dispatchers import eu.kanade.tachiyomi.util.lang.withIOContext
import kotlinx.coroutines.withContext
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
class GithubUpdateChecker { class GithubUpdateChecker {
@ -23,7 +22,7 @@ class GithubUpdateChecker {
} }
suspend fun checkForUpdate(): UpdateResult { suspend fun checkForUpdate(): UpdateResult {
return withContext(Dispatchers.IO) { return withIOContext {
networkService.client networkService.client
.newCall(GET("https://api.github.com/repos/$repo/releases/latest")) .newCall(GET("https://api.github.com/repos/$repo/releases/latest"))
.await() .await()

View File

@ -9,8 +9,7 @@ import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.network.NetworkHelper
import eu.kanade.tachiyomi.network.await import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.network.parseAs import eu.kanade.tachiyomi.network.parseAs
import kotlinx.coroutines.Dispatchers import eu.kanade.tachiyomi.util.lang.withIOContext
import kotlinx.coroutines.withContext
import kotlinx.serialization.json.JsonArray import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.int import kotlinx.serialization.json.int
import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.jsonObject
@ -24,7 +23,7 @@ internal class ExtensionGithubApi {
private val preferences: PreferencesHelper by injectLazy() private val preferences: PreferencesHelper by injectLazy()
suspend fun findExtensions(): List<Extension.Available> { suspend fun findExtensions(): List<Extension.Available> {
return withContext(Dispatchers.IO) { return withIOContext {
networkService.client networkService.client
.newCall(GET("${REPO_URL_PREFIX}index.min.json")) .newCall(GET("${REPO_URL_PREFIX}index.min.json"))
.await() .await()

View File

@ -55,13 +55,13 @@ class SearchPresenter(
replacingMangaRelay.call(true) replacingMangaRelay.call(true)
launchIO { presenterScope.launchIO {
val chapters = source.getChapterList(manga.toMangaInfo()) val chapters = source.getChapterList(manga.toMangaInfo())
.map { it.toSChapter() } .map { it.toSChapter() }
migrateMangaInternal(source, chapters, prevManga, manga, replace) migrateMangaInternal(source, chapters, prevManga, manga, replace)
}.invokeOnCompletion { }.invokeOnCompletion {
launchUI { replacingMangaRelay.call(false) } presenterScope.launchUI { replacingMangaRelay.call(false) }
} }
} }

View File

@ -31,7 +31,7 @@ import eu.kanade.tachiyomi.ui.browse.source.filter.TriStateItem
import eu.kanade.tachiyomi.ui.browse.source.filter.TriStateSectionItem import eu.kanade.tachiyomi.ui.browse.source.filter.TriStateSectionItem
import eu.kanade.tachiyomi.util.chapter.ChapterSettingsHelper import eu.kanade.tachiyomi.util.chapter.ChapterSettingsHelper
import eu.kanade.tachiyomi.util.lang.launchIO import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.launchUI import eu.kanade.tachiyomi.util.lang.withUIContext
import eu.kanade.tachiyomi.util.removeCovers import eu.kanade.tachiyomi.util.removeCovers
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asFlow import kotlinx.coroutines.flow.asFlow
@ -213,12 +213,12 @@ open class BrowseSourcePresenter(
* @param mangas the list of manga to initialize. * @param mangas the list of manga to initialize.
*/ */
fun initializeMangas(mangas: List<Manga>) { fun initializeMangas(mangas: List<Manga>) {
launchIO { presenterScope.launchIO {
mangas.asFlow() mangas.asFlow()
.filter { it.thumbnail_url == null && !it.initialized } .filter { it.thumbnail_url == null && !it.initialized }
.map { getMangaDetails(it) } .map { getMangaDetails(it) }
.onEach { .onEach {
launchUI { withUIContext {
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
view?.onMangaInitialized(it) view?.onMangaInitialized(it)
} }

View File

@ -292,7 +292,7 @@ class MainActivity : BaseViewBindingActivity<MainActivityBinding>() {
setSelectedNavItem(startScreenId) setSelectedNavItem(startScreenId)
} else if (shouldHandleExitConfirmation()) { } else if (shouldHandleExitConfirmation()) {
// Exit confirmation (resets after 2 seconds) // Exit confirmation (resets after 2 seconds)
launchUI { resetExitConfirmation() } lifecycleScope.launchUI { resetExitConfirmation() }
} else if (backstackSize == 1 || !router.handleBack()) { } else if (backstackSize == 1 || !router.handleBack()) {
// Regular back // Regular back
super.onBackPressed() super.onBackPressed()

View File

@ -26,7 +26,7 @@ import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource
import eu.kanade.tachiyomi.util.isLocal import eu.kanade.tachiyomi.util.isLocal
import eu.kanade.tachiyomi.util.lang.await import eu.kanade.tachiyomi.util.lang.await
import eu.kanade.tachiyomi.util.lang.launchIO import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.launchUI import eu.kanade.tachiyomi.util.lang.withUIContext
import eu.kanade.tachiyomi.util.prepUpdateCover import eu.kanade.tachiyomi.util.prepUpdateCover
import eu.kanade.tachiyomi.util.removeCovers import eu.kanade.tachiyomi.util.removeCovers
import eu.kanade.tachiyomi.util.shouldDownloadNewChapters import eu.kanade.tachiyomi.util.shouldDownloadNewChapters
@ -161,7 +161,7 @@ class MangaPresenter(
*/ */
fun fetchMangaFromSource(manualFetch: Boolean = false) { fun fetchMangaFromSource(manualFetch: Boolean = false) {
if (fetchMangaJob?.isActive == true) return if (fetchMangaJob?.isActive == true) return
fetchMangaJob = launchIO { fetchMangaJob = presenterScope.launchIO {
try { try {
val networkManga = source.getMangaDetails(manga.toMangaInfo()) val networkManga = source.getMangaDetails(manga.toMangaInfo())
val sManga = networkManga.toSManga() val sManga = networkManga.toSManga()
@ -170,9 +170,9 @@ class MangaPresenter(
manga.initialized = true manga.initialized = true
db.insertManga(manga).await() db.insertManga(manga).await()
launchUI { view?.onFetchMangaInfoDone() } withUIContext { view?.onFetchMangaInfoDone() }
} catch (e: Throwable) { } catch (e: Throwable) {
launchUI { view?.onFetchMangaInfoError(e) } withUIContext { view?.onFetchMangaInfoError(e) }
} }
} }
} }
@ -360,9 +360,9 @@ class MangaPresenter(
downloadNewChapters(newChapters) downloadNewChapters(newChapters)
} }
launchUI { view?.onFetchChaptersDone() } withUIContext { view?.onFetchChaptersDone() }
} catch (e: Throwable) { } catch (e: Throwable) {
launchUI { view?.onFetchChaptersError(e) } withUIContext { view?.onFetchChaptersError(e) }
} }
} }
} }

View File

@ -10,11 +10,12 @@ import eu.kanade.tachiyomi.data.track.TrackService
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
import eu.kanade.tachiyomi.util.lang.await import eu.kanade.tachiyomi.util.lang.await
import eu.kanade.tachiyomi.util.lang.launchIO import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.launchUI import eu.kanade.tachiyomi.util.lang.withUIContext
import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.toast
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.supervisorScope
import rx.Subscription import rx.Subscription
import rx.android.schedulers.AndroidSchedulers import rx.android.schedulers.AndroidSchedulers
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
@ -59,6 +60,7 @@ class TrackPresenter(
fun refresh() { fun refresh() {
refreshJob?.cancel() refreshJob?.cancel()
refreshJob = launchIO { refreshJob = launchIO {
supervisorScope {
try { try {
trackList trackList
.filter { it.track != null } .filter { it.track != null }
@ -70,9 +72,10 @@ class TrackPresenter(
} }
.awaitAll() .awaitAll()
view?.onRefreshDone() withUIContext { view?.onRefreshDone() }
} catch (e: Throwable) { } catch (e: Throwable) {
view?.onRefreshError(e) withUIContext { view?.onRefreshError(e) }
}
} }
} }
} }
@ -82,9 +85,9 @@ class TrackPresenter(
searchJob = launchIO { searchJob = launchIO {
try { try {
val results = service.search(query) val results = service.search(query)
launchUI { view?.onSearchResults(results) } withUIContext { view?.onSearchResults(results) }
} catch (e: Throwable) { } catch (e: Throwable) {
launchUI { view?.onSearchResultsError(e) } withUIContext { view?.onSearchResultsError(e) }
} }
} }
} }
@ -97,7 +100,7 @@ class TrackPresenter(
service.bind(item) service.bind(item)
db.insertTrack(item).await() db.insertTrack(item).await()
} catch (e: Throwable) { } catch (e: Throwable) {
launchUI { context.toast(e.message) } withUIContext { context.toast(e.message) }
} }
} }
} else { } else {
@ -114,9 +117,9 @@ class TrackPresenter(
try { try {
service.update(track) service.update(track)
db.insertTrack(track).await() db.insertTrack(track).await()
view?.onRefreshDone() withUIContext { view?.onRefreshDone() }
} catch (e: Throwable) { } catch (e: Throwable) {
launchUI { view?.onRefreshError(e) } withUIContext { view?.onRefreshError(e) }
// Restart on error to set old values // Restart on error to set old values
fetchTrackings() fetchTrackings()

View File

@ -8,7 +8,7 @@ import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.data.track.TrackService import eu.kanade.tachiyomi.data.track.TrackService
import eu.kanade.tachiyomi.util.lang.launchIO import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.launchUI import eu.kanade.tachiyomi.util.lang.withUIContext
import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.toast
import eu.kanade.tachiyomi.widget.preference.LoginDialogPreference import eu.kanade.tachiyomi.widget.preference.LoginDialogPreference
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
@ -46,11 +46,11 @@ class TrackLoginDialog(
try { try {
service.login(user, pass) service.login(user, pass)
dialog?.dismiss() dialog?.dismiss()
launchUI { view?.context?.toast(R.string.login_success) } withUIContext { view?.context?.toast(R.string.login_success) }
} catch (e: Throwable) { } catch (e: Throwable) {
binding?.login?.progress = -1 binding?.login?.progress = -1
binding?.login?.setText(R.string.unknown_error) binding?.login?.setText(R.string.unknown_error)
launchUI { e.message?.let { view?.context?.toast(it) } } withUIContext { e.message?.let { view?.context?.toast(it) } }
} }
} }
} }

View File

@ -6,6 +6,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
fun launchUI(block: suspend CoroutineScope.() -> Unit): Job = fun launchUI(block: suspend CoroutineScope.() -> Unit): Job =
GlobalScope.launch(Dispatchers.Main, CoroutineStart.DEFAULT, block) GlobalScope.launch(Dispatchers.Main, CoroutineStart.DEFAULT, block)
@ -15,3 +16,13 @@ fun launchIO(block: suspend CoroutineScope.() -> Unit): Job =
fun launchNow(block: suspend CoroutineScope.() -> Unit): Job = fun launchNow(block: suspend CoroutineScope.() -> Unit): Job =
GlobalScope.launch(Dispatchers.Main, CoroutineStart.UNDISPATCHED, block) GlobalScope.launch(Dispatchers.Main, CoroutineStart.UNDISPATCHED, block)
fun CoroutineScope.launchUI(block: suspend CoroutineScope.() -> Unit): Job =
launch(Dispatchers.Main, block = block)
fun CoroutineScope.launchIO(block: suspend CoroutineScope.() -> Unit): Job =
launch(Dispatchers.IO, block = block)
suspend fun <T> withUIContext(block: suspend CoroutineScope.() -> T) = withContext(Dispatchers.Main, block)
suspend fun <T> withIOContext(block: suspend CoroutineScope.() -> T) = withContext(Dispatchers.IO, block)