Show usernames for each tracker in settings (#3533)
This commit is contained in:
@@ -16,6 +16,11 @@ class TrackPreferences(
|
||||
"",
|
||||
)
|
||||
|
||||
fun trackDisplayUsername(tracker: Tracker) = preferenceStore.getString(
|
||||
Preference.privateKey("pref_mangasync_displayname_${tracker.id}"),
|
||||
"",
|
||||
)
|
||||
|
||||
fun trackPassword(tracker: Tracker) = preferenceStore.getString(
|
||||
Preference.privateKey("pref_mangasync_password_${tracker.id}"),
|
||||
"",
|
||||
|
||||
@@ -171,7 +171,7 @@ internal fun PreferenceItem(
|
||||
}
|
||||
TrackingPreferenceWidget(
|
||||
tracker = item.tracker,
|
||||
checked = isLoggedIn,
|
||||
isLoggedIn = isLoggedIn,
|
||||
onClick = { if (isLoggedIn) item.logout() else item.login() },
|
||||
)
|
||||
}
|
||||
|
||||
+27
-11
@@ -2,6 +2,7 @@ package eu.kanade.presentation.more.settings.widget
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
@@ -15,6 +16,8 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import eu.kanade.presentation.more.settings.LocalPreferenceHighlighted
|
||||
import eu.kanade.presentation.track.components.TrackLogoIcon
|
||||
@@ -26,7 +29,7 @@ import tachiyomi.presentation.core.i18n.stringResource
|
||||
fun TrackingPreferenceWidget(
|
||||
modifier: Modifier = Modifier,
|
||||
tracker: Tracker,
|
||||
checked: Boolean,
|
||||
isLoggedIn: Boolean,
|
||||
onClick: (() -> Unit)? = null,
|
||||
) {
|
||||
val highlighted = LocalPreferenceHighlighted.current
|
||||
@@ -39,16 +42,29 @@ fun TrackingPreferenceWidget(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
TrackLogoIcon(tracker)
|
||||
Text(
|
||||
text = tracker.name,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(horizontal = 16.dp),
|
||||
maxLines = 1,
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
fontSize = TitleFontSize,
|
||||
)
|
||||
if (checked) {
|
||||
Column(
|
||||
modifier = Modifier.weight(1f),
|
||||
) {
|
||||
Text(
|
||||
text = tracker.name,
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
maxLines = 1,
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
fontSize = TitleFontSize,
|
||||
fontWeight = FontWeight.Medium,
|
||||
)
|
||||
val displayName = tracker.getDisplayUsername()
|
||||
if (isLoggedIn && displayName.isNotBlank()) {
|
||||
Text(
|
||||
text = displayName,
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
maxLines = 1,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
}
|
||||
if (isLoggedIn) {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Done,
|
||||
modifier = Modifier
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -81,6 +81,10 @@ data class DummyTracker(
|
||||
|
||||
override fun getUsername(): String = "username"
|
||||
|
||||
override fun getDisplayUsername(): String = "UserName"
|
||||
|
||||
override fun saveDisplayUsername(displayName: String): Unit = Unit
|
||||
|
||||
override fun getPassword(): String = "passw0rd"
|
||||
|
||||
override fun saveCredentials(username: String, password: String) = Unit
|
||||
|
||||
Reference in New Issue
Block a user