Better (?) MutableStateFlows approach

This commit is contained in:
Sad Ellie 2022-09-03 13:01:06 +03:00
parent 0ffec5e15a
commit 66227f4ed7
6 changed files with 151 additions and 125 deletions

View File

@ -26,6 +26,7 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect import androidx.compose.runtime.SideEffect
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavGraphBuilder import androidx.navigation.NavGraphBuilder
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost import androidx.navigation.compose.NavHost
@ -41,8 +42,8 @@ import com.sadellie.unitto.data.NavRoutes.SETTINGS_GRAPH
import com.sadellie.unitto.data.NavRoutes.SETTINGS_SCREEN import com.sadellie.unitto.data.NavRoutes.SETTINGS_SCREEN
import com.sadellie.unitto.data.NavRoutes.THEMES_SCREEN import com.sadellie.unitto.data.NavRoutes.THEMES_SCREEN
import com.sadellie.unitto.data.NavRoutes.UNIT_GROUPS_SCREEN import com.sadellie.unitto.data.NavRoutes.UNIT_GROUPS_SCREEN
import com.sadellie.unitto.screens.main.MainViewModel
import com.sadellie.unitto.screens.main.MainScreen import com.sadellie.unitto.screens.main.MainScreen
import com.sadellie.unitto.screens.main.MainViewModel
import com.sadellie.unitto.screens.second.LeftSideScreen import com.sadellie.unitto.screens.second.LeftSideScreen
import com.sadellie.unitto.screens.second.RightSideScreen import com.sadellie.unitto.screens.second.RightSideScreen
import com.sadellie.unitto.screens.second.SecondViewModel import com.sadellie.unitto.screens.second.SecondViewModel
@ -67,15 +68,15 @@ class MainActivity : ComponentActivity() {
setContent { setContent {
val settingsViewModel: SettingsViewModel = hiltViewModel() val settingsViewModel: SettingsViewModel = hiltViewModel()
val userPrefs = settingsViewModel.userPrefs val userPrefs = settingsViewModel.userPrefs.collectAsStateWithLifecycle()
val themmoController = rememberThemmoController( val themmoController = rememberThemmoController(
lightColorScheme = LightThemeColors, lightColorScheme = LightThemeColors,
darkColorScheme = DarkThemeColors, darkColorScheme = DarkThemeColors,
// Anything below will not called if theming mode is still loading from DataStore // Anything below will not called if theming mode is still loading from DataStore
themingMode = userPrefs.themingMode ?: return@setContent, themingMode = userPrefs.value.themingMode ?: return@setContent,
dynamicThemeEnabled = userPrefs.enableDynamicTheme, dynamicThemeEnabled = userPrefs.value.enableDynamicTheme,
amoledThemeEnabled = userPrefs.enableAmoledTheme amoledThemeEnabled = userPrefs.value.enableAmoledTheme
) )
val navController = rememberNavController() val navController = rememberNavController()
val sysUiController = rememberSystemUiController() val sysUiController = rememberSystemUiController()

View File

@ -28,6 +28,8 @@ import com.sadellie.unitto.data.KEY_DOT
import com.sadellie.unitto.data.KEY_E import com.sadellie.unitto.data.KEY_E
import com.sadellie.unitto.data.preferences.Separator import com.sadellie.unitto.data.preferences.Separator
import com.sadellie.unitto.data.units.AbstractUnit import com.sadellie.unitto.data.units.AbstractUnit
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import java.math.BigDecimal import java.math.BigDecimal
import java.math.RoundingMode import java.math.RoundingMode
import java.text.NumberFormat import java.text.NumberFormat
@ -222,3 +224,27 @@ fun Sequence<AbstractUnit>.sortByLev(stringA: String): Sequence<AbstractUnit> {
.map { it.first } .map { it.first }
.asSequence() .asSequence()
} }
@Suppress("UNCHECKED_CAST")
fun <T1, T2, T3, T4, T5, T6, T7, T8, R> combine(
flow: Flow<T1>,
flow2: Flow<T2>,
flow3: Flow<T3>,
flow4: Flow<T4>,
flow5: Flow<T5>,
flow6: Flow<T6>,
flow7: Flow<T7>,
flow8: Flow<T8>,
transform: suspend (T1, T2, T3, T4, T5, T6, T7, T8) -> R
): Flow<R> = combine(flow, flow2, flow3, flow4, flow5, flow6, flow7, flow8) { args: Array<*> ->
transform(
args[0] as T1,
args[1] as T2,
args[2] as T3,
args[3] as T4,
args[4] as T5,
args[5] as T6,
args[6] as T7,
args[7] as T8
)
}

View File

@ -19,7 +19,6 @@
package com.sadellie.unitto.screens.main package com.sadellie.unitto.screens.main
import android.app.Application import android.app.Application
import android.util.Log
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
@ -40,12 +39,13 @@ import com.sadellie.unitto.data.units.database.MyBasedUnit
import com.sadellie.unitto.data.units.database.MyBasedUnitsRepository import com.sadellie.unitto.data.units.database.MyBasedUnitsRepository
import com.sadellie.unitto.data.units.remote.CurrencyApi import com.sadellie.unitto.data.units.remote.CurrencyApi
import com.sadellie.unitto.data.units.remote.CurrencyUnitResponse import com.sadellie.unitto.data.units.remote.CurrencyUnitResponse
import com.sadellie.unitto.screens.combine
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.math.BigDecimal import java.math.BigDecimal
import java.math.RoundingMode import java.math.RoundingMode
@ -58,12 +58,39 @@ class MainViewModel @Inject constructor(
private val application: Application, private val application: Application,
private val allUnitsRepository: AllUnitsRepository private val allUnitsRepository: AllUnitsRepository
) : ViewModel() { ) : ViewModel() {
private var userPrefs = UserPreferences()
/** private val _inputValue: MutableStateFlow<String> = MutableStateFlow(KEY_0)
* UI state private val _deleteButtonEnabled: MutableStateFlow<Boolean> = MutableStateFlow(false)
*/ private val _dotButtonEnabled: MutableStateFlow<Boolean> = MutableStateFlow(true)
private val _mainUIState = MutableStateFlow(MainScreenUIState()) private val _negateButtonEnabled: MutableStateFlow<Boolean> = MutableStateFlow(false)
private val _isLoadingDatabase: MutableStateFlow<Boolean> = MutableStateFlow(true)
private val _isLoadingNetwork: MutableStateFlow<Boolean> = MutableStateFlow(false)
private val _showError: MutableStateFlow<Boolean> = MutableStateFlow(false)
private val _userPrefs = userPrefsRepository.userPreferencesFlow
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), UserPreferences())
val mainFlow = combine(
_inputValue,
_deleteButtonEnabled,
_dotButtonEnabled,
_negateButtonEnabled,
_isLoadingDatabase,
_isLoadingNetwork,
_showError,
_userPrefs
) { inputValue, deleteButtonEnabled, dotButtonEnabled, negateButtonEnabled, isLoadingDatabase, isLoadingNetwork, showError, _ ->
return@combine MainScreenUIState(
inputValue = inputValue,
resultValue = convertValue(),
deleteButtonEnabled = deleteButtonEnabled,
dotButtonEnabled = dotButtonEnabled,
negateButtonEnabled = negateButtonEnabled,
isLoadingDatabase = isLoadingDatabase,
isLoadingNetwork = isLoadingNetwork,
showError = showError
)
}
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), MainScreenUIState())
/** /**
* Unit we converting from (left side) * Unit we converting from (left side)
@ -77,27 +104,16 @@ class MainViewModel @Inject constructor(
var unitTo: AbstractUnit by mutableStateOf(allUnitsRepository.getById(MyUnitIDS.mile)) var unitTo: AbstractUnit by mutableStateOf(allUnitsRepository.getById(MyUnitIDS.mile))
private set private set
val mainFlow = combine(_mainUIState, userPrefsRepository.userPreferencesFlow) { UIState, prefs ->
userPrefs = prefs
convertValue()
return@combine UIState
}
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000),
initialValue = MainScreenUIState()
)
/** /**
* This function takes local variables, converts values and then causes the UI to update * This function takes local variables, converts values and then causes the UI to update
*/ */
private fun convertValue() { private fun convertValue(): String {
// Converting value using a specified precision // Converting value using a specified precision
val convertedValue: BigDecimal = val convertedValue: BigDecimal =
unitFrom.convert( unitFrom.convert(
unitTo, unitTo,
_mainUIState.value.inputValue.toBigDecimal(), _inputValue.value.toBigDecimal(),
userPrefs.digitsPrecision _userPrefs.value.digitsPrecision
) )
/** /**
@ -107,20 +123,20 @@ class MainViewModel @Inject constructor(
*/ */
val resultValue = val resultValue =
if (convertedValue == BigDecimal.ZERO.setScale( if (convertedValue == BigDecimal.ZERO.setScale(
userPrefs.digitsPrecision, _userPrefs.value.digitsPrecision,
RoundingMode.HALF_EVEN RoundingMode.HALF_EVEN
) )
) { ) {
KEY_0 KEY_0
} else { } else {
// Setting result value using a specified OutputFormat // Setting result value using a specified OutputFormat
when (userPrefs.outputFormat) { when (_userPrefs.value.outputFormat) {
OutputFormat.ALLOW_ENGINEERING -> convertedValue.toString() OutputFormat.ALLOW_ENGINEERING -> convertedValue.toString()
OutputFormat.FORCE_ENGINEERING -> convertedValue.toEngineeringString() OutputFormat.FORCE_ENGINEERING -> convertedValue.toEngineeringString()
else -> convertedValue.toPlainString() else -> convertedValue.toPlainString()
} }
} }
_mainUIState.value = _mainUIState.value.copy(resultValue = resultValue) return resultValue
} }
/** /**
@ -133,16 +149,10 @@ class MainViewModel @Inject constructor(
unitFrom = clickedUnit unitFrom = clickedUnit
// Now we check for negate button // Now we check for negate button
_mainUIState.value = _negateButtonEnabled.update { clickedUnit.group.canNegate }
_mainUIState.value.copy(negateButtonEnabled = clickedUnit.group.canNegate)
// Now we change to positive if the group we switched to supports negate // Now we change to positive if the group we switched to supports negate
if (!clickedUnit.group.canNegate) { if (!clickedUnit.group.canNegate) {
_mainUIState.value = _inputValue.update { _inputValue.value.removePrefix(KEY_MINUS) }
_mainUIState.value.copy(
inputValue = _mainUIState.value.inputValue.removePrefix(
KEY_MINUS
)
)
} }
// Now setting up right unit (pair for the left one) // Now setting up right unit (pair for the left one)
@ -157,8 +167,6 @@ class MainViewModel @Inject constructor(
incrementCounter(clickedUnit) incrementCounter(clickedUnit)
// Currencies require us to get data from the internet // Currencies require us to get data from the internet
updateCurrenciesBasicUnits() updateCurrenciesBasicUnits()
// We can't call outside of this block. It will set precision to 0 in that case
convertValue()
// Saving latest pair // Saving latest pair
saveLatestPairOfUnits() saveLatestPairOfUnits()
} }
@ -190,9 +198,6 @@ class MainViewModel @Inject constructor(
// Saving latest pair // Saving latest pair
saveLatestPairOfUnits() saveLatestPairOfUnits()
} }
// Changed units, now we can convert
convertValue()
} }
private suspend fun incrementCounter(unit: AbstractUnit) { private suspend fun incrementCounter(unit: AbstractUnit) {
@ -213,12 +218,13 @@ class MainViewModel @Inject constructor(
*/ */
private suspend fun updateCurrenciesBasicUnits() { private suspend fun updateCurrenciesBasicUnits() {
// Resetting error and network loading states in case we are not gonna do anything below // Resetting error and network loading states in case we are not gonna do anything below
_mainUIState.value = _mainUIState.value.copy(isLoadingNetwork = false, showError = false) _isLoadingNetwork.update { false }
_showError.update { false }
// We update currencies only when needed // We update currencies only when needed
if (unitFrom.group != UnitGroup.CURRENCY) return if (unitFrom.group != UnitGroup.CURRENCY) return
// Starting to load stuff // Starting to load stuff
_mainUIState.value = _mainUIState.value.copy(isLoadingNetwork = true) _isLoadingNetwork.update { true }
try { try {
val pairs: CurrencyUnitResponse = val pairs: CurrencyUnitResponse =
@ -233,10 +239,10 @@ class MainViewModel @Inject constructor(
FirebaseHelper().recordException(e) FirebaseHelper().recordException(e)
} }
} }
_mainUIState.value = _mainUIState.value.copy(showError = true) _showError.update { true }
} finally { } finally {
// Loaded // Loaded
_mainUIState.value = _mainUIState.value.copy(isLoadingNetwork = false) _isLoadingNetwork.update { false }
} }
} }
@ -251,8 +257,6 @@ class MainViewModel @Inject constructor(
updateCurrenciesBasicUnits() updateCurrenciesBasicUnits()
saveLatestPairOfUnits() saveLatestPairOfUnits()
} }
// Swapped, can convert now
convertValue()
} }
/** /**
@ -266,17 +270,14 @@ class MainViewModel @Inject constructor(
// Here we add a dot to input // Here we add a dot to input
// Disabling dot button to avoid multiple dots in input value // Disabling dot button to avoid multiple dots in input value
// Enabling delete button to so that we can delete this dot from input // Enabling delete button to so that we can delete this dot from input
_mainUIState.value = _mainUIState.value.copy( _inputValue.update { _inputValue.value + digitToAdd }
inputValue = _mainUIState.value.inputValue + digitToAdd, _dotButtonEnabled.update { false }
dotButtonEnabled = false, _deleteButtonEnabled.update { true }
deleteButtonEnabled = true
)
} }
KEY_0 -> { KEY_0 -> {
// We shouldn't add zero to another zero in input, i.e. 00 // We shouldn't add zero to another zero in input, i.e. 00
if (_mainUIState.value.inputValue != KEY_0) { if (_inputValue.value != KEY_0) {
_mainUIState.value = _inputValue.update { _inputValue.value + digitToAdd }
_mainUIState.value.copy(inputValue = _mainUIState.value.inputValue + digitToAdd)
} }
} }
else -> { else -> {
@ -285,13 +286,12 @@ class MainViewModel @Inject constructor(
When there is just a zero, we should replace it with the digit we want to add, When there is just a zero, we should replace it with the digit we want to add,
avoids input to be like 03 (with this check it will be just 3) avoids input to be like 03 (with this check it will be just 3)
*/ */
_mainUIState.value = _mainUIState.value.copy( _inputValue.update {
inputValue = if (_mainUIState.value.inputValue == KEY_0) digitToAdd else _mainUIState.value.inputValue + digitToAdd, if (_inputValue.value == KEY_0) digitToAdd else _inputValue.value + digitToAdd
deleteButtonEnabled = true }
) _deleteButtonEnabled.update { true }
} }
} }
convertValue()
} }
/** /**
@ -300,13 +300,12 @@ class MainViewModel @Inject constructor(
fun deleteDigit() { fun deleteDigit() {
// Last symbol is a dot // Last symbol is a dot
// We enable DOT button // We enable DOT button
if (_mainUIState.value.inputValue.endsWith(KEY_DOT)) { if (_inputValue.value.endsWith(KEY_DOT)) {
_mainUIState.value = _mainUIState.value.copy(dotButtonEnabled = true) _dotButtonEnabled.update { true }
} }
// Deleting last symbol // Deleting last symbol
_mainUIState.value = var inputToSet = _inputValue.value.dropLast(1)
_mainUIState.value.copy(inputValue = _mainUIState.value.inputValue.dropLast(1))
/* /*
Now we check what we have left Now we check what we have left
@ -316,42 +315,37 @@ class MainViewModel @Inject constructor(
Skipping this block means that we are left we acceptable value, i.e. 123.03 Skipping this block means that we are left we acceptable value, i.e. 123.03
*/ */
if ( if (
_mainUIState.value.inputValue in listOf(String(), KEY_MINUS, KEY_0) inputToSet in listOf(String(), KEY_MINUS, KEY_0)
) { ) {
_mainUIState.value = _deleteButtonEnabled.update { false }
_mainUIState.value.copy(deleteButtonEnabled = false, inputValue = KEY_0) inputToSet = KEY_0
} }
// We are sure that input has acceptable value, so we convert it _inputValue.update { inputToSet }
convertValue()
} }
/** /**
* Clears input value and sets it to default (ZERO) * Clears input value and sets it to default (ZERO)
*/ */
fun clearInput() { fun clearInput() {
_mainUIState.value = _mainUIState.value.copy( _inputValue.update { KEY_0 }
inputValue = KEY_0, _deleteButtonEnabled.update { false }
deleteButtonEnabled = false, _dotButtonEnabled.update { true }
dotButtonEnabled = true
)
convertValue()
} }
/** /**
* Changes input from positive to negative and vice versa * Changes input from positive to negative and vice versa
*/ */
fun negateInput() { fun negateInput() {
_mainUIState.value = _mainUIState.value.copy( _inputValue.update {
inputValue = if (_mainUIState.value.inputValue.getOrNull(0) != KEY_MINUS.single()) { if (_inputValue.value.getOrNull(0) != KEY_MINUS.single()) {
// If input doesn't have minus at the beginning, we give it to it // If input doesn't have minus at the beginning, we give it to it
KEY_MINUS + _mainUIState.value.inputValue KEY_MINUS + _inputValue.value
} else { } else {
// Input has minus, meaning we need to remove it // Input has minus, meaning we need to remove it
_mainUIState.value.inputValue.removePrefix(KEY_MINUS) _inputValue.value.removePrefix(KEY_MINUS)
} }
) }
convertValue()
} }
/** /**
@ -363,34 +357,29 @@ class MainViewModel @Inject constructor(
init { init {
viewModelScope.launch { viewModelScope.launch {
userPrefs = userPrefsRepository.userPreferencesFlow.first() val initialUserPrefs = userPrefsRepository.userPreferencesFlow.first()
// First we load latest pair of units // First we load latest pair of units
unitFrom = try { unitFrom = try {
allUnitsRepository.getById(userPrefs.latestLeftSideUnit) allUnitsRepository.getById(initialUserPrefs.latestLeftSideUnit)
} catch (e: java.util.NoSuchElementException) { } catch (e: java.util.NoSuchElementException) {
Log.w("MainViewModel", "No unit with the given unitId")
allUnitsRepository.getById(MyUnitIDS.kilometer) allUnitsRepository.getById(MyUnitIDS.kilometer)
} }
unitTo = try { unitTo = try {
allUnitsRepository.getById(userPrefs.latestRightSideUnit) allUnitsRepository.getById(initialUserPrefs.latestRightSideUnit)
} catch (e: java.util.NoSuchElementException) { } catch (e: java.util.NoSuchElementException) {
Log.w("MainViewModel", "No unit with the given unitId")
allUnitsRepository.getById(MyUnitIDS.mile) allUnitsRepository.getById(MyUnitIDS.mile)
} }
_mainUIState.value =
_mainUIState.value.copy(negateButtonEnabled = unitFrom.group.canNegate)
// Now we load units data from database // Now we load units data from database
val allBasedUnits = basedUnitRepository.getAll() val allBasedUnits = basedUnitRepository.getAll()
allUnitsRepository.loadFromDatabase(application, allBasedUnits) allUnitsRepository.loadFromDatabase(application, allBasedUnits)
// User is free to convert values and units on units screen can be sorted properly // User is free to convert values and units on units screen can be sorted properly
_mainUIState.value = _mainUIState.value.copy(isLoadingDatabase = false) _negateButtonEnabled.update { unitFrom.group.canNegate }
_isLoadingDatabase.update { false }
updateCurrenciesBasicUnits() updateCurrenciesBasicUnits()
convertValue()
} }
} }
} }

View File

@ -32,6 +32,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import javax.inject.Inject import javax.inject.Inject
@ -43,25 +44,35 @@ class SecondViewModel @Inject constructor(
unitGroupsRepository: UnitGroupsRepository unitGroupsRepository: UnitGroupsRepository
) : ViewModel() { ) : ViewModel() {
private val _uiStateFlow = MutableStateFlow(SecondScreenUIState()) private val _favoritesOnly = MutableStateFlow(false)
private val _unitsToShow = MutableStateFlow(emptyMap<UnitGroup, List<AbstractUnit>>())
private val _searchQuery = MutableStateFlow("")
private val _chosenUnitGroup: MutableStateFlow<UnitGroup?> = MutableStateFlow(null)
private val _shownUnitGroups = unitGroupsRepository.shownUnitGroups
val mainFlow = combine(_uiStateFlow, unitGroupsRepository.shownUnitGroups) { uiState, shown -> val mainFlow = combine(
val newState = uiState.copy(shownUnitGroups = shown) _favoritesOnly,
_uiStateFlow.value = newState _unitsToShow,
return@combine newState _searchQuery,
} _chosenUnitGroup,
.stateIn( _shownUnitGroups
scope = viewModelScope, ) { favoritesOnly, unitsToShow, searchQuery, chosenUnitGroup, shownUnitGroups ->
started = SharingStarted.WhileSubscribed(5000), return@combine SecondScreenUIState(
initialValue = SecondScreenUIState() favoritesOnly = favoritesOnly,
unitsToShow = unitsToShow,
searchQuery = searchQuery,
chosenUnitGroup = chosenUnitGroup,
shownUnitGroups = shownUnitGroups
) )
}
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), SecondScreenUIState())
fun toggleFavoritesOnly() { fun toggleFavoritesOnly() {
_uiStateFlow.value = _uiStateFlow.value.copy(favoritesOnly = !_uiStateFlow.value.favoritesOnly) _favoritesOnly.update { !_favoritesOnly.value }
} }
fun onSearchQueryChange(newValue: String) { fun onSearchQueryChange(newValue: String) {
_uiStateFlow.value = _uiStateFlow.value.copy(searchQuery = newValue) _searchQuery.update { newValue }
} }
/** /**
@ -70,7 +81,7 @@ class SecondViewModel @Inject constructor(
* @param unitGroup Chip to change to. * @param unitGroup Chip to change to.
*/ */
fun setSelectedChip(unitGroup: UnitGroup) { fun setSelectedChip(unitGroup: UnitGroup) {
_uiStateFlow.value = _uiStateFlow.value.copy(chosenUnitGroup = unitGroup) _chosenUnitGroup.update { unitGroup }
} }
/** /**
@ -82,8 +93,8 @@ class SecondViewModel @Inject constructor(
* @param unitGroup [UnitGroup], currently selected chip. * @param unitGroup [UnitGroup], currently selected chip.
*/ */
fun toggleSelectedChip(unitGroup: UnitGroup) { fun toggleSelectedChip(unitGroup: UnitGroup) {
val newUnitGroup = if (_uiStateFlow.value.chosenUnitGroup == unitGroup) null else unitGroup val newUnitGroup = if (_chosenUnitGroup.value == unitGroup) null else unitGroup
_uiStateFlow.value = _uiStateFlow.value.copy(chosenUnitGroup = newUnitGroup) _chosenUnitGroup.update { newUnitGroup }
} }
/** /**
@ -101,13 +112,13 @@ class SecondViewModel @Inject constructor(
withContext(Dispatchers.Default) { withContext(Dispatchers.Default) {
val unitsToShow = allUnitsRepository.filterUnits( val unitsToShow = allUnitsRepository.filterUnits(
hideBrokenCurrencies = hideBrokenCurrencies, hideBrokenCurrencies = hideBrokenCurrencies,
chosenUnitGroup = _uiStateFlow.value.chosenUnitGroup, chosenUnitGroup = _chosenUnitGroup.value,
favoritesOnly = _uiStateFlow.value.favoritesOnly, favoritesOnly = _favoritesOnly.value,
searchQuery = _uiStateFlow.value.searchQuery, searchQuery = _searchQuery.value,
allUnitsGroups = _uiStateFlow.value.shownUnitGroups allUnitsGroups = _shownUnitGroups.value
) )
_uiStateFlow.value = _uiStateFlow.value.copy(unitsToShow = unitsToShow) _unitsToShow.update { unitsToShow }
} }
} }
} }

View File

@ -30,6 +30,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.sadellie.unitto.BuildConfig import com.sadellie.unitto.BuildConfig
import com.sadellie.unitto.R import com.sadellie.unitto.R
import com.sadellie.unitto.data.NavRoutes.ABOUT_SCREEN import com.sadellie.unitto.data.NavRoutes.ABOUT_SCREEN
@ -51,6 +52,7 @@ fun SettingsScreen(
navControllerAction: (String) -> Unit navControllerAction: (String) -> Unit
) { ) {
val mContext = LocalContext.current val mContext = LocalContext.current
val userPrefs = viewModel.userPrefs.collectAsStateWithLifecycle()
var dialogState: DialogState by rememberSaveable { var dialogState: DialogState by rememberSaveable {
mutableStateOf(DialogState.NONE) mutableStateOf(DialogState.NONE)
} }
@ -151,7 +153,7 @@ fun SettingsScreen(
UnittoListItem( UnittoListItem(
label = stringResource(R.string.send_usage_statistics), label = stringResource(R.string.send_usage_statistics),
supportText = stringResource(R.string.send_usage_statistics_support), supportText = stringResource(R.string.send_usage_statistics_support),
switchState = viewModel.userPrefs.enableAnalytics switchState = userPrefs.value.enableAnalytics
) { viewModel.updateEnableAnalytics(it) } ) { viewModel.updateEnableAnalytics(it) }
} }
} }
@ -198,7 +200,7 @@ fun SettingsScreen(
AlertDialogWithList( AlertDialogWithList(
title = stringResource(R.string.precision_setting), title = stringResource(R.string.precision_setting),
listItems = PRECISIONS, listItems = PRECISIONS,
selectedItemIndex = viewModel.userPrefs.digitsPrecision, selectedItemIndex = userPrefs.value.digitsPrecision,
selectAction = { viewModel.updatePrecision(it) }, selectAction = { viewModel.updatePrecision(it) },
dismissAction = { resetDialog() }, dismissAction = { resetDialog() },
supportText = stringResource(R.string.precision_setting_info) supportText = stringResource(R.string.precision_setting_info)
@ -208,7 +210,7 @@ fun SettingsScreen(
AlertDialogWithList( AlertDialogWithList(
title = stringResource(R.string.separator_setting), title = stringResource(R.string.separator_setting),
listItems = SEPARATORS, listItems = SEPARATORS,
selectedItemIndex = viewModel.userPrefs.separator, selectedItemIndex = userPrefs.value.separator,
selectAction = { viewModel.updateSeparator(it) }, selectAction = { viewModel.updateSeparator(it) },
dismissAction = { resetDialog() } dismissAction = { resetDialog() }
) )
@ -217,7 +219,7 @@ fun SettingsScreen(
AlertDialogWithList( AlertDialogWithList(
title = stringResource(R.string.output_format_setting), title = stringResource(R.string.output_format_setting),
listItems = OUTPUT_FORMAT, listItems = OUTPUT_FORMAT,
selectedItemIndex = viewModel.userPrefs.outputFormat, selectedItemIndex = userPrefs.value.outputFormat,
selectAction = { viewModel.updateOutputFormat(it) }, selectAction = { viewModel.updateOutputFormat(it) },
dismissAction = { resetDialog() }, dismissAction = { resetDialog() },
supportText = stringResource(R.string.output_format_setting_info) supportText = stringResource(R.string.output_format_setting_info)

View File

@ -19,9 +19,6 @@
package com.sadellie.unitto.screens.setttings package com.sadellie.unitto.screens.setttings
import android.app.Application import android.app.Application
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.sadellie.unitto.FirebaseHelper import com.sadellie.unitto.FirebaseHelper
@ -32,7 +29,10 @@ import com.sadellie.unitto.data.units.UnitGroupsRepository
import com.sadellie.unitto.screens.Formatter import com.sadellie.unitto.screens.Formatter
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import io.github.sadellie.themmo.ThemingMode import io.github.sadellie.themmo.ThemingMode
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.burnoutcrew.reorderable.ItemPosition import org.burnoutcrew.reorderable.ItemPosition
import javax.inject.Inject import javax.inject.Inject
@ -43,7 +43,9 @@ class SettingsViewModel @Inject constructor(
private val unitGroupsRepository: UnitGroupsRepository, private val unitGroupsRepository: UnitGroupsRepository,
private val application: Application, private val application: Application,
) : ViewModel() { ) : ViewModel() {
var userPrefs: UserPreferences by mutableStateOf(UserPreferences()) var userPrefs = userPrefsRepository.userPreferencesFlow
.onEach { Formatter.setSeparator(it.separator) }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), UserPreferences())
val shownUnitGroups = unitGroupsRepository.shownUnitGroups val shownUnitGroups = unitGroupsRepository.shownUnitGroups
val hiddenUnitGroups = unitGroupsRepository.hiddenUnitGroups val hiddenUnitGroups = unitGroupsRepository.hiddenUnitGroups
@ -164,11 +166,6 @@ class SettingsViewModel @Inject constructor(
unitGroupsRepository.updateShownGroups( unitGroupsRepository.updateShownGroups(
userPrefsRepository.userPreferencesFlow.first().shownUnitGroups userPrefsRepository.userPreferencesFlow.first().shownUnitGroups
) )
userPrefsRepository.userPreferencesFlow.collect {
userPrefs = it
Formatter.setSeparator(it.separator)
}
} }
} }
} }