refactor: Don't use context receiver

Deprecated on Kotlin 2.x, scheduled for removal in v2.1.x, will be replaced with context parameters

REF: https://github.com/Kotlin/KEEP/issues/259#issuecomment-2278319746
REF: https://youtrack.jetbrains.com/issue/KT-67119/Migration-warning-from-context-receivers-to-context-parameters
REF: https://github.com/Kotlin/KEEP/issues/367
This commit is contained in:
Ahmad Ansori Palembani 2025-01-01 07:10:30 +07:00
parent 1b92ae2e5f
commit d02f1bdd11
Signed by: null2264
GPG key ID: BA64F8B60AF3EFB6
17 changed files with 265 additions and 347 deletions

View file

@ -278,7 +278,6 @@ tasks {
// See https://kotlinlang.org/docs/reference/experimental.html#experimental-status-of-experimental-api(-markers)
withType<KotlinCompile> {
compilerOptions.freeCompilerArgs.addAll(
"-Xcontext-receivers",
// "-opt-in=kotlin.Experimental",
"-opt-in=kotlin.RequiresOptIn",
"-opt-in=kotlin.ExperimentalStdlibApi",

View file

@ -49,15 +49,13 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
put("completedAt", createDate(track.finished_reading_date))
}
}
with(json) {
authClient.newCall(POST(API_URL, body = payload.toString().toRequestBody(jsonMime)))
.awaitSuccess()
.parseAs<ALAddMangaResult>()
.let {
track.library_id = it.data.entry.id
track
}
}
authClient.newCall(POST(API_URL, body = payload.toString().toRequestBody(jsonMime)))
.awaitSuccess()
.parseAs<ALAddMangaResult>()
.let {
track.library_id = it.data.entry.id
track
}
}
}
@ -74,11 +72,9 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
put("completedAt", createDate(track.finished_reading_date))
}
}
with(json) {
authClient.newCall(POST(API_URL, body = payload.toString().toRequestBody(jsonMime)))
.awaitSuccess()
track
}
authClient.newCall(POST(API_URL, body = payload.toString().toRequestBody(jsonMime)))
.awaitSuccess()
track
}
}
@ -90,13 +86,11 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
put("query", search)
}
}
with(json) {
authClient.newCall(POST(API_URL, body = payload.toString().toRequestBody(jsonMime)))
.awaitSuccess()
.parseAs<ALSearchResult>()
.data.page.media
.map { it.toALManga().toTrack() }
}
authClient.newCall(POST(API_URL, body = payload.toString().toRequestBody(jsonMime)))
.awaitSuccess()
.parseAs<ALSearchResult>()
.data.page.media
.map { it.toALManga().toTrack() }
}
}
@ -109,15 +103,13 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
put("manga_id", track.media_id)
}
}
with(json) {
authClient.newCall(POST(API_URL, body = payload.toString().toRequestBody(jsonMime)))
.awaitSuccess()
.parseAs<ALUserListMangaQueryResult>()
.data.page.mediaList
.map { it.toALUserManga() }
.firstOrNull()
?.toTrack()
}
authClient.newCall(POST(API_URL, body = payload.toString().toRequestBody(jsonMime)))
.awaitSuccess()
.parseAs<ALUserListMangaQueryResult>()
.data.page.mediaList
.map { it.toALUserManga() }
.firstOrNull()
?.toTrack()
}
}

View file

