Show usernames for each tracker in settings (#3533)

This commit is contained in:
MajorTanya
2026-07-07 12:31:22 +02:00
committed by GitHub
parent 17aed02e56
commit d0c79399b9
24 changed files with 102 additions and 33 deletions
@@ -68,6 +68,10 @@ abstract class BaseTracker(
override fun getUsername() = trackPreferences.trackUsername(this).get()
override fun getDisplayUsername(): String = trackPreferences.trackDisplayUsername(this).get()
override fun saveDisplayUsername(displayName: String) = trackPreferences.trackDisplayUsername(this).set(displayName)
override fun getPassword() = trackPreferences.trackPassword(this).get()
override fun saveCredentials(username: String, password: String) {
@@ -65,6 +65,10 @@ interface Tracker {
fun getPassword(): String
fun getDisplayUsername(): String
fun saveDisplayUsername(displayName: String)
fun saveCredentials(username: String, password: String)
// TODO: move this to an interactor, and update all trackers based on common data
@@ -213,9 +213,10 @@ class Anilist(id: Long) : BaseTracker(id, "AniList"), DeletableTracker {
try {
val oauth = api.createOAuth(token)
interceptor.setAuth(oauth)
val (username, scoreType) = api.getCurrentUser()
scorePreference.set(scoreType)
saveCredentials(username.toString(), oauth.accessToken)
val currentUser = api.getCurrentUser()
scorePreference.set(currentUser.mediaListOptions.scoreFormat)
saveDisplayUsername(currentUser.name)
saveCredentials(currentUser.id.toString(), oauth.accessToken)
} catch (e: Throwable) {
logout()
}
@@ -8,6 +8,7 @@ import eu.kanade.tachiyomi.data.track.anilist.dto.ALCurrentUserResult
import eu.kanade.tachiyomi.data.track.anilist.dto.ALOAuth
import eu.kanade.tachiyomi.data.track.anilist.dto.ALSearchResult
import eu.kanade.tachiyomi.data.track.anilist.dto.ALUserListMangaQueryResult
import eu.kanade.tachiyomi.data.track.anilist.dto.ALUserViewerData
import eu.kanade.tachiyomi.data.track.model.TrackSearch
import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.network.awaitSuccess
@@ -285,12 +286,13 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
return ALOAuth(token, "Bearer", System.currentTimeMillis() + 31536000000, 31536000000)
}
suspend fun getCurrentUser(): Pair<Int, String> {
suspend fun getCurrentUser(): ALUserViewerData {
return withIOContext {
val query = """
|query User {
|Viewer {
|id
|name
|mediaListOptions {
|scoreFormat
|}
@@ -310,10 +312,7 @@ class AnilistApi(val client: OkHttpClient, interceptor: AnilistInterceptor) {
)
.awaitSuccess()
.parseAs<ALCurrentUserResult>()
.let {
val viewer = it.data.viewer
Pair(viewer.id, viewer.mediaListOptions.scoreFormat)
}
.data.viewer
}
}
}
@@ -17,6 +17,7 @@ data class ALUserViewer(
@Serializable
data class ALUserViewerData(
val id: Int,
val name: String,
val mediaListOptions: ALUserListOptions,
)
@@ -106,8 +106,9 @@ class Bangumi(id: Long) : BaseTracker(id, "Bangumi") {
// Users can set a 'username' (not nickname) once which effectively
// replaces the stringified ID in certain queries.
// If no username is set, the API returns the user ID as a strings
val username = api.getUsername()
saveCredentials(username, oauth.accessToken)
val currentUser = api.getCurrentUser()
saveDisplayUsername(currentUser.nickname?.takeIf { it.isNotBlank() } ?: currentUser.username)
saveCredentials(currentUser.username, oauth.accessToken)
} catch (_: Throwable) {
logout()
}
@@ -153,13 +153,12 @@ class BangumiApi(
}
}
suspend fun getUsername(): String {
suspend fun getCurrentUser(): BGMUser {
return withIOContext {
with(json) {
authClient.newCall(GET("$API_URL/v0/me"))
.awaitSuccess()
.parseAs<BGMUser>()
.username
}
}
}
@@ -6,4 +6,5 @@ import kotlinx.serialization.Serializable
// Incomplete DTO with only our needed attributes
data class BGMUser(
val username: String,
val nickname: String?,
)
@@ -144,6 +144,7 @@ class Hikka(id: Long) : BaseTracker(id, "Hikka"), DeletableTracker {
val oauth = api.accessToken(reference)
interceptor.setAuth(oauth)
val user = api.getCurrentUser()
saveDisplayUsername(user.username)
saveCredentials(user.reference, oauth.accessToken)
} catch (_: Throwable) {
logout()
@@ -127,8 +127,9 @@ class Kitsu(id: Long) : BaseTracker(id, "Kitsu"), DeletableTracker {
override suspend fun login(username: String, password: String) {
val token = api.login(username, password)
interceptor.newAuth(token)
val userId = api.getCurrentUser()
saveCredentials(username, userId)
val currentUser = api.getCurrentUser()
saveDisplayUsername(currentUser.attributes.name)
saveCredentials(username, currentUser.id)
}
override fun logout() {
@@ -8,6 +8,7 @@ import eu.kanade.tachiyomi.data.track.kitsu.dto.KitsuCurrentUserResult
import eu.kanade.tachiyomi.data.track.kitsu.dto.KitsuListSearchResult
import eu.kanade.tachiyomi.data.track.kitsu.dto.KitsuOAuth
import eu.kanade.tachiyomi.data.track.kitsu.dto.KitsuSearchResult
import eu.kanade.tachiyomi.data.track.kitsu.dto.KitsuUser
import eu.kanade.tachiyomi.data.track.model.TrackSearch
import eu.kanade.tachiyomi.network.DELETE
import eu.kanade.tachiyomi.network.GET
@@ -227,7 +228,7 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
}
}
suspend fun getCurrentUser(): String {
suspend fun getCurrentUser(): KitsuUser {
return withIOContext {
val url = "${BASE_URL}users".toUri().buildUpon()
.encodedQuery("filter[self]=true")
@@ -237,7 +238,6 @@ class KitsuApi(private val client: OkHttpClient, interceptor: KitsuInterceptor)
.awaitSuccess()
.parseAs<KitsuCurrentUserResult>()
.data[0]
.id
}
}
}
@@ -10,4 +10,10 @@ data class KitsuCurrentUserResult(
@Serializable
data class KitsuUser(
val id: String,
val attributes: KitsuUserAttributes,
)
@Serializable
data class KitsuUserAttributes(
val name: String,
)
@@ -107,8 +107,10 @@ class MangaUpdates(id: Long) : BaseTracker(id, "MangaUpdates"), DeletableTracker
override suspend fun login(username: String, password: String) {
val authenticated = api.authenticate(username, password)
saveCredentials(authenticated.uid.toString(), authenticated.sessionToken)
interceptor.newAuth(authenticated.sessionToken)
val currentUser = api.getCurrentUser()
saveDisplayUsername(currentUser.username)
saveCredentials(authenticated.uid.toString(), authenticated.sessionToken)
}
fun restoreSession(): String? {
@@ -4,6 +4,7 @@ import eu.kanade.tachiyomi.data.database.models.Track
import eu.kanade.tachiyomi.data.track.mangaupdates.MangaUpdates.Companion.READING_LIST
import eu.kanade.tachiyomi.data.track.mangaupdates.MangaUpdates.Companion.WISH_LIST
import eu.kanade.tachiyomi.data.track.mangaupdates.dto.MUContext
import eu.kanade.tachiyomi.data.track.mangaupdates.dto.MUCurrentUser
import eu.kanade.tachiyomi.data.track.mangaupdates.dto.MUListItem
import eu.kanade.tachiyomi.data.track.mangaupdates.dto.MULoginResponse
import eu.kanade.tachiyomi.data.track.mangaupdates.dto.MURating
@@ -190,6 +191,14 @@ class MangaUpdatesApi(
}
}
suspend fun getCurrentUser(): MUCurrentUser {
return with(json) {
authClient.newCall(GET("$BASE_URL/v1/account/profile"))
.awaitSuccess()
.parseAs<MUCurrentUser>()
}
}
companion object {
private const val BASE_URL = "https://api.mangaupdates.com"
@@ -0,0 +1,8 @@
package eu.kanade.tachiyomi.data.track.mangaupdates.dto
import kotlinx.serialization.Serializable
@Serializable
data class MUCurrentUser(
val username: String,
)
@@ -137,6 +137,7 @@ class MyAnimeList(id: Long) : BaseTracker(id, "MyAnimeList"), DeletableTracker {
val oauth = api.getAccessToken(authCode)
interceptor.setAuth(oauth)
val username = api.getCurrentUser()
saveDisplayUsername(username)
saveCredentials(username, oauth.accessToken)
} catch (e: Throwable) {
logout()
@@ -122,7 +122,8 @@ class Shikimori(id: Long) : BaseTracker(id, "Shikimori"), DeletableTracker {
val oauth = api.accessToken(code)
interceptor.newAuth(oauth)
val user = api.getCurrentUser()
saveCredentials(user.toString(), oauth.accessToken)
saveDisplayUsername(user.nickname)
saveCredentials(user.id, oauth.accessToken)
} catch (e: Throwable) {
logout()
}
@@ -7,6 +7,7 @@ import eu.kanade.tachiyomi.data.track.model.TrackSearch
import eu.kanade.tachiyomi.data.track.shikimori.dto.SMAddMangaResponse
import eu.kanade.tachiyomi.data.track.shikimori.dto.SMOAuth
import eu.kanade.tachiyomi.data.track.shikimori.dto.SMSearchResult
import eu.kanade.tachiyomi.data.track.shikimori.dto.SMUser
import eu.kanade.tachiyomi.data.track.shikimori.dto.SMUserListResult
import eu.kanade.tachiyomi.data.track.shikimori.dto.SMUserResult
import eu.kanade.tachiyomi.network.DELETE
@@ -171,12 +172,13 @@ class ShikimoriApi(
}
}
suspend fun getCurrentUser(): Int {
suspend fun getCurrentUser(): SMUser {
return with(json) {
val query = """
|{
|currentUser {
|id
|nickname
|}
|}
""".trimMargin()
@@ -191,8 +193,7 @@ class ShikimoriApi(
)
.awaitSuccess()
.parseAs<SMUserResult>()
.data.currentUser.id
.toInt()
.data.currentUser
}
}
@@ -15,4 +15,5 @@ data class SMCurrentUser(
@Serializable
data class SMUser(
val id: String,
val nickname: String,
)