feat(#208): add Monochrome, Gruvbox, Catppuccin and AMOLED theme presets

This commit is contained in:
Hy4ri committed 2026-06-21 13:01:55 +03:00
1 parent f738f33fc5
commit 8e8fca756a
6 files changed
+368 -36

No files matched your search

@@ -64,6 +64,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import com.m57.hermescontrol.BuildConfig
import com.m57.hermescontrol.R
import com.m57.hermescontrol.theme.ThemePreference
import com.m57.hermescontrol.theme.ThemePreset
import com.m57.hermescontrol.ui.common.HermesScaffold
@OptIn(ExperimentalMaterial3Api::class)
@@ -322,6 +323,88 @@ fun SettingsScreen(
}
}
}
Spacer(modifier = Modifier.height(16.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
Column(modifier = Modifier.weight(1f)) {
Text(
text = stringResource(R.string.settings_item_use_dynamic_colors),
style = MaterialTheme.typography.bodyLarge,
)
Text(
text = stringResource(R.string.settings_desc_use_dynamic_colors),
style =
MaterialTheme.typography.bodySmall.copy(
color = MaterialTheme.colorScheme.onSurfaceVariant,
),
)
}
Switch(
checked = state.useDynamicColors,
onCheckedChange = viewModel::onUseDynamicColorsChange,
colors =
SwitchDefaults.colors(
checkedThumbColor = MaterialTheme.colorScheme.primary,
checkedTrackColor = MaterialTheme.colorScheme.primaryContainer,
),
)
}
Spacer(modifier = Modifier.height(16.dp))
Text(
text = stringResource(R.string.settings_item_theme_preset),
style = MaterialTheme.typography.bodyLarge,
)
Spacer(modifier = Modifier.height(8.dp))
var presetsExpanded by remember { mutableStateOf(false) }
Box(modifier = Modifier.fillMaxWidth()) {
OutlinedButton(
onClick = { presetsExpanded = true },
modifier = Modifier.fillMaxWidth(),
) {
Text(
when (state.themePreset) {
ThemePreset.DEFAULT -> stringResource(R.string.theme_preset_default)
ThemePreset.MONOCHROME -> stringResource(R.string.theme_preset_monochrome)
ThemePreset.GRUVBOX -> stringResource(R.string.theme_preset_gruvbox)
ThemePreset.CATPPUCCIN -> stringResource(R.string.theme_preset_catppuccin)
ThemePreset.AMOLED -> stringResource(R.string.theme_preset_amoled)
},
)
}
DropdownMenu(
expanded = presetsExpanded,
onDismissRequest = { presetsExpanded = false },
modifier = Modifier.fillMaxWidth(0.85f),
) {
ThemePreset.entries.forEach { preset ->
DropdownMenuItem(
text = {
Text(
when (preset) {
ThemePreset.DEFAULT -> stringResource(R.string.theme_preset_default)
ThemePreset.MONOCHROME -> stringResource(R.string.theme_preset_monochrome)
ThemePreset.GRUVBOX -> stringResource(R.string.theme_preset_gruvbox)
ThemePreset.CATPPUCCIN -> stringResource(R.string.theme_preset_catppuccin)
ThemePreset.AMOLED -> stringResource(R.string.theme_preset_amoled)
},
)
},
onClick = {
viewModel.onThemePresetChange(preset)
presetsExpanded = false
},
)
}
}
}
}
// Chat section
@@ -8,6 +8,7 @@ import com.m57.hermescontrol.data.remote.ApiClient
import com.m57.hermescontrol.data.remote.NetworkResult
import com.m57.hermescontrol.data.remote.safeApiCall
import com.m57.hermescontrol.theme.ThemePreference
import com.m57.hermescontrol.theme.ThemePreset
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -22,6 +23,8 @@ data class SettingsUiState(
val token: String = "",
val autoReconnect: Boolean = true,
val themePreference: ThemePreference = ThemePreference.SYSTEM,
val useDynamicColors: Boolean = true,
val themePreset: ThemePreset = ThemePreset.DEFAULT,
val isTesting: Boolean = false,
val testResult: String? = null,
val isSaved: Boolean = false,
@@ -53,6 +56,8 @@ class SettingsViewModel : ViewModel() {
// choice from persistent storage on init. Previously this slot
// was missing — always defaulted to ThemePreference.SYSTEM.
themePreference = AuthManager.getThemePreference(),
useDynamicColors = AuthManager.isUseDynamicColors(),
themePreset = AuthManager.getThemePreset(),
selectedNavItems = AuthManager.getBottomNavItems(),
typingEffectEnabled = AuthManager.isTypingEffectEnabled(),
typingEffectDelayMs = AuthManager.getTypingEffectDelayMs(),
@@ -122,6 +127,16 @@ class SettingsViewModel : ViewModel() {
AuthManager.setThemePreference(theme)
}
fun onUseDynamicColorsChange(enabled: Boolean) {
_uiState.update { it.copy(useDynamicColors = enabled, isSaved = false) }
AuthManager.setUseDynamicColors(enabled)
}
fun onThemePresetChange(preset: ThemePreset) {
_uiState.update { it.copy(themePreset = preset, isSaved = false) }
AuthManager.setThemePreset(preset)
}
fun onTypingEffectEnabledChange(enabled: Boolean) {
_uiState.update { it.copy(typingEffectEnabled = enabled, isSaved = false) }
}
@@ -188,6 +203,8 @@ class SettingsViewModel : ViewModel() {
// B6 (Jun 18 2026, kanban t_86e9be9b): persist theme choice so it
// survives a cold start (was previously dropped on save()).
AuthManager.setThemePreference(state.themePreference)
AuthManager.setUseDynamicColors(state.useDynamicColors)
AuthManager.setThemePreset(state.themePreset)
AuthManager.setTypingEffectEnabled(state.typingEffectEnabled)
AuthManager.setTypingEffectDelayMs(state.typingEffectDelayMs)
ApiClient.rebuild()