@ -75,29 +75,25 @@ class BangumiApi(
.appendQueryParameter("responseGroup", "large")
.appendQueryParameter("max_results", "20")
.build()
with(json) {
authClient.newCall(GET(url.toString()))
.awaitSuccess()
.parseAs<BGMSearchResult>()
.let { result ->
if (result.code == 404) emptyList<TrackSearch>()
authClient.newCall(GET(url.toString()))
.awaitSuccess()
.parseAs<BGMSearchResult>()
.let { result ->
if (result.code == 404) emptyList<TrackSearch>()
result.list
?.map { it.toTrackSearch(trackId) }
.orEmpty()
}
}
result.list
?.map { it.toTrackSearch(trackId) }
.orEmpty()
}
}
}
suspend fun findLibManga(track: Track): Track? {
return withIOContext {
with(json) {
authClient.newCall(GET("$API_URL/subject/${track.media_id}"))
.awaitSuccess()
.parseAs<BGMSearchItem>()
.toTrackSearch(trackId)
}
authClient.newCall(GET("$API_URL/subject/${track.media_id}"))
.awaitSuccess()
.parseAs<BGMSearchItem>()
.toTrackSearch(trackId)
}
}
@ -111,29 +107,25 @@ class BangumiApi(
.build()
// TODO: get user readed chapter here
with(json) {
authClient.newCall(requestUserRead)
.awaitSuccess()
.parseAs<BGMCollectionResponse>()
.let {
if (it.code == 400) return@let null
authClient.newCall(requestUserRead)
.awaitSuccess()
.parseAs<BGMCollectionResponse>()
.let {
if (it.code == 400) return@let null
track.status = it.status?.id?.toInt() ?: Bangumi.DEFAULT_STATUS
track.last_chapter_read = it.epStatus!!.toFloat()
track.score = it.rating!!.toFloat()
track
}
}
track.status = it.status?.id?.toInt() ?: Bangumi.DEFAULT_STATUS
track.last_chapter_read = it.epStatus!!.toFloat()
track.score = it.rating!!.toFloat()
track
}
}
}
suspend fun accessToken(code: String): BGMOAuth {
return withIOContext {
with(json) {
client.newCall(accessTokenRequest(code))
.awaitSuccess()
.parseAs()
}
client.newCall(accessTokenRequest(code))
.awaitSuccess()
.parseAs()
}
}

View file

