diff --git a/app/src/main/java/com/ivy/wallet/ui/settings/SettingsScreen.kt b/app/src/main/java/com/ivy/wallet/ui/settings/SettingsScreen.kt index f80844b767..4ddd618557 100644 --- a/app/src/main/java/com/ivy/wallet/ui/settings/SettingsScreen.kt +++ b/app/src/main/java/com/ivy/wallet/ui/settings/SettingsScreen.kt @@ -46,6 +46,7 @@ import androidx.compose.ui.unit.sp import androidx.lifecycle.viewmodel.compose.viewModel import coil.compose.AsyncImage import com.ivy.design.l0_system.SunsetNight +import com.ivy.design.l0_system.Theme import com.ivy.design.l0_system.UI import com.ivy.design.l0_system.style import com.ivy.design.l1_buildingBlocks.IconScale @@ -104,6 +105,7 @@ fun BoxWithConstraintsScope.SettingsScreen(screen: Settings) { val user by viewModel.user.observeAsState() val opSync by viewModel.opSync.observeAsState() + val theme by viewModel.currentTheme.observeAsState(Theme.AUTO) val currencyCode by viewModel.currencyCode.observeAsState("") val lockApp by viewModel.lockApp.observeAsState(false) val showNotifications by viewModel.showNotifications.collectAsState() @@ -124,6 +126,8 @@ fun BoxWithConstraintsScope.SettingsScreen(screen: Settings) { user = user, currencyCode = currencyCode, opSync = opSync, + theme = theme, + onSwitchTheme = viewModel::switchTheme, lockApp = lockApp, showNotifications = showNotifications, hideCurrentBalance = hideCurrentBalance, @@ -162,6 +166,9 @@ private fun BoxWithConstraintsScope.UI( currencyCode: String, opSync: OpResult?, + theme: Theme, + onSwitchTheme: () -> Unit, + lockApp: Boolean, showNotifications: Boolean = true, hideCurrentBalance: Boolean = false, @@ -187,7 +194,7 @@ private fun BoxWithConstraintsScope.UI( onDeleteAllUserData: () -> Unit = {}, onDeleteCloudUserData: () -> Unit = {}, -) { + ) { var currencyModalVisible by remember { mutableStateOf(false) } var nameModalVisible by remember { mutableStateOf(false) } var chooseStartDateOfMonthVisible by remember { mutableStateOf(false) } @@ -310,6 +317,23 @@ private fun BoxWithConstraintsScope.UI( Spacer(Modifier.height(16.dp)) + AppThemeButton( + icon = when (theme) { + Theme.LIGHT -> R.drawable.home_more_menu_light_mode + Theme.DARK -> R.drawable.home_more_menu_dark_mode + Theme.AUTO -> R.drawable.home_more_menu_auto_mode + }, + label = when (theme) { + Theme.LIGHT -> stringResource(R.string.light_mode) + Theme.DARK -> stringResource(R.string.dark_mode) + Theme.AUTO -> stringResource(R.string.auto_mode) + } + ) { + onSwitchTheme() + } + + Spacer(Modifier.height(12.dp)) + val nav = navigation() SettingsDefaultButton( icon = R.drawable.ic_currency, @@ -676,6 +700,23 @@ private fun ProjectContributors() { } } +@Composable +private fun AppThemeButton( + @DrawableRes icon: Int, + label: String, + onClick: () -> Unit +) { + SettingsPrimaryButton( + icon = icon, + text = label, + backgroundGradient = Gradient.solid(UI.colors.medium), + textColor = UI.colors.pureInverse, + iconPadding = 6.dp, + description = "Tap to switch theme", + onClick = onClick + ) +} + @Composable private fun AppSwitch( lockApp: Boolean, @@ -1081,6 +1122,7 @@ private fun SettingsPrimaryButton( backgroundGradient: Gradient = Gradient.solid(UI.colors.medium), textColor: Color = White, iconPadding: Dp = 0.dp, + description: String = "", onClick: () -> Unit ) { SettingsButtonRow( @@ -1099,14 +1141,29 @@ private fun SettingsPrimaryButton( Spacer(Modifier.width(8.dp)) - Text( - modifier = Modifier.padding(vertical = 20.dp), - text = text, - style = UI.typo.b2.style( - color = textColor, - fontWeight = FontWeight.Bold + Column( + Modifier + .weight(1f) + .padding(top = 20.dp, bottom = 20.dp, end = 8.dp) + ) { + Text( + text = text, + style = UI.typo.b2.style( + color = UI.colors.pureInverse, + fontWeight = FontWeight.Bold + ) ) - ) + if (description.isNotEmpty()) { + Text( + modifier = Modifier.padding(end = 8.dp), + text = description, + style = UI.typo.nB2.style( + color = Gray, + fontWeight = FontWeight.Normal + ).copy(fontSize = 14.sp) + ) + } + } } } @@ -1312,6 +1369,8 @@ private fun Preview_synced() { ), nameLocalAccount = null, opSync = OpResult.success(true), + theme = Theme.AUTO, + onSwitchTheme = {}, lockApp = false, currencyCode = "BGN", onSetCurrency = {}, @@ -1337,6 +1396,8 @@ private fun Preview_notSynced() { id = UUID.randomUUID(), profilePicture = null ), + theme = Theme.AUTO, + onSwitchTheme = {}, lockApp = false, nameLocalAccount = null, opSync = OpResult.success(false), @@ -1364,6 +1425,8 @@ private fun Preview_loading() { id = UUID.randomUUID(), profilePicture = null ), + theme = Theme.AUTO, + onSwitchTheme = {}, lockApp = false, nameLocalAccount = null, opSync = OpResult.loading(), @@ -1386,6 +1449,8 @@ private fun Preview_localAccount() { nameLocalAccount = "Iliyan", opSync = null, currencyCode = "BGN", + theme = Theme.AUTO, + onSwitchTheme = {}, lockApp = false, onSetCurrency = {}, onLogout = {}, diff --git a/app/src/main/java/com/ivy/wallet/ui/settings/SettingsViewModel.kt b/app/src/main/java/com/ivy/wallet/ui/settings/SettingsViewModel.kt index 5a35f9b8fe..cf6d2a0d44 100644 --- a/app/src/main/java/com/ivy/wallet/ui/settings/SettingsViewModel.kt +++ b/app/src/main/java/com/ivy/wallet/ui/settings/SettingsViewModel.kt @@ -4,12 +4,15 @@ import android.content.Context import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.ivy.design.l0_system.Theme import com.ivy.frp.monad.Res import com.ivy.frp.test.TestIdlingResource import com.ivy.frp.view.navigation.Navigation import com.ivy.wallet.domain.action.exchange.SyncExchangeRatesAct import com.ivy.wallet.domain.action.global.StartDayOfMonthAct import com.ivy.wallet.domain.action.global.UpdateStartDayOfMonthAct +import com.ivy.wallet.domain.action.settings.SettingsAct +import com.ivy.wallet.domain.action.settings.UpdateSettingsAct import com.ivy.wallet.domain.data.core.User import com.ivy.wallet.domain.deprecated.logic.LogoutLogic import com.ivy.wallet.domain.deprecated.logic.csv.ExportCSVLogic @@ -49,6 +52,8 @@ class SettingsViewModel @Inject constructor( private val updateStartDayOfMonthAct: UpdateStartDayOfMonthAct, private val nav: Navigation, private val syncExchangeRatesAct: SyncExchangeRatesAct, + private val settingsAct: SettingsAct, + private val updateSettingsAct: UpdateSettingsAct, ) : ViewModel() { private val _user = MutableLiveData() @@ -63,6 +68,9 @@ class SettingsViewModel @Inject constructor( private val _currencyCode = MutableLiveData() val currencyCode = _currencyCode.asLiveData() + private val _currentTheme = MutableLiveData() + val currentTheme = _currentTheme.asLiveData() + private val _lockApp = MutableLiveData() val lockApp = _lockApp.asLiveData() @@ -99,6 +107,8 @@ class SettingsViewModel @Inject constructor( _user.value = null _currencyCode.value = settings.currency + _currentTheme.value = settingsAct(Unit).theme + _lockApp.value = sharedPrefs.getBoolean(SharedPrefs.APP_LOCK_ENABLED, false) _hideCurrentBalance.value = sharedPrefs.getBoolean(SharedPrefs.HIDE_CURRENT_BALANCE, false) @@ -271,6 +281,24 @@ class SettingsViewModel @Inject constructor( } } + fun switchTheme() { + viewModelScope.launch { + val currentSettings = settingsAct(Unit) + val newTheme = when (currentSettings.theme) { + Theme.LIGHT -> Theme.DARK + Theme.DARK -> Theme.AUTO + Theme.AUTO -> Theme.LIGHT + } + updateSettingsAct( + currentSettings.copy( + theme = newTheme + ) + ) + ivyContext.switchTheme(newTheme) + _currentTheme.value = newTheme + } + } + fun setLockApp(lockApp: Boolean) { viewModelScope.launch { TestIdlingResource.increment()