From ed1bf98a40496fea46a8339a0ae98e73848db74b Mon Sep 17 00:00:00 2001 From: imashnake0 Date: Fri, 24 Jul 2026 17:49:38 -0400 Subject: [PATCH 01/16] i --- .../api/anilist/sanitize/profile/Viewer.kt | 10 +- .../animite/settings/SettingsPage.kt | 99 +++++++++++++++---- settings/src/main/res/values/strings.xml | 1 + 3 files changed, 87 insertions(+), 23 deletions(-) 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 3918e63b..57c1c3cd 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 @@ -8,7 +8,7 @@ import com.imashnake.animite.api.anilist.sanitize.media.Media import com.imashnake.animite.api.anilist.sanitize.media.Media.Language import com.imashnake.animite.api.anilist.sanitize.media.Media.Small.Type import com.imashnake.animite.api.anilist.sanitize.profile.User.ListNames.Companion.sanitize -import com.imashnake.animite.api.anilist.sanitize.profile.User.ProfileColors.Companion.toHexString +import com.imashnake.animite.api.anilist.sanitize.profile.User.ProfileColor.Companion.toHexString import com.imashnake.animite.api.anilist.type.MediaType import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList @@ -206,19 +206,19 @@ data class User( ) /** Profile highlight color (blue, purple, pink, orange, red, green, gray) */ - enum class ProfileColors { + enum class ProfileColor { BLUE, PURPLE, PINK, ORANGE, RED, GREEN, GRAY; companion object { - fun safeValueOf(rawValue: String): ProfileColors? = try { - ProfileColors.valueOf(rawValue) + fun safeValueOf(rawValue: String): ProfileColor? = try { + ProfileColor.valueOf(rawValue) } catch (e: IllegalArgumentException) { Log.e(EXCEPTION_TAG, "safeValueOf: $e; Profile color $rawValue doesn't exist!.") null } internal fun String?.toHexString(): String { - val color = when(this?.let { ProfileColors.safeValueOf(it.uppercase()) }) { + val color = when(this?.let { ProfileColor.safeValueOf(it.uppercase()) }) { BLUE -> "#007BA7" PURPLE -> "#E0AFFF" PINK -> "#F2BDCD" 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 ac178eb9..3bb412af 100644 --- a/settings/src/main/kotlin/com/imashnake/animite/settings/SettingsPage.kt +++ b/settings/src/main/kotlin/com/imashnake/animite/settings/SettingsPage.kt @@ -100,6 +100,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.materialkolor.ktx.darken import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.serialization.Serializable @@ -575,6 +576,47 @@ fun SettingsPage( } }, onItemLongClick = {}, + itemSubContent = { + when (it) { + 1 -> { + Row( + horizontalArrangement = Arrangement.Absolute.SpaceBetween, + modifier = Modifier + .fillMaxWidth() + .padding(top = LocalPaddings.current.medium) + ) { + ProfileColor.entries.fastForEachIndexed { index, profileColor -> + ToggleButton( + checked = index == 1, + onCheckedChange = { }, + shapes = when (index) { + 0 -> ButtonGroupDefaults.connectedLeadingButtonShapes() + ProfileColor.entries.lastIndex -> ButtonGroupDefaults.connectedTrailingButtonShapes() + else -> ButtonGroupDefaults.connectedMiddleButtonShapes() + }, + enabled = useProfileColor, + colors = ToggleButtonDefaults.toggleButtonColors( + containerColor = profileColor.color.darken(), + contentColor = Color.White, + disabledContainerColor = profileColor.color.darken(), + checkedContainerColor = profileColor.color, + checkedContentColor = Color.White, + ), + modifier = Modifier.weight(1f) + ) { + profileColor.label?.let { id -> + Text(stringResource(id)) + } ?: Icon( + imageVector = Icons.Filled.Check, + contentDescription = null, + modifier = Modifier.size(SwitchDefaults.IconSize), + ) + } + } + } + } + } + }, isDarkMode = isDarkMode, ) { index -> when (index) { @@ -760,7 +802,8 @@ private fun Items( onItemClick: (Int) -> Unit, onItemLongClick: (Int) -> Unit, itemCustomIcon: @Composable (() -> Unit)? = null, - itemContent: @Composable (Int) -> Unit, + itemSubContent: @Composable ((Int) -> Unit)? = null, + itemContent: @Composable (Int) -> Unit ) { CompositionLocalProvider(LocalPaddings provides rememberDefaultPaddings()) { Column( @@ -795,7 +838,8 @@ private fun Items( onItemLongClick = { onItemLongClick(index) }, background = if (isDarkMode) { MaterialTheme.colorScheme.surfaceContainer - } else MaterialTheme.colorScheme.surface + } else MaterialTheme.colorScheme.surface, + subContent = { itemSubContent?.invoke(index) } ) { itemContent(index) } @@ -835,6 +879,7 @@ private fun HorizontalItem( onItemLongClick: () -> Unit, modifier: Modifier = Modifier, customIcon: @Composable (() -> Unit)? = null, + subContent: @Composable (() -> Unit)? = null, content: @Composable () -> Unit, ) { CompositionLocalProvider( @@ -842,9 +887,8 @@ private fun HorizontalItem( // Remove default M3 padding LocalMinimumInteractiveComponentSize provides 0.dp, ) { - Row( - horizontalArrangement = Arrangement.spacedBy(LocalPaddings.current.medium), - verticalAlignment = Alignment.CenterVertically, + Column( + verticalArrangement = Arrangement.spacedBy(LocalPaddings.current.medium), modifier = modifier .fillMaxWidth() .clip(shape) @@ -855,21 +899,27 @@ private fun HorizontalItem( .background(background) .padding(LocalPaddings.current.medium) ) { - if (customIcon != null) { - customIcon() - } else { - Icon( - imageVector = ImageVector.vectorResource(item.second.icon), - contentDescription = null, + Row( + horizontalArrangement = Arrangement.spacedBy(LocalPaddings.current.medium), + verticalAlignment = Alignment.CenterVertically, + ) { + if (customIcon != null) { + customIcon() + } else { + Icon( + imageVector = ImageVector.vectorResource(item.second.icon), + contentDescription = null, + ) + } + Text( + text = stringResource(item.second.label), + color = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.9f), + style = MaterialTheme.typography.titleSmall, + modifier = Modifier.weight(1f), ) + content() } - Text( - text = stringResource(item.second.label), - color = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.9f), - style = MaterialTheme.typography.titleSmall, - modifier = Modifier.weight(1f), - ) - content() + subContent?.invoke() } } } @@ -1244,5 +1294,18 @@ enum class Theme(@param:StringRes val theme: Int) { DEVICE_THEME(R.string.system), } +enum class ProfileColor( + val color: Color, + @param:StringRes val label: Int? = null +) { + BLUE(Color(0xFF007BA7), R.string.mlue), + PURPLE(Color(0xFFC262FF)), + PINK(Color(0xFFFF6C93)), + ORANGE(Color(0xFFEE9500)), + RED(Color(0xFFF84A4B)), + GREEN(Color(0xFF06C94B)), + GRAY(Color(0xFF989898)); +} + @Serializable data object SettingsPage diff --git a/settings/src/main/res/values/strings.xml b/settings/src/main/res/values/strings.xml index 63ebb277..add132c3 100644 --- a/settings/src/main/res/values/strings.xml +++ b/settings/src/main/res/values/strings.xml @@ -23,6 +23,7 @@ Profile Show description Set profile color + Mlue About From c08e962ad33cf52568f71da8039779643ff97b8e Mon Sep 17 00:00:00 2001 From: imashnake0 Date: Fri, 24 Jul 2026 17:55:28 -0400 Subject: [PATCH 02/16] add color palette --- .../animite/settings/SettingsPage.kt | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) 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 3bb412af..91e2b78c 100644 --- a/settings/src/main/kotlin/com/imashnake/animite/settings/SettingsPage.kt +++ b/settings/src/main/kotlin/com/imashnake/animite/settings/SettingsPage.kt @@ -580,15 +580,20 @@ fun SettingsPage( when (it) { 1 -> { Row( - horizontalArrangement = Arrangement.Absolute.SpaceBetween, + horizontalArrangement = Arrangement.spacedBy( + ButtonGroupDefaults.ConnectedSpaceBetween + ), modifier = Modifier .fillMaxWidth() .padding(top = LocalPaddings.current.medium) ) { + // TODO: Get from network/prefs. + var selectedColor by remember { mutableIntStateOf(0) } + ProfileColor.entries.fastForEachIndexed { index, profileColor -> ToggleButton( - checked = index == 1, - onCheckedChange = { }, + checked = index == selectedColor, + onCheckedChange = { selectedColor = index }, shapes = when (index) { 0 -> ButtonGroupDefaults.connectedLeadingButtonShapes() ProfileColor.entries.lastIndex -> ButtonGroupDefaults.connectedTrailingButtonShapes() @@ -604,13 +609,15 @@ fun SettingsPage( ), modifier = Modifier.weight(1f) ) { - profileColor.label?.let { id -> - Text(stringResource(id)) - } ?: Icon( - imageVector = Icons.Filled.Check, - contentDescription = null, - modifier = Modifier.size(SwitchDefaults.IconSize), - ) + if (index == 0) { + profileColor.label?.let { id -> + Text(stringResource(id), fontSize = 11.sp) + } ?: Icon( + imageVector = Icons.Filled.Check, + contentDescription = null, + modifier = Modifier.size(SwitchDefaults.IconSize), + ) + } } } } @@ -644,7 +651,6 @@ fun SettingsPage( ) } 1 -> { - // TODO: Add color options and mutate. Switch( checked = useProfileColor, onCheckedChange = { From 5071a1d3ee91d2914f73a9190bb629189e69730e Mon Sep 17 00:00:00 2001 From: imashnake0 Date: Fri, 24 Jul 2026 18:09:06 -0400 Subject: [PATCH 03/16] yoink state --- .../animite/settings/SettingsPage.kt | 37 ++++++++----------- settings/src/main/res/values/strings.xml | 2 +- 2 files changed, 16 insertions(+), 23 deletions(-) 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 91e2b78c..19822634 100644 --- a/settings/src/main/kotlin/com/imashnake/animite/settings/SettingsPage.kt +++ b/settings/src/main/kotlin/com/imashnake/animite/settings/SettingsPage.kt @@ -133,8 +133,6 @@ fun SettingsPage( val selectedDensity by viewModel.density.collectAsState(initial = Density.COMFY.name) val isNsfwEnabled by viewModel.isNsfwEnabled.collectAsState(initial = false) - val showUserDescription by viewModel.showUserDescription.collectAsState(initial = true) - val useProfileColor by viewModel.useProfileColor.collectAsState(initial = true) val selectedLanguage by viewModel.language.collectAsState(initial = Media.Language.DEFAULT.name) val listSize by viewModel.listSize.collectAsState(initial = 10) @@ -143,6 +141,12 @@ fun SettingsPage( val mangaLists by viewModel.mangaList.collectAsState(initial = persistentListOf()) val mangaListsIndices by viewModel.mangaListsIndices.collectAsState(initial = byteArrayOf(1, 2, 3, 4, 5)) + val showUserDescription by viewModel.showUserDescription.collectAsState(initial = true) + val useProfileColor by viewModel.useProfileColor.collectAsState(initial = true) + // TODO: Get from network/prefs. + var selectedColorIndex by remember { mutableIntStateOf(0) } + + val isDevOptionsEnabled by viewModel.isDevOptionsEnabled.collectAsState(initial = false) val isDarkMode = selectedTheme == Theme.DARK.name || @@ -565,14 +569,6 @@ fun SettingsPage( } else HapticFeedbackType.ToggleOn ) } - 1 -> { - viewModel.setUseProfileColor(!useProfileColor) - haptic.performHapticFeedback( - hapticFeedbackType = if (useProfileColor) { - HapticFeedbackType.ToggleOff - } else HapticFeedbackType.ToggleOn - ) - } } }, onItemLongClick = {}, @@ -583,17 +579,15 @@ fun SettingsPage( horizontalArrangement = Arrangement.spacedBy( ButtonGroupDefaults.ConnectedSpaceBetween ), - modifier = Modifier - .fillMaxWidth() - .padding(top = LocalPaddings.current.medium) + modifier = Modifier.fillMaxWidth() ) { - // TODO: Get from network/prefs. - var selectedColor by remember { mutableIntStateOf(0) } - ProfileColor.entries.fastForEachIndexed { index, profileColor -> ToggleButton( - checked = index == selectedColor, - onCheckedChange = { selectedColor = index }, + checked = index == selectedColorIndex && useProfileColor, + onCheckedChange = { + selectedColorIndex = index + haptic.performHapticFeedback(HapticFeedbackType.SegmentTick) + }, shapes = when (index) { 0 -> ButtonGroupDefaults.connectedLeadingButtonShapes() ProfileColor.entries.lastIndex -> ButtonGroupDefaults.connectedTrailingButtonShapes() @@ -601,17 +595,16 @@ fun SettingsPage( }, enabled = useProfileColor, colors = ToggleButtonDefaults.toggleButtonColors( - containerColor = profileColor.color.darken(), + containerColor = profileColor.color.darken(2f), contentColor = Color.White, - disabledContainerColor = profileColor.color.darken(), checkedContainerColor = profileColor.color, checkedContentColor = Color.White, ), modifier = Modifier.weight(1f) ) { - if (index == 0) { + if (index == selectedColorIndex && useProfileColor) { profileColor.label?.let { id -> - Text(stringResource(id), fontSize = 11.sp) + Text(stringResource(id), fontSize = 7.sp) } ?: Icon( imageVector = Icons.Filled.Check, contentDescription = null, diff --git a/settings/src/main/res/values/strings.xml b/settings/src/main/res/values/strings.xml index add132c3..ee0d0efa 100644 --- a/settings/src/main/res/values/strings.xml +++ b/settings/src/main/res/values/strings.xml @@ -23,7 +23,7 @@ Profile Show description Set profile color - Mlue + mlue About From a6c3b704c723c55348777f12cbe6ea0022affaab Mon Sep 17 00:00:00 2001 From: imashnake0 Date: Fri, 24 Jul 2026 18:24:44 -0400 Subject: [PATCH 04/16] round check and fix colors --- .../animite/settings/SettingsPage.kt | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) 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 19822634..6ab9076b 100644 --- a/settings/src/main/kotlin/com/imashnake/animite/settings/SettingsPage.kt +++ b/settings/src/main/kotlin/com/imashnake/animite/settings/SettingsPage.kt @@ -40,7 +40,7 @@ import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Check +import androidx.compose.material.icons.rounded.Check import androidx.compose.material3.ButtonGroupDefaults import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @@ -265,7 +265,7 @@ fun SettingsPage( thumbContent = { if (useSystemColorScheme) { Icon( - imageVector = Icons.Filled.Check, + imageVector = Icons.Rounded.Check, contentDescription = null, modifier = Modifier.size(SwitchDefaults.IconSize), tint = MaterialTheme.colorScheme.primary @@ -289,7 +289,7 @@ fun SettingsPage( thumbContent = { if (isAmoled) { Icon( - imageVector = Icons.Filled.Check, + imageVector = Icons.Rounded.Check, contentDescription = null, modifier = Modifier.size(SwitchDefaults.IconSize), tint = MaterialTheme.colorScheme.primary @@ -595,18 +595,22 @@ fun SettingsPage( }, enabled = useProfileColor, colors = ToggleButtonDefaults.toggleButtonColors( - containerColor = profileColor.color.darken(2f), - contentColor = Color.White, + containerColor = profileColor.color, + contentColor = profileColor.color.darken(4f), checkedContainerColor = profileColor.color, - checkedContentColor = Color.White, + checkedContentColor = profileColor.color.darken(4f), ), modifier = Modifier.weight(1f) ) { if (index == selectedColorIndex && useProfileColor) { profileColor.label?.let { id -> - Text(stringResource(id), fontSize = 7.sp) + Text( + text = stringResource(id), + fontSize = 7.sp, + maxLines = 1 + ) } ?: Icon( - imageVector = Icons.Filled.Check, + imageVector = Icons.Rounded.Check, contentDescription = null, modifier = Modifier.size(SwitchDefaults.IconSize), ) @@ -1250,7 +1254,7 @@ private fun PreviewItems() { thumbContent = { if (useColorScheme) { Icon( - imageVector = Icons.Filled.Check, + imageVector = Icons.Rounded.Check, contentDescription = null, modifier = Modifier.size(SwitchDefaults.IconSize), ) From 5162be4b96c99ee2c30099253aa0f7c178a1d893 Mon Sep 17 00:00:00 2001 From: imashnake0 Date: Sat, 25 Jul 2026 00:08:08 -0400 Subject: [PATCH 05/16] move files around and add mutation --- api/anilist/src/main/graphql/mutations/UpdateUser.graphql | 7 +++++++ .../src/main/graphql/{ => queries}/MediaListQuery.graphql | 0 .../graphql/{ => queries}/MediaMediumListQuery.graphql | 0 .../src/main/graphql/{ => queries}/MediaQuery.graphql | 0 .../src/main/graphql/{ => queries}/UserQuery.graphql | 0 5 files changed, 7 insertions(+) create mode 100644 api/anilist/src/main/graphql/mutations/UpdateUser.graphql rename api/anilist/src/main/graphql/{ => queries}/MediaListQuery.graphql (100%) rename api/anilist/src/main/graphql/{ => queries}/MediaMediumListQuery.graphql (100%) rename api/anilist/src/main/graphql/{ => queries}/MediaQuery.graphql (100%) rename api/anilist/src/main/graphql/{ => queries}/UserQuery.graphql (100%) diff --git a/api/anilist/src/main/graphql/mutations/UpdateUser.graphql b/api/anilist/src/main/graphql/mutations/UpdateUser.graphql new file mode 100644 index 00000000..e3307eae --- /dev/null +++ b/api/anilist/src/main/graphql/mutations/UpdateUser.graphql @@ -0,0 +1,7 @@ +mutation UpdateUser($profileColor: String) { + UpdateUser(profileColor: $profileColor) { + options { + profileColor + } + } +} diff --git a/api/anilist/src/main/graphql/MediaListQuery.graphql b/api/anilist/src/main/graphql/queries/MediaListQuery.graphql similarity index 100% rename from api/anilist/src/main/graphql/MediaListQuery.graphql rename to api/anilist/src/main/graphql/queries/MediaListQuery.graphql diff --git a/api/anilist/src/main/graphql/MediaMediumListQuery.graphql b/api/anilist/src/main/graphql/queries/MediaMediumListQuery.graphql similarity index 100% rename from api/anilist/src/main/graphql/MediaMediumListQuery.graphql rename to api/anilist/src/main/graphql/queries/MediaMediumListQuery.graphql diff --git a/api/anilist/src/main/graphql/MediaQuery.graphql b/api/anilist/src/main/graphql/queries/MediaQuery.graphql similarity index 100% rename from api/anilist/src/main/graphql/MediaQuery.graphql rename to api/anilist/src/main/graphql/queries/MediaQuery.graphql diff --git a/api/anilist/src/main/graphql/UserQuery.graphql b/api/anilist/src/main/graphql/queries/UserQuery.graphql similarity index 100% rename from api/anilist/src/main/graphql/UserQuery.graphql rename to api/anilist/src/main/graphql/queries/UserQuery.graphql From e38c5691a3d36296abfdc1c60a3f1c727292ebb8 Mon Sep 17 00:00:00 2001 From: imashnake0 Date: Sat, 25 Jul 2026 00:15:07 -0400 Subject: [PATCH 06/16] cleanup --- .../api/anilist/sanitize/profile/Viewer.kt | 42 +++++-------------- settings/build.gradle.kts | 1 + .../animite/settings/SettingsPage.kt | 8 ++-- 3 files changed, 15 insertions(+), 36 deletions(-) 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 57c1c3cd..44a1e769 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 @@ -8,7 +8,6 @@ import com.imashnake.animite.api.anilist.sanitize.media.Media import com.imashnake.animite.api.anilist.sanitize.media.Media.Language import com.imashnake.animite.api.anilist.sanitize.media.Media.Small.Type import com.imashnake.animite.api.anilist.sanitize.profile.User.ListNames.Companion.sanitize -import com.imashnake.animite.api.anilist.sanitize.profile.User.ProfileColor.Companion.toHexString import com.imashnake.animite.api.anilist.type.MediaType import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList @@ -172,7 +171,16 @@ data class User( description = query.about, avatar = query.avatar?.large, banner = query.bannerImage, - color = query.options?.profileColor?.toHexString(), + color = when(query.options?.profileColor) { + "blue" -> "#007BA7" + "purple" -> "#E0AFFF" + "pink" -> "#F2BDCD" + "orange" -> "#F2B949" + "red" -> "#FA5053" + "green" -> "#0BDA51" + "gray" -> "#D9D9D9" + else -> "#007BA7" + }, stats = listOfNotNull( query.statistics?.anime?.count?.toString()?.let { Stat("TOTAL\nANIME", it) @@ -205,36 +213,6 @@ data class User( ).toImmutableList() ) - /** Profile highlight color (blue, purple, pink, orange, red, green, gray) */ - enum class ProfileColor { - BLUE, PURPLE, PINK, ORANGE, RED, GREEN, GRAY; - - companion object { - fun safeValueOf(rawValue: String): ProfileColor? = try { - ProfileColor.valueOf(rawValue) - } catch (e: IllegalArgumentException) { - Log.e(EXCEPTION_TAG, "safeValueOf: $e; Profile color $rawValue doesn't exist!.") - null - } - - internal fun String?.toHexString(): String { - val color = when(this?.let { ProfileColor.safeValueOf(it.uppercase()) }) { - BLUE -> "#007BA7" - PURPLE -> "#E0AFFF" - PINK -> "#F2BDCD" - ORANGE -> "#F2B949" - RED -> "#FA5053" - GREEN -> "#0BDA51" - GRAY -> "#D9D9D9" - null -> "#FF8DA1" - } - Log.d("whatiscolor", "toHexString: $color") - - return color - } - } - } - enum class Favouritables { Anime, Manga, diff --git a/settings/build.gradle.kts b/settings/build.gradle.kts index 955d5e7c..9bbc3ecc 100644 --- a/settings/build.gradle.kts +++ b/settings/build.gradle.kts @@ -36,6 +36,7 @@ dependencies { implementation(projects.api.anilist) implementation(projects.api.preferences) implementation(projects.core.model) + implementation(projects.core.resource) implementation(projects.core.ui) implementation(projects.banner) implementation(projects.media) 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 6ab9076b..67a400b9 100644 --- a/settings/src/main/kotlin/com/imashnake/animite/settings/SettingsPage.kt +++ b/settings/src/main/kotlin/com/imashnake/animite/settings/SettingsPage.kt @@ -1301,12 +1301,12 @@ enum class ProfileColor( val color: Color, @param:StringRes val label: Int? = null ) { - BLUE(Color(0xFF007BA7), R.string.mlue), PURPLE(Color(0xFFC262FF)), - PINK(Color(0xFFFF6C93)), - ORANGE(Color(0xFFEE9500)), - RED(Color(0xFFF84A4B)), + BLUE(Color(0xFF0BA1DA), R.string.mlue), GREEN(Color(0xFF06C94B)), + ORANGE(Color(0xFFEE9500)), + PINK(Color(0xFFFF527E)), + RED(Color(0xFFFF3131)), GRAY(Color(0xFF989898)); } From be3750ec85e93758262fdc40205f391f320f51e2 Mon Sep 17 00:00:00 2001 From: imashnake0 Date: Sat, 25 Jul 2026 00:15:38 -0400 Subject: [PATCH 07/16] add update user --- .../animite/api/anilist/AnilistUserRepository.kt | 8 ++++++++ 1 file changed, 8 insertions(+) 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 705c26aa..f843e97c 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 @@ -58,4 +58,12 @@ class AnilistUserRepository( .filter { it.exception == null } .asResult { User.MediaCollection(it, type, language, mediaListOrder) } } + + fun updateUser(profileColor: String): Flow> { + return apolloClient + .mutation(UpdateUserMutation(Optional.presentIfNotNull(profileColor))) + .fetchPolicy(FetchPolicy.NetworkOnly) + .toFlow() + .asResult { it.UpdateUser?.options?.profileColor } + } } From 09381ec43299b3d87326e4049591c21161536e06 Mon Sep 17 00:00:00 2001 From: imashnake0 Date: Sat, 25 Jul 2026 00:20:42 -0400 Subject: [PATCH 08/16] add pref repository --- .../animite/api/preferences/PreferencesRepository.kt | 7 +++++++ 1 file changed, 7 insertions(+) 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 d0b7f87e..3c41812e 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 @@ -20,6 +20,7 @@ private const val IS_NSFW_ENABLED = false private const val DEFAULT_LANGUAGE_KEY = "DEFAULT" private const val SHOW_USER_DESCRIPTION = true private const val USE_PROFILE_COLOR = true +private const val PROFILE_COLOR = "blue" private const val IS_DEV_OPTIONS_ENABLED = false /** @@ -163,6 +164,12 @@ class PreferencesRepository internal constructor( dataStore.setValue(useProfileColorKey, useProfileColor) } + private val profileColorKey = stringPreferencesKey("profile_color") + val profileColor = dataStore.getValue(profileColorKey, PROFILE_COLOR) + suspend fun setProfileColor(profileColor: String) { + dataStore.setValue(profileColorKey, profileColor) + } + // region developer options private val isDevOptionsEnabledKey = booleanPreferencesKey("dev_options_enabled") val isDevOptionsEnabled = dataStore.getValue(isDevOptionsEnabledKey, IS_DEV_OPTIONS_ENABLED) From 5396946af7ff4c83a6e25aa3cb3ba5cbcaba2ee3 Mon Sep 17 00:00:00 2001 From: imashnake0 Date: Sat, 25 Jul 2026 01:11:05 -0400 Subject: [PATCH 09/16] mutate --- .../api/anilist/sanitize/profile/Viewer.kt | 1 + .../animite/settings/SettingsPage.kt | 24 +++++++++---------- .../animite/settings/SettingsViewModel.kt | 14 +++++++++++ 3 files changed, 26 insertions(+), 13 deletions(-) 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 44a1e769..338ab182 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 @@ -181,6 +181,7 @@ data class User( "gray" -> "#D9D9D9" else -> "#007BA7" }, + // TODO: Replace with string resources. stats = listOfNotNull( query.statistics?.anime?.count?.toString()?.let { Stat("TOTAL\nANIME", it) 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 67a400b9..8bf97f8f 100644 --- a/settings/src/main/kotlin/com/imashnake/animite/settings/SettingsPage.kt +++ b/settings/src/main/kotlin/com/imashnake/animite/settings/SettingsPage.kt @@ -143,9 +143,7 @@ fun SettingsPage( val showUserDescription by viewModel.showUserDescription.collectAsState(initial = true) val useProfileColor by viewModel.useProfileColor.collectAsState(initial = true) - // TODO: Get from network/prefs. - var selectedColorIndex by remember { mutableIntStateOf(0) } - + val profileColor by viewModel.profileColor.collectAsState(initial = "blue") val isDevOptionsEnabled by viewModel.isDevOptionsEnabled.collectAsState(initial = false) @@ -581,11 +579,11 @@ fun SettingsPage( ), modifier = Modifier.fillMaxWidth() ) { - ProfileColor.entries.fastForEachIndexed { index, profileColor -> + ProfileColor.entries.fastForEachIndexed { index, entry -> ToggleButton( - checked = index == selectedColorIndex && useProfileColor, + checked = profileColor.equals(entry.name, ignoreCase = true) && useProfileColor, onCheckedChange = { - selectedColorIndex = index + viewModel.setProfileColor(entry.name.lowercase()) haptic.performHapticFeedback(HapticFeedbackType.SegmentTick) }, shapes = when (index) { @@ -595,15 +593,15 @@ fun SettingsPage( }, enabled = useProfileColor, colors = ToggleButtonDefaults.toggleButtonColors( - containerColor = profileColor.color, - contentColor = profileColor.color.darken(4f), - checkedContainerColor = profileColor.color, - checkedContentColor = profileColor.color.darken(4f), + containerColor = entry.color, + contentColor = entry.color.darken(4f), + checkedContainerColor = entry.color, + checkedContentColor = entry.color.darken(4f), ), modifier = Modifier.weight(1f) ) { - if (index == selectedColorIndex && useProfileColor) { - profileColor.label?.let { id -> + if (profileColor.equals(entry.name, ignoreCase = true) && useProfileColor) { + entry.label?.let { id -> Text( text = stringResource(id), fontSize = 7.sp, @@ -1299,7 +1297,7 @@ enum class Theme(@param:StringRes val theme: Int) { enum class ProfileColor( val color: Color, - @param:StringRes val label: Int? = null + @param:StringRes val label: Int? = null, ) { PURPLE(Color(0xFFC262FF)), BLUE(Color(0xFF0BA1DA), R.string.mlue), 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 feab5647..a69b6886 100644 --- a/settings/src/main/kotlin/com/imashnake/animite/settings/SettingsViewModel.kt +++ b/settings/src/main/kotlin/com/imashnake/animite/settings/SettingsViewModel.kt @@ -2,6 +2,7 @@ package com.imashnake.animite.settings 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.preferences.PreferencesRepository import com.imashnake.animite.core.model.AnimeList @@ -10,13 +11,16 @@ import com.imashnake.animite.core.ui.Density import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import javax.inject.Inject +@OptIn(ExperimentalCoroutinesApi::class) @HiltViewModel class SettingsViewModel @Inject constructor( + private val userRepository: AnilistUserRepository, private val preferencesRepository: PreferencesRepository, ) : ViewModel() { val theme = preferencesRepository.theme.filterNotNull() @@ -58,6 +62,8 @@ class SettingsViewModel @Inject constructor( } val mangaListsIndices = preferencesRepository.mangaListsIndices.filterNotNull() + val profileColor = preferencesRepository.profileColor.filterNotNull() + fun setTheme(theme: Theme) = viewModelScope.launch(Dispatchers.IO) { preferencesRepository.setTheme(theme.name) } @@ -93,6 +99,14 @@ class SettingsViewModel @Inject constructor( preferencesRepository.setUseProfileColor(useProfileColor) } + fun setProfileColor(profileColor: String) = viewModelScope.launch(Dispatchers.IO) { + userRepository.updateUser(profileColor).collect { result -> + result.onSuccess { color -> + color?.let { preferencesRepository.setProfileColor(it) } + } + } + } + fun setDevOptions(enabled: Boolean) = viewModelScope.launch(Dispatchers.IO) { preferencesRepository.setDevOptionsEnabled(enabled) } From 7da196209eeabd0da285e693ba2847ce17b8ad8b Mon Sep 17 00:00:00 2001 From: imashnake0 Date: Sat, 25 Jul 2026 01:13:38 -0400 Subject: [PATCH 10/16] use profile color --- .../main/kotlin/com/imashnake/animite/profile/ProfileScreen.kt | 3 ++- .../kotlin/com/imashnake/animite/profile/ProfileViewModel.kt | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) 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 e5a5c8ab..15835b13 100644 --- a/profile/src/main/kotlin/com/imashnake/animite/profile/ProfileScreen.kt +++ b/profile/src/main/kotlin/com/imashnake/animite/profile/ProfileScreen.kt @@ -140,6 +140,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 = "#007BA7") val viewerAvatar by viewModel.viewerAvatar.collectAsState(initial = "") val viewer by viewModel.viewer.collectAsState() val viewerAnimeLists by viewModel.viewerAnimeLists.collectAsState() @@ -168,7 +169,7 @@ fun ProfileScreen( MaterialTheme( colorScheme = if (useProfileColor) { rememberColorSchemeFor( - color = color?.toColorInt(), + color = profileColor.toColorInt(), useDarkTheme = useDarkTheme, isAmoled = isAmoled ) 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 089dde3f..cb27ba31 100644 --- a/profile/src/main/kotlin/com/imashnake/animite/profile/ProfileViewModel.kt +++ b/profile/src/main/kotlin/com/imashnake/animite/profile/ProfileViewModel.kt @@ -46,6 +46,7 @@ class ProfileViewModel @Inject constructor( .map { !it.isNullOrEmpty() } val useProfileColor = preferencesRepository.useProfileColor.filterNotNull() + val profileColor = preferencesRepository.profileColor.filterNotNull() val viewer = combine( flow = refreshTrigger.onStart { emit(Unit) }, From a8ab5c9c3e974efe18890d07dd1009bc4463e37e Mon Sep 17 00:00:00 2001 From: imashnake0 Date: Sat, 25 Jul 2026 01:16:31 -0400 Subject: [PATCH 11/16] fix --- .../com/imashnake/animite/profile/ProfileScreen.kt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) 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 15835b13..e6b14d6f 100644 --- a/profile/src/main/kotlin/com/imashnake/animite/profile/ProfileScreen.kt +++ b/profile/src/main/kotlin/com/imashnake/animite/profile/ProfileScreen.kt @@ -169,7 +169,16 @@ fun ProfileScreen( MaterialTheme( colorScheme = if (useProfileColor) { rememberColorSchemeFor( - color = profileColor.toColorInt(), + color = when(profileColor) { + "blue" -> "#007BA7" + "purple" -> "#E0AFFF" + "pink" -> "#F2BDCD" + "orange" -> "#F2B949" + "red" -> "#FA5053" + "green" -> "#0BDA51" + "gray" -> "#D9D9D9" + else -> "#007BA7" + }.toColorInt(), useDarkTheme = useDarkTheme, isAmoled = isAmoled ) From 64af190bda2cfcc431a694c17f9c1b152fdc72a3 Mon Sep 17 00:00:00 2001 From: imashnake0 Date: Sat, 25 Jul 2026 01:21:49 -0400 Subject: [PATCH 12/16] profileColorToHex --- .../api/anilist/sanitize/profile/Viewer.kt | 24 +++++++++++-------- .../animite/profile/ProfileScreen.kt | 12 ++-------- 2 files changed, 16 insertions(+), 20 deletions(-) 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 338ab182..1cb1a366 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 @@ -171,16 +171,7 @@ data class User( description = query.about, avatar = query.avatar?.large, banner = query.bannerImage, - color = when(query.options?.profileColor) { - "blue" -> "#007BA7" - "purple" -> "#E0AFFF" - "pink" -> "#F2BDCD" - "orange" -> "#F2B949" - "red" -> "#FA5053" - "green" -> "#0BDA51" - "gray" -> "#D9D9D9" - else -> "#007BA7" - }, + color = profileColorToHex(query.options?.profileColor), // TODO: Replace with string resources. stats = listOfNotNull( query.statistics?.anime?.count?.toString()?.let { @@ -247,4 +238,17 @@ data class User( fun String?.sanitize() = safeValueOf(this) } } + + companion object { + fun profileColorToHex(profileColor: String?) = when(profileColor) { + "blue" -> "#007BA7" + "purple" -> "#E0AFFF" + "pink" -> "#F2BDCD" + "orange" -> "#F2B949" + "red" -> "#FA5053" + "green" -> "#0BDA51" + "gray" -> "#D9D9D9" + else -> "#007BA7" + } + } } 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 e6b14d6f..1605bba1 100644 --- a/profile/src/main/kotlin/com/imashnake/animite/profile/ProfileScreen.kt +++ b/profile/src/main/kotlin/com/imashnake/animite/profile/ProfileScreen.kt @@ -82,6 +82,7 @@ import androidx.core.graphics.toColorInt 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.banner.NestedScrollBannerLayout import com.imashnake.animite.core.resource.Resource import com.imashnake.animite.core.ui.LocalPaddings @@ -169,16 +170,7 @@ fun ProfileScreen( MaterialTheme( colorScheme = if (useProfileColor) { rememberColorSchemeFor( - color = when(profileColor) { - "blue" -> "#007BA7" - "purple" -> "#E0AFFF" - "pink" -> "#F2BDCD" - "orange" -> "#F2B949" - "red" -> "#FA5053" - "green" -> "#0BDA51" - "gray" -> "#D9D9D9" - else -> "#007BA7" - }.toColorInt(), + color = profileColorToHex(profileColor).toColorInt(), useDarkTheme = useDarkTheme, isAmoled = isAmoled ) From 2db84c22f16b1725ec2615b64658f00e403dddcb Mon Sep 17 00:00:00 2001 From: imashnake0 Date: Sat, 25 Jul 2026 02:06:31 -0400 Subject: [PATCH 13/16] observe and update in settings vm --- .../api/anilist/AnilistUserRepository.kt | 2 +- .../animite/profile/ProfileScreen.kt | 2 +- .../animite/settings/SettingsPage.kt | 7 +++-- .../animite/settings/SettingsViewModel.kt | 30 ++++++++++++------- 4 files changed, 26 insertions(+), 15 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 f843e97c..bc6eee42 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 @@ -59,7 +59,7 @@ class AnilistUserRepository( .asResult { User.MediaCollection(it, type, language, mediaListOrder) } } - fun updateUser(profileColor: String): Flow> { + fun updateUser(profileColor: String?): Flow> { return apolloClient .mutation(UpdateUserMutation(Optional.presentIfNotNull(profileColor))) .fetchPolicy(FetchPolicy.NetworkOnly) 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 1605bba1..0bc63ac2 100644 --- a/profile/src/main/kotlin/com/imashnake/animite/profile/ProfileScreen.kt +++ b/profile/src/main/kotlin/com/imashnake/animite/profile/ProfileScreen.kt @@ -141,7 +141,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 = "#007BA7") + val profileColor by viewModel.profileColor.collectAsState(initial = "blue") val viewerAvatar by viewModel.viewerAvatar.collectAsState(initial = "") val viewer by viewModel.viewer.collectAsState() val viewerAnimeLists by viewModel.viewerAnimeLists.collectAsState() 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 8bf97f8f..c7be5c2f 100644 --- a/settings/src/main/kotlin/com/imashnake/animite/settings/SettingsPage.kt +++ b/settings/src/main/kotlin/com/imashnake/animite/settings/SettingsPage.kt @@ -90,6 +90,7 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import com.imashnake.animite.api.anilist.sanitize.media.Media import com.imashnake.animite.banner.BannerLayout import com.imashnake.animite.banner.MountFuji +import com.imashnake.animite.core.resource.Resource.Companion.loading import com.imashnake.animite.core.ui.DayPart import com.imashnake.animite.core.ui.Density import com.imashnake.animite.core.ui.LocalPaddings @@ -143,7 +144,7 @@ fun SettingsPage( val showUserDescription by viewModel.showUserDescription.collectAsState(initial = true) val useProfileColor by viewModel.useProfileColor.collectAsState(initial = true) - val profileColor by viewModel.profileColor.collectAsState(initial = "blue") + val profileColor by viewModel.profileColor.collectAsState(initial = loading()) val isDevOptionsEnabled by viewModel.isDevOptionsEnabled.collectAsState(initial = false) @@ -581,7 +582,7 @@ fun SettingsPage( ) { ProfileColor.entries.fastForEachIndexed { index, entry -> ToggleButton( - checked = profileColor.equals(entry.name, ignoreCase = true) && useProfileColor, + checked = profileColor.data.equals(entry.name, ignoreCase = true) && useProfileColor, onCheckedChange = { viewModel.setProfileColor(entry.name.lowercase()) haptic.performHapticFeedback(HapticFeedbackType.SegmentTick) @@ -600,7 +601,7 @@ fun SettingsPage( ), modifier = Modifier.weight(1f) ) { - if (profileColor.equals(entry.name, ignoreCase = true) && useProfileColor) { + if (profileColor.data.equals(entry.name, ignoreCase = true) && useProfileColor) { entry.label?.let { id -> Text( text = stringResource(id), 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 a69b6886..f401b170 100644 --- a/settings/src/main/kotlin/com/imashnake/animite/settings/SettingsViewModel.kt +++ b/settings/src/main/kotlin/com/imashnake/animite/settings/SettingsViewModel.kt @@ -7,13 +7,21 @@ import com.imashnake.animite.api.anilist.sanitize.media.Media import com.imashnake.animite.api.preferences.PreferencesRepository import com.imashnake.animite.core.model.AnimeList import com.imashnake.animite.core.model.MangaList +import com.imashnake.animite.core.resource.Resource +import com.imashnake.animite.core.resource.Resource.Companion.asResource import com.imashnake.animite.core.ui.Density import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import javax.inject.Inject @@ -46,9 +54,6 @@ class SettingsViewModel @Inject constructor( }.orEmpty().filterNotNull().toImmutableList() } val animeListsIndices = preferencesRepository.animeListsIndices.filterNotNull() - val showUserDescription = preferencesRepository.showUserDescription.filterNotNull() - val useProfileColor = preferencesRepository.useProfileColor.filterNotNull() - val mangaList = preferencesRepository.mangaListsIndices.map { indices -> indices?.map { // each int from +-1 to +-3 maps to a MangaList @@ -61,8 +66,17 @@ class SettingsViewModel @Inject constructor( }.orEmpty().filterNotNull().toImmutableList() } val mangaListsIndices = preferencesRepository.mangaListsIndices.filterNotNull() - - val profileColor = preferencesRepository.profileColor.filterNotNull() + val showUserDescription = preferencesRepository.showUserDescription.filterNotNull() + val useProfileColor = preferencesRepository.useProfileColor.filterNotNull() + val profileColor = preferencesRepository.profileColor + .filterNotNull() + .flatMapLatest { userRepository.updateUser(it) } + .asResource() + .stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(1000), + initialValue = Resource.loading(), + ) fun setTheme(theme: Theme) = viewModelScope.launch(Dispatchers.IO) { preferencesRepository.setTheme(theme.name) @@ -100,11 +114,7 @@ class SettingsViewModel @Inject constructor( } fun setProfileColor(profileColor: String) = viewModelScope.launch(Dispatchers.IO) { - userRepository.updateUser(profileColor).collect { result -> - result.onSuccess { color -> - color?.let { preferencesRepository.setProfileColor(it) } - } - } + preferencesRepository.setProfileColor(profileColor) } fun setDevOptions(enabled: Boolean) = viewModelScope.launch(Dispatchers.IO) { From 58c1b6e2d7e612298293b4c15aa74d1a0fc1ce6b Mon Sep 17 00:00:00 2001 From: imashnake0 Date: Sat, 25 Jul 2026 02:14:46 -0400 Subject: [PATCH 14/16] re: cleanup --- .../imashnake/animite/api/anilist/sanitize/profile/Viewer.kt | 2 +- .../imashnake/animite/api/preferences/PreferencesRepository.kt | 2 +- .../kotlin/com/imashnake/animite/settings/SettingsViewModel.kt | 3 --- 3 files changed, 2 insertions(+), 5 deletions(-) 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 1cb1a366..6d4a3b1d 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 @@ -171,7 +171,7 @@ data class User( description = query.about, avatar = query.avatar?.large, banner = query.bannerImage, - color = profileColorToHex(query.options?.profileColor), + color = query.options?.profileColor, // TODO: Replace with string resources. stats = listOfNotNull( query.statistics?.anime?.count?.toString()?.let { 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 3c41812e..33bc71c3 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 @@ -166,7 +166,7 @@ class PreferencesRepository internal constructor( private val profileColorKey = stringPreferencesKey("profile_color") val profileColor = dataStore.getValue(profileColorKey, PROFILE_COLOR) - suspend fun setProfileColor(profileColor: String) { + suspend fun setProfileColor(profileColor: String?) { dataStore.setValue(profileColorKey, profileColor) } 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 f401b170..475e3113 100644 --- a/settings/src/main/kotlin/com/imashnake/animite/settings/SettingsViewModel.kt +++ b/settings/src/main/kotlin/com/imashnake/animite/settings/SettingsViewModel.kt @@ -14,13 +14,10 @@ import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import javax.inject.Inject From fd76267635f1ce3db7104863a771870bfdcd8891 Mon Sep 17 00:00:00 2001 From: imashnake0 Date: Sat, 25 Jul 2026 02:20:35 -0400 Subject: [PATCH 15/16] ??? --- .../com/imashnake/animite/profile/ProfileViewModel.kt | 1 + .../com/imashnake/animite/settings/SettingsViewModel.kt | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) 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 cb27ba31..c9908b17 100644 --- a/profile/src/main/kotlin/com/imashnake/animite/profile/ProfileViewModel.kt +++ b/profile/src/main/kotlin/com/imashnake/animite/profile/ProfileViewModel.kt @@ -57,6 +57,7 @@ class ProfileViewModel @Inject constructor( }.asResource().onEach { it.data?.let { user -> preferencesRepository.setViewerId(user.id) + preferencesRepository.setProfileColor(user.color) preferencesRepository.setAnimeListOrder(user.animeListOrder) preferencesRepository.setMangaListOrder(user.mangaListOrder) } 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 475e3113..f8a2436d 100644 --- a/settings/src/main/kotlin/com/imashnake/animite/settings/SettingsViewModel.kt +++ b/settings/src/main/kotlin/com/imashnake/animite/settings/SettingsViewModel.kt @@ -18,6 +18,7 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import javax.inject.Inject @@ -67,7 +68,11 @@ class SettingsViewModel @Inject constructor( val useProfileColor = preferencesRepository.useProfileColor.filterNotNull() val profileColor = preferencesRepository.profileColor .filterNotNull() - .flatMapLatest { userRepository.updateUser(it) } + .flatMapLatest { + userRepository.updateUser(it).onEach { + preferencesRepository.setProfileColor(it.getOrNull()) + } + } .asResource() .stateIn( scope = viewModelScope, From effff775dc7f264119642c0d3a1d650f9a84a00c Mon Sep 17 00:00:00 2001 From: imashnake0 Date: Sun, 26 Jul 2026 01:04:13 -0400 Subject: [PATCH 16/16] fix behaviour if updated on anilist --- .../kotlin/com/imashnake/animite/profile/ProfileViewModel.kt | 5 ++++- .../com/imashnake/animite/settings/SettingsViewModel.kt | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) 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 c9908b17..ca8d3b14 100644 --- a/profile/src/main/kotlin/com/imashnake/animite/profile/ProfileViewModel.kt +++ b/profile/src/main/kotlin/com/imashnake/animite/profile/ProfileViewModel.kt @@ -57,7 +57,10 @@ class ProfileViewModel @Inject constructor( }.asResource().onEach { it.data?.let { user -> preferencesRepository.setViewerId(user.id) - preferencesRepository.setProfileColor(user.color) + // Don't update with cached color + if (useNetwork) { + preferencesRepository.setProfileColor(user.color) + } preferencesRepository.setAnimeListOrder(user.animeListOrder) preferencesRepository.setMangaListOrder(user.mangaListOrder) } 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 f8a2436d..48632e40 100644 --- a/settings/src/main/kotlin/com/imashnake/animite/settings/SettingsViewModel.kt +++ b/settings/src/main/kotlin/com/imashnake/animite/settings/SettingsViewModel.kt @@ -69,8 +69,8 @@ class SettingsViewModel @Inject constructor( val profileColor = preferencesRepository.profileColor .filterNotNull() .flatMapLatest { - userRepository.updateUser(it).onEach { - preferencesRepository.setProfileColor(it.getOrNull()) + userRepository.updateUser(it).onEach { profileColor -> + preferencesRepository.setProfileColor(profileColor.getOrNull()) } } .asResource()