@ -44,7 +44,7 @@ class KavitaApi(private val client: OkHttpClient, interceptor: KavitaInterceptor
try {
client.newCall(request).execute().use {
when (it.code) {
200 -> return with(json) { it.parseAs<AuthenticationDto>().token }
200 -> return it.parseAs<AuthenticationDto>().token
401 -> {
Logger.w { "Unauthorized / api key not valid: Cleaned api URL: $apiUrl, Api key is empty: ${apiKey.isEmpty()}" }
throw IOException("Unauthorized / api key not valid")
@ -89,11 +89,10 @@ class KavitaApi(private val client: OkHttpClient, interceptor: KavitaInterceptor
private fun getTotalChapters(url: String): Long {
val requestUrl = getApiVolumesUrl(url)
try {
val listVolumeDto = with(json) {
val listVolumeDto =
authClient.newCall(GET(requestUrl))
.execute()
.parseAs<List<VolumeDto>>()
}
var volumeNumber = 0L
var maxChapterNumber = 0L
for (volume in listVolumeDto) {
@ -117,9 +116,7 @@ class KavitaApi(private val client: OkHttpClient, interceptor: KavitaInterceptor
try {
authClient.newCall(GET(requestUrl)).execute().use {
if (it.code == 200) {
return with(json) {
it.parseAs<ChapterDto>().number!!.replace(",", ".").toFloat()
}
return it.parseAs<ChapterDto>().number!!.replace(",", ".").toFloat()
}
if (it.code == 204) {
return 0F
@ -134,11 +131,10 @@ class KavitaApi(private val client: OkHttpClient, interceptor: KavitaInterceptor
suspend fun getTrackSearch(url: String): TrackSearch = withIOContext {
try {
val serieDto: SeriesDto = with(json) {
val serieDto: SeriesDto =
authClient.newCall(GET(url))
.awaitSuccess()
.parseAs()
}
val track = serieDto.toTrack()
track.apply {

View file

@ -131,14 +131,12 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
suspend fun search(query: String): List<TrackSearch> {
return withIOContext {
with(json) {
authClient.newCall(GET(ALGOLIA_KEY_URL))
.awaitSuccess()
.parseAs<KitsuSearchResult>()
.let {
algoliaSearch(it.media.key, query)
}
}
authClient.newCall(GET(ALGOLIA_KEY_URL))
.awaitSuccess()
.parseAs<KitsuSearchResult>()
.let {
algoliaSearch(it.media.key, query)
}
}
}
@ -147,25 +145,23 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
val jsonObject = buildJsonObject {
put("params", "query=$query$ALGOLIA_FILTER")
}
with(json) {
client.newCall(
POST(
ALGOLIA_URL,
headers = headersOf(
"X-Algolia-Application-Id",
ALGOLIA_APP_ID,
"X-Algolia-API-Key",
key,
),
body = jsonObject.toString().toRequestBody(jsonMime),
client.newCall(
POST(
ALGOLIA_URL,
headers = headersOf(
"X-Algolia-Application-Id",
ALGOLIA_APP_ID,
"X-Algolia-API-Key",
key,
),
)
.awaitSuccess()
.parseAs<KitsuAlgoliaSearchResult>()
.hits
.filter { it.subtype != "novel" }
.map { it.toTrack() }
}
body = jsonObject.toString().toRequestBody(jsonMime),
),
)
.awaitSuccess()
.parseAs<KitsuAlgoliaSearchResult>()
.hits
.filter { it.subtype != "novel" }
.map { it.toTrack() }
}
}
@ -175,18 +171,16 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
.encodedQuery("filter[manga_id]=${track.media_id}&filter[user_id]=$userId")
.appendQueryParameter("include", "manga")
.build()
with(json) {
authClient.newCall(GET(url.toString()))
.awaitSuccess()
.parseAs<KitsuListSearchResult>()
.let {
if (it.data.isNotEmpty() && it.included.isNotEmpty()) {
it.firstToTrack()
} else {
null
}
authClient.newCall(GET(url.toString()))
.awaitSuccess()
.parseAs<KitsuListSearchResult>()
.let {
if (it.data.isNotEmpty() && it.included.isNotEmpty()) {
it.firstToTrack()
} else {
null
}
}
}
}
}
@ -196,18 +190,16 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
.encodedQuery("filter[id]=${track.media_id}")
.appendQueryParameter("include", "manga")
.build()
with(json) {
authClient.newCall(GET(url.toString()))
.awaitSuccess()
.parseAs<KitsuListSearchResult>()
.let {
if (it.data.isNotEmpty() && it.included.isNotEmpty()) {
it.firstToTrack()
} else {
throw Exception("Could not find manga")
}
authClient.newCall(GET(url.toString()))
.awaitSuccess()
.parseAs<KitsuListSearchResult>()
.let {
if (it.data.isNotEmpty() && it.included.isNotEmpty()) {
it.firstToTrack()
} else {
throw Exception("Could not find manga")
}
}
}
}
}
@ -220,11 +212,9 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
.add("client_id", CLIENT_ID)
.add("client_secret", CLIENT_SECRET)
.build()
with(json) {
client.newCall(POST(LOGIN_URL, body = formBody))
.awaitSuccess()
.parseAs()
}
client.newCall(POST(LOGIN_URL, body = formBody))
.awaitSuccess()
.parseAs()
}
}
@ -233,13 +223,11 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
val url = "${BASE_URL}users".toUri().buildUpon()
.encodedQuery("filter[self]=true")
.build()
with(json) {
authClient.newCall(GET(url.toString()))
.awaitSuccess()
.parseAs<KitsuCurrentUserResult>()
.data[0]
.id
}
authClient.newCall(GET(url.toString()))
.awaitSuccess()
.parseAs<KitsuCurrentUserResult>()
.data[0]
.id
}
}

View file

@ -26,21 +26,19 @@ class KomgaApi(private val client: OkHttpClient) {
withIOContext {
try {
val track =
with(json) {
if (url.contains(READLIST_API)) {
client.newCall(GET(url))
.awaitSuccess()
.parseAs<ReadListDto>()
.toTrack()
} else {
client.newCall(GET(url))
.awaitSuccess()
.parseAs<SeriesDto>()
.toTrack()
}
if (url.contains(READLIST_API)) {
client.newCall(GET(url))
.awaitSuccess()
.parseAs<ReadListDto>()
.toTrack()
} else {
client.newCall(GET(url))
.awaitSuccess()
.parseAs<SeriesDto>()
.toTrack()
}
val progress = with(json) {
val progress =
client
.newCall(
GET(
@ -59,7 +57,6 @@ class KomgaApi(private val client: OkHttpClient) {
it.parseAs<ReadProgressDto>().toV2()
}
}
}
track.apply {
cover_url = "$url/thumbnail"
tracking_url = url

View file

@ -37,15 +37,13 @@ class MangaUpdatesApi(
suspend fun getSeriesListItem(track: Track): Pair<MUListItem, MURating?> {
val listItem =
with(json) {
authClient.newCall(
GET(
url = "$BASE_URL/v1/lists/series/${track.media_id}",
),
)
.awaitSuccess()
.parseAs<MUListItem>()
}
authClient.newCall(
GET(
url = "$BASE_URL/v1/lists/series/${track.media_id}",
),
)
.awaitSuccess()
.parseAs<MUListItem>()
val rating = getSeriesRating(track)
@ -104,15 +102,13 @@ class MangaUpdatesApi(
private suspend fun getSeriesRating(track: Track): MURating? {
return try {
with(json) {
authClient.newCall(
GET(
url = "$BASE_URL/v1/series/${track.media_id}/rating",
),
)
.awaitSuccess()
.parseAs<MURating>()
}
authClient.newCall(
GET(
url = "$BASE_URL/v1/series/${track.media_id}/rating",
),
)
.awaitSuccess()
.parseAs<MURating>()
} catch (e: Exception) {
null
}
@ -151,18 +147,16 @@ class MangaUpdatesApi(
},
)
}
return with(json) {
client.newCall(
POST(
url = "$BASE_URL/v1/series/search",
body = body.toString().toRequestBody(CONTENT_TYPE),
),
)
.awaitSuccess()
.parseAs<MUSearchResult>()
.results
.map { it.record }
}
return client.newCall(
POST(
url = "$BASE_URL/v1/series/search",
body = body.toString().toRequestBody(CONTENT_TYPE),
),
)
.awaitSuccess()
.parseAs<MUSearchResult>()
.results
.map { it.record }
}
suspend fun authenticate(username: String, password: String): MUContext? {
@ -170,17 +164,15 @@ class MangaUpdatesApi(
put("username", username)
put("password", password)
}
return with(json) {
client.newCall(
PUT(
url = "$BASE_URL/v1/account/login",
body = body.toString().toRequestBody(CONTENT_TYPE),
),
)
.awaitSuccess()
.parseAs<MULoginResponse>()
.context
}
return client.newCall(
PUT(
url = "$BASE_URL/v1/account/login",
body = body.toString().toRequestBody(CONTENT_TYPE),
),
)
.awaitSuccess()
.parseAs<MULoginResponse>()
.context
}
companion object {

View file

@ -45,11 +45,9 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
.add("code_verifier", codeVerifier)
.add("grant_type", "authorization_code")
.build()
with(json) {
client.newCall(POST("$BASE_OAUTH_URL/token", body = formBody))
.awaitSuccess()
.parseAs()
}
client.newCall(POST("$BASE_OAUTH_URL/token", body = formBody))
.awaitSuccess()
.parseAs()
}
}
@ -59,12 +57,10 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
.url("$BASE_API_URL/users/@me")
.get()
.build()
with(json) {
authClient.newCall(request)
.awaitSuccess()
.parseAs<MALUser>()
.name
}
authClient.newCall(request)
.awaitSuccess()
.parseAs<MALUser>()
.name
}
}
@ -75,15 +71,13 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
.appendQueryParameter("q", query.take(64))
.appendQueryParameter("nsfw", "true")
.build()
with(json) {
authClient.newCall(GET(url.toString()))
.awaitSuccess()
.parseAs<MALSearchResult>()
.data
.map { async { getMangaDetails(it.node.id) } }
.awaitAll()
.filter { !it.publishing_type.contains("novel") }
}
authClient.newCall(GET(url.toString()))
.awaitSuccess()
.parseAs<MALSearchResult>()
.data
.map { async { getMangaDetails(it.node.id) } }
.awaitAll()
.filter { !it.publishing_type.contains("novel") }
}
}
@ -93,24 +87,22 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
.appendPath(id.toString())
.appendQueryParameter("fields", "id,title,synopsis,num_chapters,main_picture,status,media_type,start_date")
.build()
with(json) {
authClient.newCall(GET(url.toString()))
.awaitSuccess()
.parseAs<MALManga>()
.let {
TrackSearch.create(TrackManager.MYANIMELIST).apply {
media_id = it.id
title = it.title
summary = it.synopsis
total_chapters = it.numChapters
cover_url = it.covers.large
tracking_url = "https://myanimelist.net/manga/$media_id"
publishing_status = it.status.replace("_", " ")
publishing_type = it.mediaType.replace("_", " ")
start_date = it.startDate ?: ""
}
authClient.newCall(GET(url.toString()))
.awaitSuccess()
.parseAs<MALManga>()
.let {
TrackSearch.create(TrackManager.MYANIMELIST).apply {
media_id = it.id
title = it.title
summary = it.synopsis
total_chapters = it.numChapters
cover_url = it.covers.large
tracking_url = "https://myanimelist.net/manga/$media_id"
publishing_status = it.status.replace("_", " ")
publishing_type = it.mediaType.replace("_", " ")
start_date = it.startDate ?: ""
}
}
}
}
}
@ -132,12 +124,10 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
.url(mangaUrl(track.media_id).toString())
.put(formBodyBuilder.build())
.build()
with(json) {
authClient.newCall(request)
.awaitSuccess()
.parseAs<MALListItemStatus>()
.let { parseMangaItem(it, track) }
}
authClient.newCall(request)
.awaitSuccess()
.parseAs<MALListItemStatus>()
.let { parseMangaItem(it, track) }
}
}
@ -147,15 +137,13 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
.appendPath(track.media_id.toString())
.appendQueryParameter("fields", "num_chapters,my_list_status{start_date,finish_date}")
.build()
with(json) {
authClient.newCall(GET(uri.toString()))
.awaitSuccess()
.parseAs<MALListItem>()
.let { item ->
track.total_chapters = item.numChapters
item.myListStatus?.let { parseMangaItem(it, track) }
}
}
authClient.newCall(GET(uri.toString()))
.awaitSuccess()
.parseAs<MALListItem>()
.let { item ->
track.total_chapters = item.numChapters
item.myListStatus?.let { parseMangaItem(it, track) }
}
}
}
@ -190,11 +178,9 @@ class MyAnimeListApi(private val client: OkHttpClient, interceptor: MyAnimeListI
.url(urlBuilder.build().toString())
.get()
.build()
with(json) {
authClient.newCall(request)
.awaitSuccess()
.parseAs()
}
authClient.newCall(request)
.awaitSuccess()
.parseAs()
}
}

View file

@ -66,7 +66,7 @@ class MyAnimeListInterceptor(private val myanimelist: MyAnimeList) : Interceptor
return runCatching {
if (response.isSuccessful) {
with(json) { response.parseAs<MALOAuth>() }
response.parseAs<MALOAuth>()
} else {
response.close()
null

View file

@ -74,12 +74,10 @@ class ShikimoriApi(
.appendQueryParameter("search", search)
.appendQueryParameter("limit", "20")
.build()
with(json) {
authClient.newCall(GET(url.toString()))
.awaitSuccess()
.parseAs<List<SMManga>>()
.map { it.toTrack(trackId) }
}
authClient.newCall(GET(url.toString()))
.awaitSuccess()
.parseAs<List<SMManga>>()
.map { it.toTrack(trackId) }
}
}
@ -102,51 +100,44 @@ class ShikimoriApi(
val urlMangas = "$API_URL/mangas".toUri().buildUpon()
.appendPath(track.media_id.toString())
.build()
val manga = with(json) {
val manga =
authClient.newCall(GET(urlMangas.toString()))
.awaitSuccess()
.parseAs<SMManga>()
}
val url = "$API_URL/v2/user_rates".toUri().buildUpon()
.appendQueryParameter("user_id", user_id)
.appendQueryParameter("target_id", track.media_id.toString())
.appendQueryParameter("target_type", "Manga")
.build()
with(json) {
authClient.newCall(GET(url.toString()))
.execute()
.parseAs<List<SMUserListEntry>>()
.let { entries ->
if (entries.size > 1) {
throw Exception("Too manga manga in response")
}
entries
.map { it.toTrack(trackId, manga) }
.firstOrNull()
authClient.newCall(GET(url.toString()))
.execute()
.parseAs<List<SMUserListEntry>>()
.let { entries ->
if (entries.size > 1) {
throw Exception("Too manga manga in response")
}
}
entries
.map { it.toTrack(trackId, manga) }
.firstOrNull()
}
}
}
suspend fun getCurrentUser(): Int {
return withIOContext {
with(json) {
authClient.newCall(GET("$API_URL/users/whoami"))
.awaitSuccess()
.parseAs<SMUser>()
.id
}
authClient.newCall(GET("$API_URL/users/whoami"))
.awaitSuccess()
.parseAs<SMUser>()
.id
}
}
suspend fun accessToken(code: String): SMOAuth {
return withIOContext {
with(json) {
client.newCall(accessTokenRequest(code))
.awaitSuccess()
.parseAs()
}
client.newCall(accessTokenRequest(code))
.awaitSuccess()
.parseAs()
}
}

View file

@ -52,9 +52,7 @@ class TachideskApi {
trackUrl
}
val manga = with(json) {
client.newCall(GET("$url/full", headers)).awaitSuccess().parseAs<MangaDataClass>()
}
val manga = client.newCall(GET("$url/full", headers)).awaitSuccess().parseAs<MangaDataClass>()
TrackSearch.create(TrackManager.SUWAYOMI).apply {
title = manga.title
@ -74,9 +72,7 @@ class TachideskApi {
suspend fun updateProgress(track: Track): Track {
val url = track.tracking_url
val chapters = with(json) {
client.newCall(GET("$url/chapters", headers)).awaitSuccess().parseAs<List<ChapterDataClass>>()
}
val chapters = client.newCall(GET("$url/chapters", headers)).awaitSuccess().parseAs<List<ChapterDataClass>>()
val lastChapterIndex = chapters.first { it.chapterNumber == track.last_chapter_read }.index
client.newCall(

View file

@ -11,12 +11,12 @@ import eu.kanade.tachiyomi.network.await
import eu.kanade.tachiyomi.network.parseAs
import eu.kanade.tachiyomi.util.system.localeContext
import eu.kanade.tachiyomi.util.system.withIOContext
import java.util.Date
import java.util.concurrent.TimeUnit
import kotlinx.serialization.json.Json
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import yokai.domain.base.models.Version
import java.util.*
import java.util.concurrent.*
class AppUpdateChecker(
private val json: Json = Injekt.get(),
@ -31,48 +31,46 @@ class AppUpdateChecker(
}
return withIOContext {
val result = with(json) {
if (preferences.checkForBetas().get()) {
networkService.client
.newCall(GET("https://api.github.com/repos/$GITHUB_REPO/releases"))
.await()
.parseAs<List<GithubRelease>>()
.let { githubReleases ->
val releases =
githubReleases.take(10).filter { isNewVersion(it.version) }
// Check if any of the latest versions are newer than the current version
val release = releases
.maxWithOrNull { r1, r2 ->
when {
r1.version == r2.version -> 0
isNewVersion(r2.version, r1.version) -> -1
else -> 1
}
val result = if (preferences.checkForBetas().get()) {
networkService.client
.newCall(GET("https://api.github.com/repos/$GITHUB_REPO/releases"))
.await()
.parseAs<List<GithubRelease>>()
.let { githubReleases ->
val releases =
githubReleases.take(10).filter { isNewVersion(it.version) }
// Check if any of the latest versions are newer than the current version
val release = releases
.maxWithOrNull { r1, r2 ->
when {
r1.version == r2.version -> 0
isNewVersion(r2.version, r1.version) -> -1
else -> 1
}
preferences.lastAppCheck().set(Date().time)
if (release != null) {
AppUpdateResult.NewUpdate(release)
} else {
AppUpdateResult.NoNewUpdate
}
}
} else {
networkService.client
.newCall(GET("https://api.github.com/repos/$GITHUB_REPO/releases/latest"))
.await()
.parseAs<GithubRelease>()
.let {
preferences.lastAppCheck().set(Date().time)
preferences.lastAppCheck().set(Date().time)
// Check if latest version is newer than the current version
if (isNewVersion(it.version)) {
AppUpdateResult.NewUpdate(it)
} else {
AppUpdateResult.NoNewUpdate
}
if (release != null) {
AppUpdateResult.NewUpdate(release)
} else {
AppUpdateResult.NoNewUpdate
}
}
}
} else {
networkService.client
.newCall(GET("https://api.github.com/repos/$GITHUB_REPO/releases/latest"))
.await()
.parseAs<GithubRelease>()
.let {
preferences.lastAppCheck().set(Date().time)
// Check if latest version is newer than the current version
if (isNewVersion(it.version)) {
AppUpdateResult.NewUpdate(it)
} else {
AppUpdateResult.NoNewUpdate
}
}
}
if (doExtrasAfterNewUpdate && result is AppUpdateResult.NewUpdate) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S &&

View file

@ -14,7 +14,6 @@ import eu.kanade.tachiyomi.util.system.withIOContext
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
@ -24,7 +23,6 @@ import yokai.domain.extension.repo.model.ExtensionRepo
internal class ExtensionApi {
private val json: Json by injectLazy()
private val networkService: NetworkHelper by injectLazy()
private val getExtensionRepo: GetExtensionRepo by injectLazy()
private val updateExtensionRepo: UpdateExtensionRepo by injectLazy()
@ -47,11 +45,9 @@ internal class ExtensionApi {
.newCall(GET("$repoBaseUrl/index.min.json"))
.awaitSuccess()
with(json) {
response
.parseAs<List<ExtensionJsonObject>>()
.toExtensions(repoBaseUrl)
}
response
.parseAs<List<ExtensionJsonObject>>()
.toExtensions(repoBaseUrl)
} catch (e: Throwable) {
Logger.e(e) { "Failed to get extensions from $repoBaseUrl" }
emptyList()

View file

@ -25,6 +25,7 @@ import eu.kanade.tachiyomi.util.chapter.ChapterFilter
import eu.kanade.tachiyomi.util.manga.MangaShortcutManager
import io.requery.android.database.sqlite.RequerySQLiteOpenHelperFactory
import kotlinx.serialization.json.Json
import kotlinx.serialization.protobuf.ProtoBuf
import nl.adaptivity.xmlutil.XmlDeclMode
import nl.adaptivity.xmlutil.core.XmlVersion
import nl.adaptivity.xmlutil.serialization.XML
@ -145,6 +146,9 @@ fun appModule(app: Application) = module {
xmlVersion = XmlVersion.XML10
}
}
single<ProtoBuf> {
ProtoBuf
}
single { ChapterFilter() }

View file

@ -6,17 +6,13 @@ import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.awaitSuccess
import eu.kanade.tachiyomi.network.parseAs
import eu.kanade.tachiyomi.util.system.withIOContext
import kotlinx.serialization.json.Json
import okhttp3.OkHttpClient
import uy.kohesive.injekt.injectLazy
import yokai.domain.extension.repo.model.ExtensionRepo
class ExtensionRepoService(
private val client: OkHttpClient,
) {
private val json: Json by injectLazy()
suspend fun fetchRepoDetails(
repo: String,
): ExtensionRepo? {
@ -24,12 +20,10 @@ class ExtensionRepoService(
val url = "$repo/repo.json".toUri()
try {
with(json) {
client.newCall(GET(url.toString()))
.awaitSuccess()
.parseAs<ExtensionRepoMetaDto>()
.toExtensionRepo(baseUrl = repo)
}
client.newCall(GET(url.toString()))
.awaitSuccess()
.parseAs<ExtensionRepoMetaDto>()
.toExtensionRepo(baseUrl = repo)
} catch (e: Exception) {
Logger.e(e) { "Failed to fetch repo details" }
null

View file

@ -66,7 +66,6 @@ android {
tasks {
withType<KotlinCompile> {
compilerOptions.freeCompilerArgs.addAll(
"-Xcontext-receivers",
"-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
"-opt-in=kotlinx.serialization.ExperimentalSerializationApi",
)

View file

@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.network
import java.io.IOException
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.coroutines.resumeWithException
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.serialization.DeserializationStrategy
import kotlinx.serialization.json.Json
@ -18,6 +17,8 @@ import okhttp3.Response
import rx.Observable
import rx.Producer
import rx.Subscription
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
val jsonMime = "application/json; charset=utf-8".toMediaType()
@ -68,7 +69,6 @@ fun Call.asObservableSuccess(): Observable<Response> {
}
// Based on https://github.com/gildor/kotlin-coroutines-okhttp
@OptIn(ExperimentalCoroutinesApi::class)
private suspend fun Call.await(callStack: Array<StackTraceElement>): Response {
return suspendCancellableCoroutine { continuation ->
val callback =
@ -131,13 +131,11 @@ fun OkHttpClient.newCachelessCallWithProgress(request: Request, listener: Progre
return progressClient.newCall(request)
}
context(Json)
inline fun <reified T> Response.parseAs(): T {
return decodeFromJsonResponse(serializer(), this)
return Injekt.get<Json>().decodeFromJsonResponse(serializer(), this)
}
context(Json)
fun <T> decodeFromJsonResponse(
fun <T> Json.decodeFromJsonResponse(
deserializer: DeserializationStrategy<T>,
response: Response,
): T {