From 92c91d7018682106196fbe682a64a86179eca4d5 Mon Sep 17 00:00:00 2001 From: imashnake0 Date: Wed, 5 Aug 2026 13:57:27 -0400 Subject: [PATCH 01/10] add score format pref and udpate --- .../src/main/graphql/fragments/User.graphql | 1 + .../main/graphql/mutations/UpdateUser.graphql | 3 ++ .../api/anilist/AnilistUserRepository.kt | 3 ++ .../api/anilist/sanitize/profile/Viewer.kt | 2 +- .../api/preferences/PreferencesRepository.kt | 12 ++++++ .../animite/settings/SettingsPage.kt | 38 +++++++++++++++++++ .../animite/settings/SettingsViewModel.kt | 8 ++++ .../imashnake/animite/settings/ext/EnumExt.kt | 12 ++++++ settings/src/main/res/drawable/review.xml | 24 ++++++++++++ settings/src/main/res/values/strings.xml | 1 + 10 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 settings/src/main/kotlin/com/imashnake/animite/settings/ext/EnumExt.kt create mode 100644 settings/src/main/res/drawable/review.xml diff --git a/api/anilist/src/main/graphql/fragments/User.graphql b/api/anilist/src/main/graphql/fragments/User.graphql index 315eae8f..0d8e1dcb 100644 --- a/api/anilist/src/main/graphql/fragments/User.graphql +++ b/api/anilist/src/main/graphql/fragments/User.graphql @@ -38,6 +38,7 @@ fragment User on User { } } mediaListOptions { + scoreFormat animeList { sectionOrder } diff --git a/api/anilist/src/main/graphql/mutations/UpdateUser.graphql b/api/anilist/src/main/graphql/mutations/UpdateUser.graphql index 5aedbd48..f9ecd580 100644 --- a/api/anilist/src/main/graphql/mutations/UpdateUser.graphql +++ b/api/anilist/src/main/graphql/mutations/UpdateUser.graphql @@ -1,10 +1,12 @@ mutation UpdateUser( $profileColor: String, + $scoreFormat: ScoreFormat, $animeListOptions: MediaListOptionsInput, $mangaListOptions: MediaListOptionsInput, ) { UpdateUser( profileColor: $profileColor, + scoreFormat: $scoreFormat, animeListOptions: $animeListOptions mangaListOptions: $mangaListOptions ) { @@ -12,6 +14,7 @@ mutation UpdateUser( profileColor } mediaListOptions { + scoreFormat animeList { sectionOrder } diff --git a/api/anilist/src/main/kotlin/com/imashnake/animite/api/anilist/AnilistUserRepository.kt b/api/anilist/src/main/kotlin/com/imashnake/animite/api/anilist/AnilistUserRepository.kt index 26871280..1d1c7499 100644 --- a/api/anilist/src/main/kotlin/com/imashnake/animite/api/anilist/AnilistUserRepository.kt +++ b/api/anilist/src/main/kotlin/com/imashnake/animite/api/anilist/AnilistUserRepository.kt @@ -8,6 +8,7 @@ import com.imashnake.animite.api.anilist.sanitize.media.Media import com.imashnake.animite.api.anilist.sanitize.profile.User import com.imashnake.animite.api.anilist.type.MediaListOptionsInput import com.imashnake.animite.api.anilist.type.MediaType +import com.imashnake.animite.api.anilist.type.ScoreFormat import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.filter @@ -62,6 +63,7 @@ class AnilistUserRepository( fun updateUser( profileColor: String? = null, + scoreFormat: ScoreFormat? = null, animeSectionOrder: List? = null, mangaSectionOrder: List? = null, ): Flow> { @@ -69,6 +71,7 @@ class AnilistUserRepository( .mutation( UpdateUserMutation( profileColor = Optional.presentIfNotNull(profileColor), + scoreFormat = Optional.presentIfNotNull(scoreFormat), animeListOptions = Optional.presentIfNotNull( MediaListOptionsInput( sectionOrder = Optional.presentIfNotNull(animeSectionOrder) diff --git a/api/anilist/src/main/kotlin/com/imashnake/animite/api/anilist/sanitize/profile/Viewer.kt b/api/anilist/src/main/kotlin/com/imashnake/animite/api/anilist/sanitize/profile/Viewer.kt index f1bcd746..8493825f 100644 --- a/api/anilist/src/main/kotlin/com/imashnake/animite/api/anilist/sanitize/profile/Viewer.kt +++ b/api/anilist/src/main/kotlin/com/imashnake/animite/api/anilist/sanitize/profile/Viewer.kt @@ -247,7 +247,7 @@ data class User( fun String?.sanitize() = safeValueOf(this) - fun MediaListStatus?.toTrackingStatus(type: Type): TrackingStatus = when (this) { + fun MediaListStatus?.toTrackingStatus(type: Type) = when (this) { MediaListStatus.CURRENT -> if (type == Type.ANIME) WATCHING else READING MediaListStatus.PLANNING -> if (type == Type.ANIME) PLAN_TO_WATCH else PLAN_TO_READ MediaListStatus.COMPLETED -> COMPLETED diff --git a/api/preferences/src/commonMain/kotlin/com/imashnake/animite/api/preferences/PreferencesRepository.kt b/api/preferences/src/commonMain/kotlin/com/imashnake/animite/api/preferences/PreferencesRepository.kt index 3e3c5dc8..230efa30 100644 --- a/api/preferences/src/commonMain/kotlin/com/imashnake/animite/api/preferences/PreferencesRepository.kt +++ b/api/preferences/src/commonMain/kotlin/com/imashnake/animite/api/preferences/PreferencesRepository.kt @@ -22,6 +22,7 @@ private const val SHOW_USER_DESCRIPTION = true private const val USE_PROFILE_COLOR = true private const val USE_EXPRESSIVE_PROGRESS_INDICATOR = true private const val PROFILE_COLOR = "blue" +private const val SCORE_FORMAT = "POINT_10_DECIMAL" private const val IS_DEV_OPTIONS_ENABLED = false const val DEFAULT_MEDIA_LIST_ORDER = "[]" @@ -112,6 +113,8 @@ class PreferencesRepository internal constructor( // endregion // region settings + + // TODO: Expose and set enum value. private val themeKey = stringPreferencesKey("theme") val theme = dataStore.getValue(themeKey, DEFAULT_THEME_KEY) suspend fun setTheme(theme: String) { @@ -130,6 +133,7 @@ class PreferencesRepository internal constructor( dataStore.setValue(isAmoledKey, isAmoled) } + // TODO: Expose and set enum value. private val densityKey = stringPreferencesKey("density") val density = dataStore.getValue(densityKey, DEFAULT_DENSITY_KEY) suspend fun setDensity(density: String) { @@ -142,6 +146,7 @@ class PreferencesRepository internal constructor( dataStore.setValue(isNsfwEnabledKey, isNsfwEnabled) } + // TODO: Expose and set enum value. private val languageKey = stringPreferencesKey("language") val language = dataStore.getValue(languageKey, DEFAULT_LANGUAGE_KEY) suspend fun setLanguage(language: String) { @@ -178,6 +183,13 @@ class PreferencesRepository internal constructor( dataStore.setValue(profileColorKey, profileColor) } + // TODO: Expose and set enum value. + private val scoreFormatKey = stringPreferencesKey("score_format") + val scoreFormat = dataStore.getValue(scoreFormatKey, SCORE_FORMAT) + suspend fun setScoreFormat(scoreFormat: String?) { + dataStore.setValue(scoreFormatKey, scoreFormat) + } + // region developer options private val isDevOptionsEnabledKey = booleanPreferencesKey("dev_options_enabled") val isDevOptionsEnabled = dataStore.getValue(isDevOptionsEnabledKey, IS_DEV_OPTIONS_ENABLED) diff --git a/settings/src/main/kotlin/com/imashnake/animite/settings/SettingsPage.kt b/settings/src/main/kotlin/com/imashnake/animite/settings/SettingsPage.kt index 636668d5..a5542cd8 100644 --- a/settings/src/main/kotlin/com/imashnake/animite/settings/SettingsPage.kt +++ b/settings/src/main/kotlin/com/imashnake/animite/settings/SettingsPage.kt @@ -92,6 +92,7 @@ import androidx.compose.ui.util.fastRoundToInt import androidx.core.graphics.drawable.toBitmap import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import com.imashnake.animite.api.anilist.sanitize.media.Media +import com.imashnake.animite.api.anilist.type.ScoreFormat import com.imashnake.animite.banner.BannerLayout import com.imashnake.animite.banner.MountFuji import com.imashnake.animite.core.ui.DayPart @@ -104,6 +105,7 @@ import com.imashnake.animite.core.ui.ext.horizontalOnly import com.imashnake.animite.core.ui.layout.TranslucentStatusBarLayout import com.imashnake.animite.core.ui.rememberDefaultPaddings import com.imashnake.animite.media.ext.res +import com.imashnake.animite.settings.ext.title import com.materialkolor.ktx.darken import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf @@ -151,6 +153,7 @@ fun SettingsPage( val useProfileColor by viewModel.useProfileColor.collectAsState(initial = true) val useExpressiveProgressIndicator by viewModel.useExpressiveProgressIndicator.collectAsState(initial = true) val profileColor by viewModel.profileColor.collectAsState(initial = "blue") + val scoreFormat by viewModel.scoreFormat.collectAsState(initial = ScoreFormat.POINT_10_DECIMAL.name) val isDevOptionsEnabled by viewModel.isDevOptionsEnabled.collectAsState(initial = false) @@ -569,6 +572,11 @@ fun SettingsPage( label = R.string.use_expressive_progress_indicator, orientation = Item.Orientation.HORIZONTAL ), + Item( + icon = R.drawable.review, + label = R.string.score_format, + orientation = Item.Orientation.VERTICAL + ), ), onItemClick = { index -> when (index) { @@ -762,6 +770,36 @@ fun SettingsPage( }, ) } + + 3 -> { + Row( + horizontalArrangement = Arrangement.spacedBy( + ButtonGroupDefaults.ConnectedSpaceBetween + ) + ) { + // TODO: Maybe sanitize. + ScoreFormat.entries.minus(ScoreFormat.UNKNOWN__).fastForEach { format -> + ToggleButton( + checked = scoreFormat == format.name, + onCheckedChange = { + viewModel.updateScoreFormat(format) + haptic.performHapticFeedback(HapticFeedbackType.SegmentTick) + }, + shapes = when (format) { + ScoreFormat.POINT_100 -> ButtonGroupDefaults.connectedLeadingButtonShapes() + ScoreFormat.POINT_3 -> ButtonGroupDefaults.connectedTrailingButtonShapes() + else -> ButtonGroupDefaults.connectedMiddleButtonShapes() + }, + colors = ToggleButtonDefaults.toggleButtonColors( + containerColor = MaterialTheme.colorScheme.background + ), + modifier = Modifier.weight(1f) + ) { + Text(format.title) + } + } + } + } } } } diff --git a/settings/src/main/kotlin/com/imashnake/animite/settings/SettingsViewModel.kt b/settings/src/main/kotlin/com/imashnake/animite/settings/SettingsViewModel.kt index 16605a80..097ba299 100644 --- a/settings/src/main/kotlin/com/imashnake/animite/settings/SettingsViewModel.kt +++ b/settings/src/main/kotlin/com/imashnake/animite/settings/SettingsViewModel.kt @@ -4,6 +4,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.imashnake.animite.api.anilist.AnilistUserRepository import com.imashnake.animite.api.anilist.sanitize.media.Media +import com.imashnake.animite.api.anilist.type.ScoreFormat import com.imashnake.animite.api.preferences.PreferencesRepository import com.imashnake.animite.core.model.AnimeList import com.imashnake.animite.core.model.MangaList @@ -67,6 +68,7 @@ class SettingsViewModel @Inject constructor( val useProfileColor = preferencesRepository.useProfileColor.filterNotNull() val useExpressiveProgressIndicator = preferencesRepository.useExpressiveProgressIndicator.filterNotNull() val profileColor = preferencesRepository.profileColor.filterNotNull() + val scoreFormat = preferencesRepository.scoreFormat.filterNotNull() fun updateProfileColor(profileColor: String) = viewModelScope.launch(Dispatchers.IO) { userRepository.updateUser(profileColor = profileColor).collect { @@ -74,6 +76,12 @@ class SettingsViewModel @Inject constructor( } } + fun updateScoreFormat(scoreFormat: ScoreFormat) = viewModelScope.launch(Dispatchers.IO) { + userRepository.updateUser(scoreFormat = scoreFormat).collect { + preferencesRepository.setScoreFormat(it.getOrNull()?.mediaListOptions?.scoreFormat?.name) + } + } + fun setTheme(theme: Theme) = viewModelScope.launch(Dispatchers.IO) { preferencesRepository.setTheme(theme.name) } diff --git a/settings/src/main/kotlin/com/imashnake/animite/settings/ext/EnumExt.kt b/settings/src/main/kotlin/com/imashnake/animite/settings/ext/EnumExt.kt new file mode 100644 index 00000000..a7f5de70 --- /dev/null +++ b/settings/src/main/kotlin/com/imashnake/animite/settings/ext/EnumExt.kt @@ -0,0 +1,12 @@ +package com.imashnake.animite.settings.ext + +import com.imashnake.animite.api.anilist.type.ScoreFormat + +val ScoreFormat.title get() = when(this) { + ScoreFormat.POINT_100 -> "100" + ScoreFormat.POINT_10_DECIMAL -> "10.0" + ScoreFormat.POINT_10 -> "10" + ScoreFormat.POINT_5 -> "5" + ScoreFormat.POINT_3 -> "3" + ScoreFormat.UNKNOWN__ -> "" +} \ No newline at end of file diff --git a/settings/src/main/res/drawable/review.xml b/settings/src/main/res/drawable/review.xml new file mode 100644 index 00000000..9e70bb6f --- /dev/null +++ b/settings/src/main/res/drawable/review.xml @@ -0,0 +1,24 @@ + + + + diff --git a/settings/src/main/res/values/strings.xml b/settings/src/main/res/values/strings.xml index 8a8c00d0..6366ab5c 100644 --- a/settings/src/main/res/values/strings.xml +++ b/settings/src/main/res/values/strings.xml @@ -25,6 +25,7 @@ Set profile color Use expressive progress indicator mlue + Score format About From f2a65cb32431420ab3e9d9996fbcc04df443aeeb Mon Sep 17 00:00:00 2001 From: imashnake0 Date: Wed, 5 Aug 2026 14:31:01 -0400 Subject: [PATCH 02/10] add icons --- .../animite/settings/SettingsPage.kt | 24 ++++++++++++++++++- settings/src/main/res/drawable/smile.xml | 24 +++++++++++++++++++ settings/src/main/res/drawable/star.xml | 24 +++++++++++++++++++ 3 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 settings/src/main/res/drawable/smile.xml create mode 100644 settings/src/main/res/drawable/star.xml diff --git a/settings/src/main/kotlin/com/imashnake/animite/settings/SettingsPage.kt b/settings/src/main/kotlin/com/imashnake/animite/settings/SettingsPage.kt index a5542cd8..c676c55c 100644 --- a/settings/src/main/kotlin/com/imashnake/animite/settings/SettingsPage.kt +++ b/settings/src/main/kotlin/com/imashnake/animite/settings/SettingsPage.kt @@ -795,7 +795,29 @@ fun SettingsPage( ), modifier = Modifier.weight(1f) ) { - Text(format.title) + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(LocalPaddings.current.tiny), + ) { + Text(format.title, maxLines = 1) + when(format) { + ScoreFormat.POINT_5 -> { + Icon( + imageVector = ImageVector.vectorResource(R.drawable.star), + contentDescription = null, + modifier = Modifier.size(16.dp).padding(bottom = 1.dp), + ) + } + ScoreFormat.POINT_3 -> { + Icon( + imageVector = ImageVector.vectorResource(R.drawable.smile), + contentDescription = null, + modifier = Modifier.size(16.dp).padding(bottom = 1.dp), + ) + } + else -> {} + } + } } } } diff --git a/settings/src/main/res/drawable/smile.xml b/settings/src/main/res/drawable/smile.xml new file mode 100644 index 00000000..749c4e6e --- /dev/null +++ b/settings/src/main/res/drawable/smile.xml @@ -0,0 +1,24 @@ + + + + diff --git a/settings/src/main/res/drawable/star.xml b/settings/src/main/res/drawable/star.xml new file mode 100644 index 00000000..829cf879 --- /dev/null +++ b/settings/src/main/res/drawable/star.xml @@ -0,0 +1,24 @@ + + + + From d5abe8ca17e22d2c40c252f1a9d8579213a3f2b6 Mon Sep 17 00:00:00 2001 From: imashnake0 Date: Wed, 5 Aug 2026 15:02:41 -0400 Subject: [PATCH 03/10] fetch with score format and pass to ui --- .../src/main/graphql/queries/UserQuery.graphql | 4 ++-- .../api/anilist/AnilistUserRepository.kt | 4 +++- .../api/anilist/sanitize/profile/Viewer.kt | 4 ++++ .../imashnake/animite/profile/ProfileScreen.kt | 6 ++++++ .../animite/profile/ProfileViewModel.kt | 17 ++++++++++++++--- .../com/imashnake/animite/profile/tabs/Media.kt | 9 +++++++-- .../animite/profile/ui/MediaTrackingList.kt | 2 ++ 7 files changed, 38 insertions(+), 8 deletions(-) diff --git a/api/anilist/src/main/graphql/queries/UserQuery.graphql b/api/anilist/src/main/graphql/queries/UserQuery.graphql index c39558e1..5993e3b1 100644 --- a/api/anilist/src/main/graphql/queries/UserQuery.graphql +++ b/api/anilist/src/main/graphql/queries/UserQuery.graphql @@ -22,14 +22,14 @@ query Viewer { } } -query UserMediaListQuery($userId: Int, $type: MediaType) { +query UserMediaListQuery($userId: Int, $type: MediaType, $scoreFormat: ScoreFormat) { mediaListCollection: MediaListCollection(userId: $userId, type: $type) { lists { name entries { status progress - score(format: POINT_10_DECIMAL) + score(format: $scoreFormat) media { ...MediaTracking } diff --git a/api/anilist/src/main/kotlin/com/imashnake/animite/api/anilist/AnilistUserRepository.kt b/api/anilist/src/main/kotlin/com/imashnake/animite/api/anilist/AnilistUserRepository.kt index 1d1c7499..074feafd 100644 --- a/api/anilist/src/main/kotlin/com/imashnake/animite/api/anilist/AnilistUserRepository.kt +++ b/api/anilist/src/main/kotlin/com/imashnake/animite/api/anilist/AnilistUserRepository.kt @@ -43,12 +43,14 @@ class AnilistUserRepository( type: MediaType?, useNetwork: Boolean, language: Media.Language = Media.Language.DEFAULT, + scoreFormat: ScoreFormat?, mediaListOrder: List ): Flow> { return apolloClient.query( UserMediaListQuery( userId = Optional.presentIfNotNull(id), - type = Optional.presentIfNotNull(type) + type = Optional.presentIfNotNull(type), + scoreFormat = Optional.presentIfNotNull(scoreFormat) ) ) .fetchPolicy( diff --git a/api/anilist/src/main/kotlin/com/imashnake/animite/api/anilist/sanitize/profile/Viewer.kt b/api/anilist/src/main/kotlin/com/imashnake/animite/api/anilist/sanitize/profile/Viewer.kt index 8493825f..d8dc9905 100644 --- a/api/anilist/src/main/kotlin/com/imashnake/animite/api/anilist/sanitize/profile/Viewer.kt +++ b/api/anilist/src/main/kotlin/com/imashnake/animite/api/anilist/sanitize/profile/Viewer.kt @@ -11,6 +11,7 @@ import com.imashnake.animite.api.anilist.sanitize.profile.User.TrackingStatus.Co import com.imashnake.animite.api.anilist.sanitize.profile.User.TrackingStatus.Companion.toTrackingStatus import com.imashnake.animite.api.anilist.type.MediaListStatus import com.imashnake.animite.api.anilist.type.MediaType +import com.imashnake.animite.api.anilist.type.ScoreFormat import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList import kotlin.time.Duration.Companion.minutes @@ -44,6 +45,8 @@ data class User( val banner: String?, /** @see User.Options.profileColor */ val color: String?, + /** @see User.MediaListOptions.scoreFormat */ + val scoreFormat: ScoreFormat?, // region About /** User Stats */ @@ -180,6 +183,7 @@ data class User( avatar = query.avatar?.large, banner = query.bannerImage, color = query.options?.profileColor, + scoreFormat = query.mediaListOptions?.scoreFormat, // TODO: Replace with string resources. stats = listOfNotNull( query.statistics?.anime?.count?.toString()?.let { diff --git a/profile/src/main/kotlin/com/imashnake/animite/profile/ProfileScreen.kt b/profile/src/main/kotlin/com/imashnake/animite/profile/ProfileScreen.kt index f259d716..2a3cb8ca 100644 --- a/profile/src/main/kotlin/com/imashnake/animite/profile/ProfileScreen.kt +++ b/profile/src/main/kotlin/com/imashnake/animite/profile/ProfileScreen.kt @@ -86,6 +86,7 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import coil3.compose.AsyncImage import com.imashnake.animite.api.anilist.sanitize.profile.User import com.imashnake.animite.api.anilist.sanitize.profile.User.Companion.profileColorToHex +import com.imashnake.animite.api.anilist.type.ScoreFormat import com.imashnake.animite.banner.NestedScrollBannerLayout import com.imashnake.animite.core.resource.Resource import com.imashnake.animite.core.ui.LocalPaddings @@ -146,6 +147,7 @@ fun ProfileScreen( val isLoggedIn by viewModel.isLoggedIn.collectAsState(initial = false) val useProfileColor by viewModel.useProfileColor.collectAsState(initial = true) val profileColor by viewModel.profileColor.collectAsState(initial = "blue") + val scoreFormat by viewModel.scoreFormat.collectAsState(initial = ScoreFormat.POINT_10_DECIMAL) val useExpressiveProgressIndicator by viewModel.useExpressiveProgressIndicator.collectAsState(initial = true) val viewerAvatar by viewModel.viewerAvatar.collectAsState(initial = "") val viewer by viewModel.viewer.collectAsState() @@ -272,6 +274,7 @@ fun ProfileScreen( user = this@run, animeCollection = viewerAnimeLists.data, mangaCollection = viewerMangaLists.data, + scoreFormat = scoreFormat, updateAnimeListsOrder = viewModel::updateAnimeLists, updateMangaListsOrder = viewModel::updateMangaLists, onNavigateToMediaItem = onNavigateToMediaItem, @@ -528,6 +531,7 @@ private fun UserTabs( user: User, animeCollection: User.MediaCollection?, mangaCollection: User.MediaCollection?, + scoreFormat: ScoreFormat, updateAnimeListsOrder: (List) -> Unit, updateMangaListsOrder: (List) -> Unit, onNavigateToMediaItem: (MediaPage) -> Unit, @@ -661,6 +665,7 @@ private fun UserTabs( ProfileTab.ANIME -> MediaTab( mediaCollection = animeCollection, listVisibility = animeListVisibility, + scoreFormat = scoreFormat, updateMediaListsOrder = updateAnimeListsOrder, onNavigateToMediaItem = onNavigateToMediaItem, useExpressiveProgressIndicator = useExpressiveProgressIndicator, @@ -669,6 +674,7 @@ private fun UserTabs( ProfileTab.MANGA -> MediaTab( mediaCollection = mangaCollection, listVisibility = mangaListVisibility, + scoreFormat = scoreFormat, updateMediaListsOrder = updateMangaListsOrder, onNavigateToMediaItem = onNavigateToMediaItem, useExpressiveProgressIndicator = useExpressiveProgressIndicator, diff --git a/profile/src/main/kotlin/com/imashnake/animite/profile/ProfileViewModel.kt b/profile/src/main/kotlin/com/imashnake/animite/profile/ProfileViewModel.kt index ed633ad6..814261ae 100644 --- a/profile/src/main/kotlin/com/imashnake/animite/profile/ProfileViewModel.kt +++ b/profile/src/main/kotlin/com/imashnake/animite/profile/ProfileViewModel.kt @@ -7,6 +7,7 @@ import androidx.navigation.toRoute import com.imashnake.animite.api.anilist.AnilistUserRepository import com.imashnake.animite.api.anilist.sanitize.media.Media import com.imashnake.animite.api.anilist.type.MediaType +import com.imashnake.animite.api.anilist.type.ScoreFormat import com.imashnake.animite.api.preferences.PreferencesRepository import com.imashnake.animite.core.resource.Resource import com.imashnake.animite.core.resource.Resource.Companion.asResource @@ -49,6 +50,11 @@ class ProfileViewModel @Inject constructor( val useProfileColor = preferencesRepository.useProfileColor.filterNotNull() val profileColor = preferencesRepository.profileColor.filterNotNull() + val scoreFormat = preferencesRepository + .scoreFormat + .filterNotNull() + .map { ScoreFormat.safeValueOf(it) } + val useExpressiveProgressIndicator = preferencesRepository.useExpressiveProgressIndicator.filterNotNull() val viewer = combine( @@ -63,6 +69,7 @@ class ProfileViewModel @Inject constructor( // Don't update with cache if (useNetwork) { preferencesRepository.setProfileColor(user.color) + preferencesRepository.setScoreFormat(user.scoreFormat?.name) preferencesRepository.setAnimeListOrder(user.animeListOrder) preferencesRepository.setMangaListOrder(user.mangaListOrder) } @@ -78,7 +85,8 @@ class ProfileViewModel @Inject constructor( flow2 = combine( preferencesRepository.viewerId.filterNotNull(), preferencesRepository.language.filterNotNull(), - ::Pair + scoreFormat, + ::Triple ), flow3 = preferencesRepository.animeListOrder.filterNotNull().map { Json.decodeFromString>(it) @@ -90,7 +98,8 @@ class ProfileViewModel @Inject constructor( type = MediaType.ANIME, useNetwork = useNetwork, language = Media.Language.valueOf(it.second.second), - mediaListOrder = it.third + scoreFormat = it.second.third, + mediaListOrder = it.third, ) }.asResource().stateIn( scope = viewModelScope, @@ -103,7 +112,8 @@ class ProfileViewModel @Inject constructor( flow2 = combine( preferencesRepository.viewerId.filterNotNull(), preferencesRepository.language.filterNotNull(), - ::Pair + scoreFormat, + ::Triple ), flow3 = preferencesRepository.mangaListOrder.filterNotNull().map { Json.decodeFromString>(it) @@ -115,6 +125,7 @@ class ProfileViewModel @Inject constructor( type = MediaType.MANGA, useNetwork = useNetwork, language = Media.Language.valueOf(it.second.second), + scoreFormat = it.second.third, mediaListOrder = it.third ) }.asResource().stateIn( diff --git a/profile/src/main/kotlin/com/imashnake/animite/profile/tabs/Media.kt b/profile/src/main/kotlin/com/imashnake/animite/profile/tabs/Media.kt index 67fdf90c..0a905b5e 100644 --- a/profile/src/main/kotlin/com/imashnake/animite/profile/tabs/Media.kt +++ b/profile/src/main/kotlin/com/imashnake/animite/profile/tabs/Media.kt @@ -9,6 +9,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import com.imashnake.animite.api.anilist.sanitize.media.Media import com.imashnake.animite.api.anilist.sanitize.profile.User +import com.imashnake.animite.api.anilist.type.ScoreFormat import com.imashnake.animite.core.ui.screen.FallbackScreen import com.imashnake.animite.media.MediaPage import com.imashnake.animite.profile.R @@ -25,9 +26,10 @@ import kotlinx.collections.immutable.ImmutableList fun MediaTab( mediaCollection: User.MediaCollection?, listVisibility: SnapshotStateMap, + scoreFormat: ScoreFormat, + useExpressiveProgressIndicator: Boolean, updateMediaListsOrder: (List) -> Unit, onNavigateToMediaItem: (MediaPage) -> Unit, - useExpressiveProgressIndicator: Boolean, modifier: Modifier = Modifier, contentPadding: PaddingValues = PaddingValues(), ) { @@ -50,6 +52,7 @@ fun MediaTab( type = mediaCollection.type, lists = mediaCollection.namedLists, listVisibility = listVisibility, + scoreFormat = scoreFormat, updateMediaListsOrder = updateMediaListsOrder, onNavigateToMediaItem = onNavigateToMediaItem, useExpressiveProgressIndicator = useExpressiveProgressIndicator, @@ -65,9 +68,10 @@ private fun UserMediaLists( type: Media.Small.Type, lists: ImmutableList, listVisibility: SnapshotStateMap, + scoreFormat: ScoreFormat, + useExpressiveProgressIndicator: Boolean, updateMediaListsOrder: (List) -> Unit, onNavigateToMediaItem: (MediaPage) -> Unit, - useExpressiveProgressIndicator: Boolean, modifier: Modifier = Modifier, contentPadding: PaddingValues = PaddingValues(), ) { @@ -75,6 +79,7 @@ private fun UserMediaLists( type = type, namedLists = lists, listVisibility = listVisibility, + scoreFormat = scoreFormat, updateMediaListsOrder = updateMediaListsOrder, onNavigateToMediaItem = onNavigateToMediaItem, useExpressiveProgressIndicator = useExpressiveProgressIndicator, diff --git a/profile/src/main/kotlin/com/imashnake/animite/profile/ui/MediaTrackingList.kt b/profile/src/main/kotlin/com/imashnake/animite/profile/ui/MediaTrackingList.kt index c509d402..b182af7c 100644 --- a/profile/src/main/kotlin/com/imashnake/animite/profile/ui/MediaTrackingList.kt +++ b/profile/src/main/kotlin/com/imashnake/animite/profile/ui/MediaTrackingList.kt @@ -60,6 +60,7 @@ import androidx.compose.ui.util.fastMapNotNull import com.imashnake.animite.api.anilist.sanitize.media.Media import com.imashnake.animite.api.anilist.sanitize.profile.User import com.imashnake.animite.api.anilist.sanitize.profile.User.TrackingStatus.Companion.sanitize +import com.imashnake.animite.api.anilist.type.ScoreFormat import com.imashnake.animite.core.ui.LocalPaddings import com.imashnake.animite.core.ui.component.Divider import com.imashnake.animite.core.ui.component.DropDownIcon @@ -78,6 +79,7 @@ fun MediaTrackingLists( type: Media.Small.Type, namedLists: ImmutableList, listVisibility: SnapshotStateMap, + scoreFormat: ScoreFormat, updateMediaListsOrder: (List) -> Unit, onNavigateToMediaItem: (MediaPage) -> Unit, useExpressiveProgressIndicator: Boolean, From 2657b1eafcca12064eb2e26e6ba20b7ba75ef7d3 Mon Sep 17 00:00:00 2001 From: imashnake0 Date: Wed, 5 Aug 2026 15:43:29 -0400 Subject: [PATCH 04/10] pass score format to score --- .../api/anilist/AnilistUserRepository.kt | 4 +-- .../api/anilist/sanitize/media/Media.kt | 35 ++++++++++++++----- .../api/anilist/sanitize/profile/Viewer.kt | 7 ++-- 3 files changed, 34 insertions(+), 12 deletions(-) diff --git a/api/anilist/src/main/kotlin/com/imashnake/animite/api/anilist/AnilistUserRepository.kt b/api/anilist/src/main/kotlin/com/imashnake/animite/api/anilist/AnilistUserRepository.kt index 074feafd..80ad35bf 100644 --- a/api/anilist/src/main/kotlin/com/imashnake/animite/api/anilist/AnilistUserRepository.kt +++ b/api/anilist/src/main/kotlin/com/imashnake/animite/api/anilist/AnilistUserRepository.kt @@ -43,7 +43,7 @@ class AnilistUserRepository( type: MediaType?, useNetwork: Boolean, language: Media.Language = Media.Language.DEFAULT, - scoreFormat: ScoreFormat?, + scoreFormat: ScoreFormat, mediaListOrder: List ): Flow> { return apolloClient.query( @@ -60,7 +60,7 @@ class AnilistUserRepository( ) .toFlow() .filter { it.exception == null } - .asResult { User.MediaCollection(it, type, language, mediaListOrder) } + .asResult { User.MediaCollection(it, type, language, scoreFormat, mediaListOrder) } } fun updateUser( diff --git a/api/anilist/src/main/kotlin/com/imashnake/animite/api/anilist/sanitize/media/Media.kt b/api/anilist/src/main/kotlin/com/imashnake/animite/api/anilist/sanitize/media/Media.kt index 2871489d..3e496217 100644 --- a/api/anilist/src/main/kotlin/com/imashnake/animite/api/anilist/sanitize/media/Media.kt +++ b/api/anilist/src/main/kotlin/com/imashnake/animite/api/anilist/sanitize/media/Media.kt @@ -24,6 +24,7 @@ import com.imashnake.animite.api.anilist.type.MediaSeason import com.imashnake.animite.api.anilist.type.MediaSort import com.imashnake.animite.api.anilist.type.MediaSource import com.imashnake.animite.api.anilist.type.MediaStatus +import com.imashnake.animite.api.anilist.type.ScoreFormat import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList @@ -737,6 +738,7 @@ data class Media( query: MediaTracking, progress: Int?, score: Float?, + scoreFormat: ScoreFormat, status: TrackingStatus, language: Language ) : this( @@ -755,29 +757,46 @@ data class Media( format = query.format?.sanitize(), segments = query.episodes ?: query.nextAiringEpisode?.episode ?: query.chapters, progress = progress, - score = score?.let { Score(it) }, + score = score?.let { Score(it, scoreFormat) }, status = status ) } data class Score( val value: Float, - val color: Long, + val scoreFormat: ScoreFormat, + val color: Long ) { companion object { private const val RED = 0xffff7770 private const val ORANGE = 0xffffc863 private const val LIME = 0xffb8ff70 private const val GREEN = 0xff63ff88 + + fun getColor(normalizedScore: Float) = when { + normalizedScore < 5f -> RED + normalizedScore < 7f -> ORANGE + normalizedScore < 9f -> LIME + else -> GREEN + } } - internal constructor(score: Float) : this( + internal constructor(score: Float, scoreFormat: ScoreFormat) : this( value = score, - color = when { - score < 5f -> RED - score < 7f -> ORANGE - score < 9f -> LIME - else -> GREEN + scoreFormat = scoreFormat, + color = when(scoreFormat) { + ScoreFormat.POINT_100 -> getColor(score / 100f) + ScoreFormat.POINT_10_DECIMAL, + ScoreFormat.POINT_10 -> getColor(score) + ScoreFormat.POINT_5 -> getColor(score / 5f) + ScoreFormat.POINT_3 -> when (score) { + 0f -> RED + 1f -> ORANGE + 2f -> LIME + 3f -> GREEN + else -> GREEN + } + ScoreFormat.UNKNOWN__ -> GREEN } ) } diff --git a/api/anilist/src/main/kotlin/com/imashnake/animite/api/anilist/sanitize/profile/Viewer.kt b/api/anilist/src/main/kotlin/com/imashnake/animite/api/anilist/sanitize/profile/Viewer.kt index d8dc9905..f7708d09 100644 --- a/api/anilist/src/main/kotlin/com/imashnake/animite/api/anilist/sanitize/profile/Viewer.kt +++ b/api/anilist/src/main/kotlin/com/imashnake/animite/api/anilist/sanitize/profile/Viewer.kt @@ -99,6 +99,7 @@ data class User( ) { internal constructor( query: UserMediaListQuery.List, + scoreFormat: ScoreFormat, language: Language ) : this( name = query.name, @@ -108,6 +109,7 @@ data class User( query = it?.media?.mediaTracking ?: return@mapNotNull null, progress = it.progress, score = it.score?.toFloat(), + scoreFormat = scoreFormat, language = language, status = it.status.toTrackingStatus( type = it.media.mediaTracking.type?.name?.let { type -> @@ -123,14 +125,15 @@ data class User( query: UserMediaListQuery.Data, type: MediaType?, language: Language, + scoreFormat: ScoreFormat, mediaListOrder: List ) : this( type = type?.name?.let { Type.valueOf(it) } ?: Type.UNKNOWN, namedLists = query.mediaListCollection?.lists.orEmpty().sortedBy { mediaListOrder.indexOf(it?.name) }.mapNotNull { - NamedTrackingList(it ?: return@mapNotNull null, language) - }.toImmutableList() + NamedTrackingList(it ?: return@mapNotNull null, scoreFormat, language) + }.toImmutableList(), ) } From 0391b5b2721a47ff38e4a0c1d18a0ff66807425c Mon Sep 17 00:00:00 2001 From: imashnake0 Date: Wed, 5 Aug 2026 15:45:08 -0400 Subject: [PATCH 05/10] normalize properly --- .../com/imashnake/animite/api/anilist/sanitize/media/Media.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/anilist/src/main/kotlin/com/imashnake/animite/api/anilist/sanitize/media/Media.kt b/api/anilist/src/main/kotlin/com/imashnake/animite/api/anilist/sanitize/media/Media.kt index 3e496217..655ff043 100644 --- a/api/anilist/src/main/kotlin/com/imashnake/animite/api/anilist/sanitize/media/Media.kt +++ b/api/anilist/src/main/kotlin/com/imashnake/animite/api/anilist/sanitize/media/Media.kt @@ -785,10 +785,10 @@ data class Media( value = score, scoreFormat = scoreFormat, color = when(scoreFormat) { - ScoreFormat.POINT_100 -> getColor(score / 100f) + ScoreFormat.POINT_100 -> getColor(score / 10f) ScoreFormat.POINT_10_DECIMAL, ScoreFormat.POINT_10 -> getColor(score) - ScoreFormat.POINT_5 -> getColor(score / 5f) + ScoreFormat.POINT_5 -> getColor(score * 2f) ScoreFormat.POINT_3 -> when (score) { 0f -> RED 1f -> ORANGE From d7e82e399e3ea84322b054315898b4d68e8ccd4a Mon Sep 17 00:00:00 2001 From: imashnake0 Date: Wed, 5 Aug 2026 15:46:52 -0400 Subject: [PATCH 06/10] possible floating point error --- .../animite/api/anilist/sanitize/media/Media.kt | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/api/anilist/src/main/kotlin/com/imashnake/animite/api/anilist/sanitize/media/Media.kt b/api/anilist/src/main/kotlin/com/imashnake/animite/api/anilist/sanitize/media/Media.kt index 655ff043..e06af1c6 100644 --- a/api/anilist/src/main/kotlin/com/imashnake/animite/api/anilist/sanitize/media/Media.kt +++ b/api/anilist/src/main/kotlin/com/imashnake/animite/api/anilist/sanitize/media/Media.kt @@ -33,6 +33,7 @@ import kotlinx.datetime.format.MonthNames import java.util.Locale import kotlin.collections.mapNotNull import kotlin.collections.orEmpty +import kotlin.math.roundToInt import kotlin.time.Clock import kotlin.time.Instant @@ -789,14 +790,14 @@ data class Media( ScoreFormat.POINT_10_DECIMAL, ScoreFormat.POINT_10 -> getColor(score) ScoreFormat.POINT_5 -> getColor(score * 2f) - ScoreFormat.POINT_3 -> when (score) { - 0f -> RED - 1f -> ORANGE - 2f -> LIME - 3f -> GREEN + ScoreFormat.POINT_3 -> when (score.roundToInt()) { + 0 -> RED + 1 -> ORANGE + 2 -> LIME + 3 -> GREEN else -> GREEN } - ScoreFormat.UNKNOWN__ -> GREEN + else -> GREEN } ) } From d9b9459ed232718908c272bc9b88470bfdc918d5 Mon Sep 17 00:00:00 2001 From: imashnake0 Date: Wed, 5 Aug 2026 16:12:46 -0400 Subject: [PATCH 07/10] add smileys --- .../api/anilist/sanitize/media/Media.kt | 5 +- profile/build.gradle.kts | 1 + .../animite/profile/ProfileScreen.kt | 6 -- .../imashnake/animite/profile/tabs/Media.kt | 4 -- .../animite/profile/ui/MediaTrackingList.kt | 56 ++++++++++++++++--- profile/src/main/res/drawable/dead.xml | 24 ++++++++ profile/src/main/res/drawable/neutral.xml | 24 ++++++++ profile/src/main/res/drawable/smile.xml | 24 ++++++++ profile/src/main/res/drawable/weary.xml | 24 ++++++++ profile/src/main/res/values/dimens.xml | 1 + 10 files changed, 150 insertions(+), 19 deletions(-) create mode 100644 profile/src/main/res/drawable/dead.xml create mode 100644 profile/src/main/res/drawable/neutral.xml create mode 100644 profile/src/main/res/drawable/smile.xml create mode 100644 profile/src/main/res/drawable/weary.xml diff --git a/api/anilist/src/main/kotlin/com/imashnake/animite/api/anilist/sanitize/media/Media.kt b/api/anilist/src/main/kotlin/com/imashnake/animite/api/anilist/sanitize/media/Media.kt index e06af1c6..13234e17 100644 --- a/api/anilist/src/main/kotlin/com/imashnake/animite/api/anilist/sanitize/media/Media.kt +++ b/api/anilist/src/main/kotlin/com/imashnake/animite/api/anilist/sanitize/media/Media.kt @@ -763,9 +763,10 @@ data class Media( ) } + @Immutable data class Score( val value: Float, - val scoreFormat: ScoreFormat, + val format: ScoreFormat, val color: Long ) { companion object { @@ -784,7 +785,7 @@ data class Media( internal constructor(score: Float, scoreFormat: ScoreFormat) : this( value = score, - scoreFormat = scoreFormat, + format = scoreFormat, color = when(scoreFormat) { ScoreFormat.POINT_100 -> getColor(score / 10f) ScoreFormat.POINT_10_DECIMAL, diff --git a/profile/build.gradle.kts b/profile/build.gradle.kts index ad1569d8..4a4be0d2 100644 --- a/profile/build.gradle.kts +++ b/profile/build.gradle.kts @@ -31,6 +31,7 @@ tasks.withType().configureEach { ) } +// TODO: Remove unused deps, dependencies { implementation(projects.api.anilist) implementation(projects.api.preferences) diff --git a/profile/src/main/kotlin/com/imashnake/animite/profile/ProfileScreen.kt b/profile/src/main/kotlin/com/imashnake/animite/profile/ProfileScreen.kt index 2a3cb8ca..f259d716 100644 --- a/profile/src/main/kotlin/com/imashnake/animite/profile/ProfileScreen.kt +++ b/profile/src/main/kotlin/com/imashnake/animite/profile/ProfileScreen.kt @@ -86,7 +86,6 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import coil3.compose.AsyncImage import com.imashnake.animite.api.anilist.sanitize.profile.User import com.imashnake.animite.api.anilist.sanitize.profile.User.Companion.profileColorToHex -import com.imashnake.animite.api.anilist.type.ScoreFormat import com.imashnake.animite.banner.NestedScrollBannerLayout import com.imashnake.animite.core.resource.Resource import com.imashnake.animite.core.ui.LocalPaddings @@ -147,7 +146,6 @@ fun ProfileScreen( val isLoggedIn by viewModel.isLoggedIn.collectAsState(initial = false) val useProfileColor by viewModel.useProfileColor.collectAsState(initial = true) val profileColor by viewModel.profileColor.collectAsState(initial = "blue") - val scoreFormat by viewModel.scoreFormat.collectAsState(initial = ScoreFormat.POINT_10_DECIMAL) val useExpressiveProgressIndicator by viewModel.useExpressiveProgressIndicator.collectAsState(initial = true) val viewerAvatar by viewModel.viewerAvatar.collectAsState(initial = "") val viewer by viewModel.viewer.collectAsState() @@ -274,7 +272,6 @@ fun ProfileScreen( user = this@run, animeCollection = viewerAnimeLists.data, mangaCollection = viewerMangaLists.data, - scoreFormat = scoreFormat, updateAnimeListsOrder = viewModel::updateAnimeLists, updateMangaListsOrder = viewModel::updateMangaLists, onNavigateToMediaItem = onNavigateToMediaItem, @@ -531,7 +528,6 @@ private fun UserTabs( user: User, animeCollection: User.MediaCollection?, mangaCollection: User.MediaCollection?, - scoreFormat: ScoreFormat, updateAnimeListsOrder: (List) -> Unit, updateMangaListsOrder: (List) -> Unit, onNavigateToMediaItem: (MediaPage) -> Unit, @@ -665,7 +661,6 @@ private fun UserTabs( ProfileTab.ANIME -> MediaTab( mediaCollection = animeCollection, listVisibility = animeListVisibility, - scoreFormat = scoreFormat, updateMediaListsOrder = updateAnimeListsOrder, onNavigateToMediaItem = onNavigateToMediaItem, useExpressiveProgressIndicator = useExpressiveProgressIndicator, @@ -674,7 +669,6 @@ private fun UserTabs( ProfileTab.MANGA -> MediaTab( mediaCollection = mangaCollection, listVisibility = mangaListVisibility, - scoreFormat = scoreFormat, updateMediaListsOrder = updateMangaListsOrder, onNavigateToMediaItem = onNavigateToMediaItem, useExpressiveProgressIndicator = useExpressiveProgressIndicator, diff --git a/profile/src/main/kotlin/com/imashnake/animite/profile/tabs/Media.kt b/profile/src/main/kotlin/com/imashnake/animite/profile/tabs/Media.kt index 0a905b5e..9854d7eb 100644 --- a/profile/src/main/kotlin/com/imashnake/animite/profile/tabs/Media.kt +++ b/profile/src/main/kotlin/com/imashnake/animite/profile/tabs/Media.kt @@ -26,7 +26,6 @@ import kotlinx.collections.immutable.ImmutableList fun MediaTab( mediaCollection: User.MediaCollection?, listVisibility: SnapshotStateMap, - scoreFormat: ScoreFormat, useExpressiveProgressIndicator: Boolean, updateMediaListsOrder: (List) -> Unit, onNavigateToMediaItem: (MediaPage) -> Unit, @@ -52,7 +51,6 @@ fun MediaTab( type = mediaCollection.type, lists = mediaCollection.namedLists, listVisibility = listVisibility, - scoreFormat = scoreFormat, updateMediaListsOrder = updateMediaListsOrder, onNavigateToMediaItem = onNavigateToMediaItem, useExpressiveProgressIndicator = useExpressiveProgressIndicator, @@ -68,7 +66,6 @@ private fun UserMediaLists( type: Media.Small.Type, lists: ImmutableList, listVisibility: SnapshotStateMap, - scoreFormat: ScoreFormat, useExpressiveProgressIndicator: Boolean, updateMediaListsOrder: (List) -> Unit, onNavigateToMediaItem: (MediaPage) -> Unit, @@ -79,7 +76,6 @@ private fun UserMediaLists( type = type, namedLists = lists, listVisibility = listVisibility, - scoreFormat = scoreFormat, updateMediaListsOrder = updateMediaListsOrder, onNavigateToMediaItem = onNavigateToMediaItem, useExpressiveProgressIndicator = useExpressiveProgressIndicator, diff --git a/profile/src/main/kotlin/com/imashnake/animite/profile/ui/MediaTrackingList.kt b/profile/src/main/kotlin/com/imashnake/animite/profile/ui/MediaTrackingList.kt index b182af7c..30cbe44c 100644 --- a/profile/src/main/kotlin/com/imashnake/animite/profile/ui/MediaTrackingList.kt +++ b/profile/src/main/kotlin/com/imashnake/animite/profile/ui/MediaTrackingList.kt @@ -10,6 +10,7 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth @@ -57,6 +58,7 @@ import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.compose.ui.util.fastForEachIndexed import androidx.compose.ui.util.fastMapNotNull +import androidx.compose.ui.util.fastRoundToInt import com.imashnake.animite.api.anilist.sanitize.media.Media import com.imashnake.animite.api.anilist.sanitize.profile.User import com.imashnake.animite.api.anilist.sanitize.profile.User.TrackingStatus.Companion.sanitize @@ -73,13 +75,13 @@ import kotlinx.collections.immutable.ImmutableList import sh.calvin.reorderable.ReorderableCollectionItemScope import sh.calvin.reorderable.ReorderableItem import sh.calvin.reorderable.rememberReorderableLazyListState +import kotlin.math.roundToInt @Composable fun MediaTrackingLists( type: Media.Small.Type, namedLists: ImmutableList, listVisibility: SnapshotStateMap, - scoreFormat: ScoreFormat, updateMediaListsOrder: (List) -> Unit, onNavigateToMediaItem: (MediaPage) -> Unit, useExpressiveProgressIndicator: Boolean, @@ -436,12 +438,7 @@ private fun MediaTrackingItem( } item.score?.let { score -> - Text( - text = score.value.toString(), - style = MaterialTheme.typography.bodyMedium.copy(fontWeight = FontWeight.Bold), - color = Color(score.color).copy(alpha = 0.6f), - modifier = Modifier.align(Alignment.CenterVertically).padding(top = 5.dp) - ) + Score(score) } } @@ -500,3 +497,48 @@ private fun MediaTrackingItem( ) } } + +@Composable +fun RowScope.Score( + score: Media.Score, + modifier: Modifier = Modifier +) { + when(score.format) { + ScoreFormat.POINT_100, + ScoreFormat.POINT_10_DECIMAL, + ScoreFormat.POINT_10 -> { + Text( + text = when (score.format) { + ScoreFormat.POINT_100, + ScoreFormat.POINT_10 -> score.value.roundToInt() + ScoreFormat.POINT_10_DECIMAL -> score.value + else -> "" + }.toString(), + style = MaterialTheme.typography.bodyMedium.copy(fontWeight = FontWeight.Bold), + color = Color(score.color).copy(alpha = 0.6f), + modifier = modifier + .align(Alignment.CenterVertically) + .padding(top = 5.dp) + ) + } + ScoreFormat.POINT_5 -> {} + ScoreFormat.POINT_3 -> { + Icon( + imageVector = ImageVector.vectorResource( + when(score.value.fastRoundToInt()) { + 0 -> R.drawable.dead + 1 -> R.drawable.weary + 2 -> R.drawable.neutral + else -> R.drawable.smile + } + ), + contentDescription = null, + tint = Color(score.color).copy(alpha = 0.6f), + modifier = modifier + .align(Alignment.CenterVertically) + .size(dimensionResource(R.dimen.smiley_icon_size)) + ) + } + else -> {} + } +} diff --git a/profile/src/main/res/drawable/dead.xml b/profile/src/main/res/drawable/dead.xml new file mode 100644 index 00000000..ff201878 --- /dev/null +++ b/profile/src/main/res/drawable/dead.xml @@ -0,0 +1,24 @@ + + + + diff --git a/profile/src/main/res/drawable/neutral.xml b/profile/src/main/res/drawable/neutral.xml new file mode 100644 index 00000000..8038334f --- /dev/null +++ b/profile/src/main/res/drawable/neutral.xml @@ -0,0 +1,24 @@ + + + + diff --git a/profile/src/main/res/drawable/smile.xml b/profile/src/main/res/drawable/smile.xml new file mode 100644 index 00000000..749c4e6e --- /dev/null +++ b/profile/src/main/res/drawable/smile.xml @@ -0,0 +1,24 @@ + + + + diff --git a/profile/src/main/res/drawable/weary.xml b/profile/src/main/res/drawable/weary.xml new file mode 100644 index 00000000..89b2d18e --- /dev/null +++ b/profile/src/main/res/drawable/weary.xml @@ -0,0 +1,24 @@ + + + + diff --git a/profile/src/main/res/values/dimens.xml b/profile/src/main/res/values/dimens.xml index bff11c62..622bcffc 100644 --- a/profile/src/main/res/values/dimens.xml +++ b/profile/src/main/res/values/dimens.xml @@ -11,4 +11,5 @@ 80dp 12dp + 24dp From 40724544b773109c360ace23b3fbf6239f81afe5 Mon Sep 17 00:00:00 2001 From: imashnake0 Date: Wed, 5 Aug 2026 16:19:46 -0400 Subject: [PATCH 08/10] animated content --- .../animite/profile/ui/MediaTrackingList.kt | 76 ++++++++++--------- 1 file changed, 40 insertions(+), 36 deletions(-) diff --git a/profile/src/main/kotlin/com/imashnake/animite/profile/ui/MediaTrackingList.kt b/profile/src/main/kotlin/com/imashnake/animite/profile/ui/MediaTrackingList.kt index 30cbe44c..484b0886 100644 --- a/profile/src/main/kotlin/com/imashnake/animite/profile/ui/MediaTrackingList.kt +++ b/profile/src/main/kotlin/com/imashnake/animite/profile/ui/MediaTrackingList.kt @@ -503,42 +503,46 @@ fun RowScope.Score( score: Media.Score, modifier: Modifier = Modifier ) { - when(score.format) { - ScoreFormat.POINT_100, - ScoreFormat.POINT_10_DECIMAL, - ScoreFormat.POINT_10 -> { - Text( - text = when (score.format) { - ScoreFormat.POINT_100, - ScoreFormat.POINT_10 -> score.value.roundToInt() - ScoreFormat.POINT_10_DECIMAL -> score.value - else -> "" - }.toString(), - style = MaterialTheme.typography.bodyMedium.copy(fontWeight = FontWeight.Bold), - color = Color(score.color).copy(alpha = 0.6f), - modifier = modifier - .align(Alignment.CenterVertically) - .padding(top = 5.dp) - ) - } - ScoreFormat.POINT_5 -> {} - ScoreFormat.POINT_3 -> { - Icon( - imageVector = ImageVector.vectorResource( - when(score.value.fastRoundToInt()) { - 0 -> R.drawable.dead - 1 -> R.drawable.weary - 2 -> R.drawable.neutral - else -> R.drawable.smile - } - ), - contentDescription = null, - tint = Color(score.color).copy(alpha = 0.6f), - modifier = modifier - .align(Alignment.CenterVertically) - .size(dimensionResource(R.dimen.smiley_icon_size)) - ) + AnimatedContent( + targetState = score, + modifier = modifier.align(Alignment.CenterVertically) + ) { score -> + when (score.format) { + ScoreFormat.POINT_100, + ScoreFormat.POINT_10_DECIMAL, + ScoreFormat.POINT_10 -> { + Text( + text = when (score.format) { + ScoreFormat.POINT_100, + ScoreFormat.POINT_10 -> score.value.roundToInt() + + ScoreFormat.POINT_10_DECIMAL -> score.value + else -> "" + }.toString(), + style = MaterialTheme.typography.bodyMedium.copy(fontWeight = FontWeight.Bold), + color = Color(score.color).copy(alpha = 0.6f), + modifier = modifier.padding(top = 5.dp) + ) + } + + ScoreFormat.POINT_5 -> {} + ScoreFormat.POINT_3 -> { + Icon( + imageVector = ImageVector.vectorResource( + when (score.value.fastRoundToInt()) { + 0 -> R.drawable.dead + 1 -> R.drawable.weary + 2 -> R.drawable.neutral + else -> R.drawable.smile + } + ), + contentDescription = null, + tint = Color(score.color).copy(alpha = 0.6f), + modifier = modifier.size(dimensionResource(R.dimen.smiley_icon_size)) + ) + } + + else -> {} } - else -> {} } } From cd5299176dfed8d6afa6793b8d9b3171ed140609 Mon Sep 17 00:00:00 2001 From: imashnake0 Date: Wed, 5 Aug 2026 16:43:06 -0400 Subject: [PATCH 09/10] add stars --- .../animite/profile/ui/MediaTrackingList.kt | 72 +++++++++++++------ profile/src/main/res/drawable/smile.xml | 24 ------- profile/src/main/res/values/dimens.xml | 1 + 3 files changed, 52 insertions(+), 45 deletions(-) delete mode 100644 profile/src/main/res/drawable/smile.xml diff --git a/profile/src/main/kotlin/com/imashnake/animite/profile/ui/MediaTrackingList.kt b/profile/src/main/kotlin/com/imashnake/animite/profile/ui/MediaTrackingList.kt index 484b0886..c4083e04 100644 --- a/profile/src/main/kotlin/com/imashnake/animite/profile/ui/MediaTrackingList.kt +++ b/profile/src/main/kotlin/com/imashnake/animite/profile/ui/MediaTrackingList.kt @@ -76,6 +76,7 @@ import sh.calvin.reorderable.ReorderableCollectionItemScope import sh.calvin.reorderable.ReorderableItem import sh.calvin.reorderable.rememberReorderableLazyListState import kotlin.math.roundToInt +import com.imashnake.animite.settings.R as settingsR @Composable fun MediaTrackingLists( @@ -410,29 +411,45 @@ private fun MediaTrackingItem( maxLines = 1, ) - Row(verticalAlignment = Alignment.CenterVertically) { - item.format?.let { - Text( - text = stringResource(it.res), - color = MaterialTheme.colorScheme.onSurfaceVariant, - style = MaterialTheme.typography.labelSmall, - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - } + Row( + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.fillMaxWidth() + ) { + Row(verticalAlignment = Alignment.CenterVertically) { + item.format?.let { + Text( + text = stringResource(it.res), + color = MaterialTheme.colorScheme.onSurfaceVariant, + style = MaterialTheme.typography.labelSmall, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } - if (item.season != null && item.format != null) { - Divider(shape = MaterialShapes.Triangle.toShape()) + if (item.season != null && item.format != null) { + Divider(shape = MaterialShapes.Triangle.toShape()) + } + + item.season?.let { + Text( + text = stringResource(it.res) + + " ${item.seasonYear?.toString().orEmpty()}", + color = MaterialTheme.colorScheme.onSurfaceVariant, + style = MaterialTheme.typography.labelSmall, + maxLines = 1 + ) + } } - item.season?.let { - Text( - text = stringResource(it.res) + - " ${item.seasonYear?.toString().orEmpty()}", - color = MaterialTheme.colorScheme.onSurfaceVariant, - style = MaterialTheme.typography.labelSmall, - maxLines = 1 - ) + item.score?.let { score -> + if (score.format == ScoreFormat.POINT_5) { + val filledStars = score.value.fastRoundToInt() + Row { + repeat(filledStars) { Star(filled = true) } + repeat(5 - filledStars) { Star(filled = false) } + } + } } } } @@ -533,7 +550,7 @@ fun RowScope.Score( 0 -> R.drawable.dead 1 -> R.drawable.weary 2 -> R.drawable.neutral - else -> R.drawable.smile + else -> settingsR.drawable.smile } ), contentDescription = null, @@ -546,3 +563,16 @@ fun RowScope.Score( } } } + +@Composable +private fun Star( + filled: Boolean, + modifier: Modifier = Modifier +) { + Icon( + imageVector = ImageVector.vectorResource(settingsR.drawable.star), + contentDescription = null, + tint = MaterialTheme.colorScheme.primary.copy(alpha = if (!filled) 0.2f else 1f), + modifier = modifier.size(dimensionResource(R.dimen.star_icon_size)) + ) +} diff --git a/profile/src/main/res/drawable/smile.xml b/profile/src/main/res/drawable/smile.xml deleted file mode 100644 index 749c4e6e..00000000 --- a/profile/src/main/res/drawable/smile.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - diff --git a/profile/src/main/res/values/dimens.xml b/profile/src/main/res/values/dimens.xml index 622bcffc..10837258 100644 --- a/profile/src/main/res/values/dimens.xml +++ b/profile/src/main/res/values/dimens.xml @@ -12,4 +12,5 @@ 12dp 24dp + 14dp From 220168287dc61660f7b185369f032c7c41e5950a Mon Sep 17 00:00:00 2001 From: imashnake0 Date: Wed, 5 Aug 2026 16:55:07 -0400 Subject: [PATCH 10/10] cleanup --- .../main/kotlin/com/imashnake/animite/profile/tabs/Media.kt | 1 - .../com/imashnake/animite/profile/ui/MediaTrackingList.kt | 4 +++- .../main/kotlin/com/imashnake/animite/settings/ext/EnumExt.kt | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/profile/src/main/kotlin/com/imashnake/animite/profile/tabs/Media.kt b/profile/src/main/kotlin/com/imashnake/animite/profile/tabs/Media.kt index 9854d7eb..d8951b4a 100644 --- a/profile/src/main/kotlin/com/imashnake/animite/profile/tabs/Media.kt +++ b/profile/src/main/kotlin/com/imashnake/animite/profile/tabs/Media.kt @@ -9,7 +9,6 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import com.imashnake.animite.api.anilist.sanitize.media.Media import com.imashnake.animite.api.anilist.sanitize.profile.User -import com.imashnake.animite.api.anilist.type.ScoreFormat import com.imashnake.animite.core.ui.screen.FallbackScreen import com.imashnake.animite.media.MediaPage import com.imashnake.animite.profile.R diff --git a/profile/src/main/kotlin/com/imashnake/animite/profile/ui/MediaTrackingList.kt b/profile/src/main/kotlin/com/imashnake/animite/profile/ui/MediaTrackingList.kt index c4083e04..672409d2 100644 --- a/profile/src/main/kotlin/com/imashnake/animite/profile/ui/MediaTrackingList.kt +++ b/profile/src/main/kotlin/com/imashnake/animite/profile/ui/MediaTrackingList.kt @@ -78,6 +78,8 @@ import sh.calvin.reorderable.rememberReorderableLazyListState import kotlin.math.roundToInt import com.imashnake.animite.settings.R as settingsR +private const val TOTAL_STARS = 5 + @Composable fun MediaTrackingLists( type: Media.Small.Type, @@ -447,7 +449,7 @@ private fun MediaTrackingItem( val filledStars = score.value.fastRoundToInt() Row { repeat(filledStars) { Star(filled = true) } - repeat(5 - filledStars) { Star(filled = false) } + repeat(TOTAL_STARS - filledStars) { Star(filled = false) } } } } diff --git a/settings/src/main/kotlin/com/imashnake/animite/settings/ext/EnumExt.kt b/settings/src/main/kotlin/com/imashnake/animite/settings/ext/EnumExt.kt index a7f5de70..813cb55d 100644 --- a/settings/src/main/kotlin/com/imashnake/animite/settings/ext/EnumExt.kt +++ b/settings/src/main/kotlin/com/imashnake/animite/settings/ext/EnumExt.kt @@ -9,4 +9,4 @@ val ScoreFormat.title get() = when(this) { ScoreFormat.POINT_5 -> "5" ScoreFormat.POINT_3 -> "3" ScoreFormat.UNKNOWN__ -> "" -} \ No newline at end of file +}