diff --git a/composeApp/src/androidMain/kotlin/com/nuvio/app/features/settings/ThemeSettingsStorage.android.kt b/composeApp/src/androidMain/kotlin/com/nuvio/app/features/settings/ThemeSettingsStorage.android.kt index b4a6e5c9..0d8e5b9b 100644 --- a/composeApp/src/androidMain/kotlin/com/nuvio/app/features/settings/ThemeSettingsStorage.android.kt +++ b/composeApp/src/androidMain/kotlin/com/nuvio/app/features/settings/ThemeSettingsStorage.android.kt @@ -26,7 +26,6 @@ actual object ThemeSettingsStorage { liquidGlassNativeTabBarEnabledKey, desktopNavigationLayoutKey, ) - private val globalSyncKeys = listOf(selectedAppLanguageKey) private var preferences: SharedPreferences? = null @@ -107,20 +106,17 @@ actual object ThemeSettingsStorage { loadAmoledEnabled()?.let { put(amoledEnabledKey, encodeSyncBoolean(it)) } loadLiquidGlassNativeTabBarEnabled()?.let { put(liquidGlassNativeTabBarEnabledKey, encodeSyncBoolean(it)) } loadDesktopNavigationLayout()?.let { put(desktopNavigationLayoutKey, encodeSyncString(it)) } - loadSelectedAppLanguage()?.let { put(selectedAppLanguageKey, encodeSyncString(it)) } } actual fun replaceFromSyncPayload(payload: JsonObject) { preferences?.edit()?.apply { profileScopedSyncKeys.forEach { remove(ProfileScopedKey.of(it)) } - globalSyncKeys.forEach { remove(it) } }?.apply() payload.decodeSyncString(selectedThemeKey)?.let(::saveSelectedTheme) payload.decodeSyncBoolean(amoledEnabledKey)?.let(::saveAmoledEnabled) payload.decodeSyncBoolean(liquidGlassNativeTabBarEnabledKey)?.let(::saveLiquidGlassNativeTabBarEnabled) payload.decodeSyncString(desktopNavigationLayoutKey)?.let(::saveDesktopNavigationLayout) - payload.decodeSyncString(selectedAppLanguageKey)?.let(::saveSelectedAppLanguage) applySelectedAppLanguage(loadSelectedAppLanguage() ?: AppLanguage.ENGLISH.code) } } diff --git a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/AppearanceSettingsPage.kt b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/AppearanceSettingsPage.kt index 39130eb0..07bb324b 100644 --- a/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/AppearanceSettingsPage.kt +++ b/composeApp/src/commonMain/kotlin/com/nuvio/app/features/settings/AppearanceSettingsPage.kt @@ -5,9 +5,9 @@ import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.ExperimentalLayoutApi -import androidx.compose.foundation.layout.FlowRow +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height @@ -38,6 +38,7 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.nuvio.app.isDesktop @@ -71,7 +72,6 @@ import org.jetbrains.compose.resources.stringResource import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.rememberModalBottomSheetState -@OptIn(ExperimentalLayoutApi::class) internal fun LazyListScope.appearanceSettingsContent( isTablet: Boolean, selectedTheme: AppTheme, @@ -93,28 +93,50 @@ internal fun LazyListScope.appearanceSettingsContent( ) { SettingsGroup(isTablet = isTablet) { val themes = listOf(AppTheme.WHITE) + AppTheme.entries.filterNot { it == AppTheme.WHITE } - FlowRow( + val horizontalPadding = if (isTablet) 20.dp else 16.dp + val verticalPadding = if (isTablet) 18.dp else 14.dp + val themeSpacing = if (isTablet) 16.dp else 12.dp + BoxWithConstraints( modifier = Modifier .fillMaxWidth() .padding( - horizontal = if (isTablet) 20.dp else 16.dp, - vertical = if (isTablet) 18.dp else 14.dp, + horizontal = horizontalPadding, + vertical = verticalPadding, ), - horizontalArrangement = Arrangement.spacedBy(if (isTablet) 16.dp else 12.dp), - verticalArrangement = Arrangement.spacedBy(if (isTablet) 16.dp else 12.dp), ) { - themes.forEach { theme -> - ThemeChip( - theme = theme, - isSelected = theme == selectedTheme, - onClick = { onThemeSelected(theme) }, - ) + val preferredColumns = if (isTablet) 4 else 3 + val minThemeCellWidth = if (isTablet) 92.dp else 78.dp + val themeColumns = ((maxWidth + themeSpacing) / (minThemeCellWidth + themeSpacing)) + .toInt() + .coerceAtLeast(1) + .coerceAtMost(preferredColumns) + + Column( + verticalArrangement = Arrangement.spacedBy(themeSpacing), + ) { + themes.chunked(themeColumns).forEach { rowThemes -> + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(themeSpacing), + ) { + rowThemes.forEach { theme -> + ThemeChip( + theme = theme, + isSelected = theme == selectedTheme, + onClick = { onThemeSelected(theme) }, + modifier = Modifier.weight(1f), + ) + } + repeat(themeColumns - rowThemes.size) { + Spacer(modifier = Modifier.weight(1f)) + } + } + } } } } } } - item { var showLanguageSheet by remember { mutableStateOf(false) } var showDesktopNavigationSheet by remember { mutableStateOf(false) } @@ -352,41 +374,48 @@ private fun ThemeChip( theme: AppTheme, isSelected: Boolean, onClick: () -> Unit, + modifier: Modifier = Modifier, ) { val palette = ThemeColors.getColorPalette(theme) Column( - modifier = Modifier + modifier = modifier .clip(RoundedCornerShape(12.dp)) .clickable(onClick = onClick) - .then( - if (isSelected) { - Modifier.border( - width = 1.5.dp, - color = palette.focusRing, - shape = RoundedCornerShape(12.dp), - ) - } else { - Modifier - }, - ) - .padding(horizontal = 12.dp, vertical = 10.dp), + .padding(horizontal = 4.dp, vertical = 8.dp), horizontalAlignment = Alignment.CenterHorizontally, ) { Box( modifier = Modifier - .size(44.dp) - .clip(CircleShape) - .background(palette.secondary), + .size(56.dp) + .then( + if (isSelected) { + Modifier.border( + width = 1.5.dp, + color = palette.focusRing, + shape = RoundedCornerShape(14.dp), + ) + } else { + Modifier + }, + ), contentAlignment = Alignment.Center, ) { - if (isSelected) { - Icon( - imageVector = Icons.Default.Check, - contentDescription = stringResource(Res.string.cd_selected), - tint = palette.onSecondary, - modifier = Modifier.size(22.dp), - ) + Box( + modifier = Modifier + .size(44.dp) + .clip(CircleShape) + .background(palette.secondary), + contentAlignment = Alignment.Center, + ) { + if (isSelected) { + Icon( + imageVector = Icons.Default.Check, + contentDescription = stringResource(Res.string.cd_selected), + tint = palette.onSecondary, + modifier = Modifier.size(22.dp), + ) + } } } @@ -402,6 +431,9 @@ private fun ThemeChip( }, fontWeight = if (isSelected) FontWeight.SemiBold else FontWeight.Medium, textAlign = TextAlign.Center, + maxLines = 2, + overflow = TextOverflow.Ellipsis, + modifier = Modifier.fillMaxWidth(), ) Spacer(modifier = Modifier.height(4.dp)) diff --git a/composeApp/src/desktopMain/kotlin/com/nuvio/app/features/settings/ThemeSettingsStorage.desktop.kt b/composeApp/src/desktopMain/kotlin/com/nuvio/app/features/settings/ThemeSettingsStorage.desktop.kt index 68891f4a..0ca7369e 100644 --- a/composeApp/src/desktopMain/kotlin/com/nuvio/app/features/settings/ThemeSettingsStorage.desktop.kt +++ b/composeApp/src/desktopMain/kotlin/com/nuvio/app/features/settings/ThemeSettingsStorage.desktop.kt @@ -70,16 +70,14 @@ internal actual object ThemeSettingsStorage { loadAmoledEnabled()?.let { put(amoledEnabledKey, encodeSyncBoolean(it)) } loadLiquidGlassNativeTabBarEnabled()?.let { put(liquidGlassNativeTabBarEnabledKey, encodeSyncBoolean(it)) } loadDesktopNavigationLayout()?.let { put(desktopNavigationLayoutKey, encodeSyncString(it)) } - loadSelectedAppLanguage()?.let { put(selectedAppLanguageKey, encodeSyncString(it)) } } actual fun replaceFromSyncPayload(payload: JsonObject) { - store.removeAll(profileScopedSyncKeys.map(ProfileScopedKey::of) + selectedAppLanguageKey) + store.removeAll(profileScopedSyncKeys.map(ProfileScopedKey::of)) payload.decodeSyncString(selectedThemeKey)?.let(::saveSelectedTheme) payload.decodeSyncBoolean(amoledEnabledKey)?.let(::saveAmoledEnabled) payload.decodeSyncBoolean(liquidGlassNativeTabBarEnabledKey)?.let(::saveLiquidGlassNativeTabBarEnabled) payload.decodeSyncString(desktopNavigationLayoutKey)?.let(::saveDesktopNavigationLayout) - payload.decodeSyncString(selectedAppLanguageKey)?.let(::saveSelectedAppLanguage) applySelectedAppLanguage(loadSelectedAppLanguage() ?: AppLanguage.ENGLISH.code) } } diff --git a/composeApp/src/iosMain/kotlin/com/nuvio/app/features/settings/ThemeSettingsStorage.ios.kt b/composeApp/src/iosMain/kotlin/com/nuvio/app/features/settings/ThemeSettingsStorage.ios.kt index dcef54f8..1737c08e 100644 --- a/composeApp/src/iosMain/kotlin/com/nuvio/app/features/settings/ThemeSettingsStorage.ios.kt +++ b/composeApp/src/iosMain/kotlin/com/nuvio/app/features/settings/ThemeSettingsStorage.ios.kt @@ -22,7 +22,6 @@ actual object ThemeSettingsStorage { liquidGlassNativeTabBarEnabledKey, desktopNavigationLayoutKey, ) - private val globalSyncKeys = listOf(selectedAppLanguageKey) actual fun loadSelectedTheme(): String? = NSUserDefaults.standardUserDefaults.stringForKey(ProfileScopedKey.of(selectedThemeKey)) @@ -101,22 +100,17 @@ actual object ThemeSettingsStorage { loadAmoledEnabled()?.let { put(amoledEnabledKey, encodeSyncBoolean(it)) } loadLiquidGlassNativeTabBarEnabled()?.let { put(liquidGlassNativeTabBarEnabledKey, encodeSyncBoolean(it)) } loadDesktopNavigationLayout()?.let { put(desktopNavigationLayoutKey, encodeSyncString(it)) } - loadSelectedAppLanguage()?.let { put(selectedAppLanguageKey, encodeSyncString(it)) } } actual fun replaceFromSyncPayload(payload: JsonObject) { profileScopedSyncKeys.forEach { key -> NSUserDefaults.standardUserDefaults.removeObjectForKey(ProfileScopedKey.of(key)) } - globalSyncKeys.forEach { key -> - NSUserDefaults.standardUserDefaults.removeObjectForKey(key) - } payload.decodeSyncString(selectedThemeKey)?.let(::saveSelectedTheme) payload.decodeSyncBoolean(amoledEnabledKey)?.let(::saveAmoledEnabled) payload.decodeSyncBoolean(liquidGlassNativeTabBarEnabledKey)?.let(::saveLiquidGlassNativeTabBarEnabled) payload.decodeSyncString(desktopNavigationLayoutKey)?.let(::saveDesktopNavigationLayout) - payload.decodeSyncString(selectedAppLanguageKey)?.let(::saveSelectedAppLanguage) applySelectedAppLanguage(loadSelectedAppLanguage() ?: AppLanguage.ENGLISH.code) } }