mirror of
https://github.com/Myzel394/NumberHub.git
synced 2025-06-19 16:55:26 +02:00
Better (?) MutableStateFlows approach
This commit is contained in:
parent
0ffec5e15a
commit
66227f4ed7
@ -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()
|
||||||
|
@ -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
|
||||||
|
)
|
||||||
|
}
|
@ -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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user