From 339e525d346c34238a9ab73757f99dfe770dfd39 Mon Sep 17 00:00:00 2001 From: KhooLy <73142442+KhooLy@users.noreply.github.com> Date: Tue, 14 Jul 2026 01:33:22 +0300 Subject: [PATCH] Polish shared Settings screen: nav icons, profile avatar, drop restart button Adds Material icons to each Settings hub nav row (Account, General, Appearance, Playback, Catalogs, Add-ons, Downloads, Developer) and shows the active profile's avatar next to the display name at the top of the hub. Removes the "Restart app" action end-to-end (action, store handling, FluxaAppHost plumbing) since it was a no-op in both native and shared code. --- .../ui/catalog/AndroidSettingsDataSource.kt | 1 + .../com/fluxa/app/shared/FluxaAppHost.kt | 4 -- .../feature/settings/SettingsContracts.kt | 2 +- .../shared/feature/settings/SettingsRows.kt | 23 ++++++++- .../shared/feature/settings/SettingsScreen.kt | 47 +++++++++++++++---- .../shared/feature/settings/SettingsStore.kt | 1 - 6 files changed, 60 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/com/fluxa/app/ui/catalog/AndroidSettingsDataSource.kt b/app/src/main/java/com/fluxa/app/ui/catalog/AndroidSettingsDataSource.kt index 5ddbc20..f5b1026 100644 --- a/app/src/main/java/com/fluxa/app/ui/catalog/AndroidSettingsDataSource.kt +++ b/app/src/main/java/com/fluxa/app/ui/catalog/AndroidSettingsDataSource.kt @@ -121,6 +121,7 @@ class AndroidSettingsDataSource( return SettingsUiState( account = SettingsAccountUiModel( displayName = profile.profileName?.takeIf { it.isNotBlank() } ?: profile.email, + avatarUrl = profile.avatarUrl, isGuest = profile.isGuest, hasTrakt = !profile.traktAccessToken.isNullOrBlank(), hasMal = !profile.malAccessToken.isNullOrBlank(), diff --git a/shared/src/commonMain/kotlin/com/fluxa/app/shared/FluxaAppHost.kt b/shared/src/commonMain/kotlin/com/fluxa/app/shared/FluxaAppHost.kt index 800d3eb..c816ba7 100644 --- a/shared/src/commonMain/kotlin/com/fluxa/app/shared/FluxaAppHost.kt +++ b/shared/src/commonMain/kotlin/com/fluxa/app/shared/FluxaAppHost.kt @@ -82,7 +82,6 @@ fun FluxaAppHost( onConnectSimklRequested: () -> Unit = {}, onConnectAnilistRequested: () -> Unit = {}, onCheckForUpdateRequested: () -> Unit = {}, - onRestartRequested: () -> Unit = {}, onDownloadOpened: (String) -> Unit = {}, onSettingsBackRequested: () -> Unit = {}, modifier: Modifier = Modifier @@ -121,7 +120,6 @@ fun FluxaAppHost( onConnectSimklRequested = onConnectSimklRequested, onConnectAnilistRequested = onConnectAnilistRequested, onCheckForUpdateRequested = onCheckForUpdateRequested, - onRestartRequested = onRestartRequested, onDownloadOpened = onDownloadOpened, onSettingsBackRequested = onSettingsBackRequested, modifier = modifier @@ -163,7 +161,6 @@ fun FluxaAppHost( onConnectSimklRequested: () -> Unit = {}, onConnectAnilistRequested: () -> Unit = {}, onCheckForUpdateRequested: () -> Unit = {}, - onRestartRequested: () -> Unit = {}, onDownloadOpened: (String) -> Unit = {}, onSettingsBackRequested: () -> Unit = {}, modifier: Modifier = Modifier @@ -349,7 +346,6 @@ fun FluxaAppHost( SettingsAction.ConnectSimklRequested -> onConnectSimklRequested() SettingsAction.ConnectAnilistRequested -> onConnectAnilistRequested() SettingsAction.CheckForUpdateRequested -> onCheckForUpdateRequested() - SettingsAction.RestartRequested -> onRestartRequested() is SettingsAction.DownloadOpened -> onDownloadOpened(action.id) else -> scope.launch { settingsStore?.dispatch(action) } } diff --git a/shared/src/commonMain/kotlin/com/fluxa/app/shared/feature/settings/SettingsContracts.kt b/shared/src/commonMain/kotlin/com/fluxa/app/shared/feature/settings/SettingsContracts.kt index 609d0ae..c6c9ea6 100644 --- a/shared/src/commonMain/kotlin/com/fluxa/app/shared/feature/settings/SettingsContracts.kt +++ b/shared/src/commonMain/kotlin/com/fluxa/app/shared/feature/settings/SettingsContracts.kt @@ -9,6 +9,7 @@ enum class SettingsCategory { data class SettingsAccountUiModel( val displayName: String = "", + val avatarUrl: String? = null, val isGuest: Boolean = true, val hasTrakt: Boolean = false, val hasMal: Boolean = false, @@ -210,7 +211,6 @@ sealed interface SettingsAction { data object DisconnectSyncRequested : SettingsAction data object SwitchProfilesRequested : SettingsAction data object CheckForUpdateRequested : SettingsAction - data object RestartRequested : SettingsAction data class DownloadOpened(val id: String) : SettingsAction data class DownloadCancelled(val id: String) : SettingsAction } diff --git a/shared/src/commonMain/kotlin/com/fluxa/app/shared/feature/settings/SettingsRows.kt b/shared/src/commonMain/kotlin/com/fluxa/app/shared/feature/settings/SettingsRows.kt index c5f4481..1a4aec8 100644 --- a/shared/src/commonMain/kotlin/com/fluxa/app/shared/feature/settings/SettingsRows.kt +++ b/shared/src/commonMain/kotlin/com/fluxa/app/shared/feature/settings/SettingsRows.kt @@ -293,13 +293,32 @@ fun SettingsInfoRow(label: String, value: String) { } @Composable -fun SettingsNavRow(label: String, onClick: () -> Unit) { +fun SettingsNavRow(label: String, icon: androidx.compose.ui.graphics.vector.ImageVector? = null, onClick: () -> Unit) { Row( modifier = Modifier.fillMaxWidth().clickable(onClick = onClick).padding(vertical = 14.dp), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { - Text(label, color = Color.White, fontWeight = FontWeight.Medium) + Row(verticalAlignment = Alignment.CenterVertically) { + if (icon != null) { + Box( + modifier = Modifier + .size(32.dp) + .clip(RoundedCornerShape(9.dp)) + .background(Color.White.copy(alpha = 0.08f)), + contentAlignment = Alignment.Center + ) { + androidx.compose.material3.Icon( + imageVector = icon, + contentDescription = null, + tint = Color.White.copy(alpha = 0.85f), + modifier = Modifier.size(18.dp) + ) + } + Spacer(Modifier.width(14.dp)) + } + Text(label, color = Color.White, fontWeight = FontWeight.Medium) + } Text("›", color = Color.White.copy(alpha = 0.4f), fontSize = 18.sp) } } diff --git a/shared/src/commonMain/kotlin/com/fluxa/app/shared/feature/settings/SettingsScreen.kt b/shared/src/commonMain/kotlin/com/fluxa/app/shared/feature/settings/SettingsScreen.kt index 8fbb5a8..d6087f2 100644 --- a/shared/src/commonMain/kotlin/com/fluxa/app/shared/feature/settings/SettingsScreen.kt +++ b/shared/src/commonMain/kotlin/com/fluxa/app/shared/feature/settings/SettingsScreen.kt @@ -16,6 +16,15 @@ import androidx.compose.foundation.layout.width import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.AccountCircle +import androidx.compose.material.icons.filled.Code +import androidx.compose.material.icons.filled.Download +import androidx.compose.material.icons.filled.Extension +import androidx.compose.material.icons.filled.MenuBook +import androidx.compose.material.icons.filled.Palette +import androidx.compose.material.icons.filled.PlayCircle +import androidx.compose.material.icons.filled.Tune import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -112,18 +121,37 @@ private fun SettingsHubContent( onSwitchProfiles: () -> Unit, onAction: (SettingsAction) -> Unit ) { - Text(state.account.displayName, color = Color.White, fontWeight = FontWeight.Bold, fontSize = 18.sp, modifier = Modifier.padding(vertical = 8.dp)) - SettingsNavRow(AppStrings.t(lang, "auto.account")) { onNavigate(SettingsCategory.Account) } + Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(vertical = 8.dp)) { + Box( + modifier = Modifier.size(44.dp).clip(CircleShape).background(Color.White.copy(alpha = 0.1f)), + contentAlignment = Alignment.Center + ) { + if (!state.account.avatarUrl.isNullOrBlank()) { + com.fluxa.app.shared.image.FluxaRemoteImage( + imageUrl = state.account.avatarUrl, + cacheKey = "settings-avatar:${state.account.avatarUrl}", + contentDescription = null, + modifier = Modifier.fillMaxSize().clip(CircleShape), + contentScale = androidx.compose.ui.layout.ContentScale.Crop + ) + } else { + com.fluxa.app.shared.feature.profile.ProfileDefaultAvatar(modifier = Modifier.size(26.dp)) + } + } + Spacer(Modifier.width(12.dp)) + Text(state.account.displayName, color = Color.White, fontWeight = FontWeight.Bold, fontSize = 18.sp) + } + SettingsNavRow(AppStrings.t(lang, "auto.account"), icon = Icons.Filled.AccountCircle) { onNavigate(SettingsCategory.Account) } SettingsSectionHeader(AppStrings.t(lang, "settings.section_preferences")) - SettingsNavRow(AppStrings.t(lang, "auto.general")) { onNavigate(SettingsCategory.General) } - SettingsNavRow(AppStrings.t(lang, "auto.appearance")) { onNavigate(SettingsCategory.Appearance) } - SettingsNavRow(AppStrings.t(lang, "auto.playback")) { onNavigate(SettingsCategory.Playback) } + SettingsNavRow(AppStrings.t(lang, "auto.general"), icon = Icons.Filled.Tune) { onNavigate(SettingsCategory.General) } + SettingsNavRow(AppStrings.t(lang, "auto.appearance"), icon = Icons.Filled.Palette) { onNavigate(SettingsCategory.Appearance) } + SettingsNavRow(AppStrings.t(lang, "auto.playback"), icon = Icons.Filled.PlayCircle) { onNavigate(SettingsCategory.Playback) } SettingsSectionHeader(AppStrings.t(lang, "settings.section_content")) - SettingsNavRow(AppStrings.t(lang, "auto.catalogs")) { onNavigate(SettingsCategory.Content) } - SettingsNavRow(AppStrings.t(lang, "auto.add_ons")) { onNavigate(SettingsCategory.Addons) } - SettingsNavRow(AppStrings.t(lang, "auto.downloads")) { onNavigate(SettingsCategory.Downloads) } + SettingsNavRow(AppStrings.t(lang, "auto.catalogs"), icon = Icons.Filled.MenuBook) { onNavigate(SettingsCategory.Content) } + SettingsNavRow(AppStrings.t(lang, "auto.add_ons"), icon = Icons.Filled.Extension) { onNavigate(SettingsCategory.Addons) } + SettingsNavRow(AppStrings.t(lang, "auto.downloads"), icon = Icons.Filled.Download) { onNavigate(SettingsCategory.Downloads) } SettingsSectionHeader(AppStrings.t(lang, "settings.section_system")) SettingsToggleRow( @@ -133,8 +161,7 @@ private fun SettingsHubContent( onValueChanged = { onAction(SettingsAction.SystemChanged(state.system.copy(automaticUpdates = it))) } ) SettingsActionRow(AppStrings.t(lang, "settings.check_for_updates")) { onAction(SettingsAction.CheckForUpdateRequested) } - SettingsNavRow(AppStrings.t(lang, "settings.developer")) { onNavigate(SettingsCategory.Developer) } - SettingsActionRow(AppStrings.t(lang, "auto.restart"), destructive = true) { onAction(SettingsAction.RestartRequested) } + SettingsNavRow(AppStrings.t(lang, "settings.developer"), icon = Icons.Filled.Code) { onNavigate(SettingsCategory.Developer) } Spacer(Modifier.height(12.dp)) Text(state.system.appVersionLabel, color = Color.White.copy(alpha = 0.35f), fontSize = 11.sp) diff --git a/shared/src/commonMain/kotlin/com/fluxa/app/shared/feature/settings/SettingsStore.kt b/shared/src/commonMain/kotlin/com/fluxa/app/shared/feature/settings/SettingsStore.kt index 4660bd6..7ee7e84 100644 --- a/shared/src/commonMain/kotlin/com/fluxa/app/shared/feature/settings/SettingsStore.kt +++ b/shared/src/commonMain/kotlin/com/fluxa/app/shared/feature/settings/SettingsStore.kt @@ -43,7 +43,6 @@ class SettingsStore( SettingsAction.DisconnectSyncRequested -> dataSource.disconnectSync() SettingsAction.SwitchProfilesRequested -> Unit SettingsAction.CheckForUpdateRequested -> Unit - SettingsAction.RestartRequested -> Unit is SettingsAction.DownloadOpened -> Unit is SettingsAction.DownloadCancelled -> dataSource.cancelDownload(action.id) }