mirror of
https://github.com/Myzel394/NumberHub.git
synced 2025-06-20 09:15:26 +02:00
Refactor ConverterScreen
This commit is contained in:
parent
185f8452a8
commit
36befca5da
@ -27,10 +27,8 @@ import androidx.compose.material.icons.outlined.Science
|
|||||||
import androidx.compose.material.ripple.rememberRipple
|
import androidx.compose.material.ripple.rememberRipple
|
||||||
import androidx.compose.material3.Badge
|
import androidx.compose.material3.Badge
|
||||||
import androidx.compose.material3.BadgedBox
|
import androidx.compose.material3.BadgedBox
|
||||||
import androidx.compose.material3.CenterAlignedTopAppBar
|
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.Scaffold
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBarDefaults
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@ -44,82 +42,121 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.semantics.Role
|
import androidx.compose.ui.semantics.Role
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
|
||||||
import com.sadellie.unitto.core.ui.R
|
import com.sadellie.unitto.core.ui.R
|
||||||
import com.sadellie.unitto.core.ui.common.AnimatedTopBarText
|
import com.sadellie.unitto.core.ui.common.AnimatedTopBarText
|
||||||
import com.sadellie.unitto.feature.converter.components.Keyboard
|
import com.sadellie.unitto.feature.converter.components.Keyboard
|
||||||
import com.sadellie.unitto.core.ui.common.PortraitLandscape
|
import com.sadellie.unitto.core.ui.common.PortraitLandscape
|
||||||
|
import com.sadellie.unitto.core.ui.common.UnittoScreenWithTopBar
|
||||||
import com.sadellie.unitto.feature.converter.components.TopScreenPart
|
import com.sadellie.unitto.feature.converter.components.TopScreenPart
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun ConverterScreen(
|
internal fun ConverterRoute(
|
||||||
|
viewModel: ConverterViewModel = hiltViewModel(),
|
||||||
|
navigateToLeftScreen: (String) -> Unit,
|
||||||
|
navigateToRightScreen: (unitFrom: String, unitTo: String, input: String) -> Unit,
|
||||||
|
navigateToTools: () -> Unit,
|
||||||
|
navigateToSettings: () -> Unit
|
||||||
|
) {
|
||||||
|
val uiState = viewModel.uiStateFlow.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
|
ConverterScreen(
|
||||||
|
uiState = uiState.value,
|
||||||
|
navigateToLeftScreen = navigateToLeftScreen,
|
||||||
|
navigateToRightScreen = navigateToRightScreen,
|
||||||
|
navigateToSettings = navigateToSettings,
|
||||||
|
navigateToTools = navigateToTools,
|
||||||
|
swapMeasurements = viewModel::swapUnits,
|
||||||
|
processInput = viewModel::processInput,
|
||||||
|
deleteDigit = viewModel::deleteDigit,
|
||||||
|
clearInput = viewModel::clearInput,
|
||||||
|
onOutputTextFieldClick = viewModel::toggleFormatTime
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun ConverterScreen(
|
||||||
|
uiState: ConverterUIState,
|
||||||
navigateToLeftScreen: (String) -> Unit,
|
navigateToLeftScreen: (String) -> Unit,
|
||||||
navigateToRightScreen: (unitFrom: String, unitTo: String, input: String) -> Unit,
|
navigateToRightScreen: (unitFrom: String, unitTo: String, input: String) -> Unit,
|
||||||
navigateToSettings: () -> Unit,
|
navigateToSettings: () -> Unit,
|
||||||
navigateToTools: () -> Unit,
|
navigateToTools: () -> Unit,
|
||||||
viewModel: ConverterViewModel = viewModel()
|
swapMeasurements: () -> Unit,
|
||||||
|
processInput: (String) -> Unit,
|
||||||
|
deleteDigit: () -> Unit,
|
||||||
|
clearInput: () -> Unit,
|
||||||
|
onOutputTextFieldClick: () -> Unit
|
||||||
) {
|
) {
|
||||||
var launched: Boolean by rememberSaveable { mutableStateOf(false) }
|
var launched: Boolean by rememberSaveable { mutableStateOf(false) }
|
||||||
val uiState = viewModel.uiStateFlow.collectAsStateWithLifecycle()
|
|
||||||
val userPrefs = viewModel.userPrefs.collectAsStateWithLifecycle()
|
|
||||||
|
|
||||||
Scaffold(
|
UnittoScreenWithTopBar(
|
||||||
modifier = Modifier,
|
title = { AnimatedTopBarText(launched) },
|
||||||
topBar = {
|
navigationIcon = {
|
||||||
CenterAlignedTopAppBar(
|
BadgedBox(
|
||||||
modifier = Modifier,
|
modifier = Modifier
|
||||||
title = { AnimatedTopBarText(launched) },
|
.padding(horizontal = 16.dp)
|
||||||
navigationIcon = {
|
.clickable(
|
||||||
if (uiState.value.showTools) {
|
onClick = navigateToTools,
|
||||||
BadgedBox(
|
interactionSource = remember { MutableInteractionSource() },
|
||||||
modifier = Modifier
|
indication = rememberRipple(false),
|
||||||
.padding(horizontal = 16.dp)
|
role = Role.Button
|
||||||
.clickable(
|
),
|
||||||
onClick = navigateToTools,
|
badge = {
|
||||||
interactionSource = remember { MutableInteractionSource() },
|
Badge { Text("1") }
|
||||||
indication = rememberRipple(false),
|
|
||||||
role = Role.Button
|
|
||||||
),
|
|
||||||
badge = {
|
|
||||||
Badge { Text("1") }
|
|
||||||
},
|
|
||||||
content = {
|
|
||||||
Icon(
|
|
||||||
Icons.Outlined.Science,
|
|
||||||
contentDescription = stringResource(R.string.tools_screen)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
actions = {
|
content = {
|
||||||
IconButton(onClick = navigateToSettings) {
|
Icon(
|
||||||
Icon(
|
Icons.Outlined.Science,
|
||||||
Icons.Outlined.MoreVert,
|
contentDescription = stringResource(R.string.tools_screen)
|
||||||
contentDescription = stringResource(R.string.open_settings_description)
|
)
|
||||||
)
|
}
|
||||||
}
|
|
||||||
},
|
|
||||||
// Makes the background of the top bar transparent, by default uses secondary color
|
|
||||||
colors = TopAppBarDefaults
|
|
||||||
.centerAlignedTopAppBarColors(containerColor = Color.Transparent)
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
actions = {
|
||||||
|
IconButton(onClick = navigateToSettings) {
|
||||||
|
Icon(
|
||||||
|
Icons.Outlined.MoreVert,
|
||||||
|
contentDescription = stringResource(R.string.open_settings_description)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
colors = TopAppBarDefaults
|
||||||
|
.centerAlignedTopAppBarColors(containerColor = Color.Transparent),
|
||||||
content = { padding ->
|
content = { padding ->
|
||||||
ConverterScreenContent(
|
PortraitLandscape(
|
||||||
modifier = Modifier.padding(padding),
|
modifier = Modifier.padding(padding),
|
||||||
uiState = uiState.value,
|
content1 = {
|
||||||
navigateToLeftScreen = navigateToLeftScreen,
|
TopScreenPart(
|
||||||
navigateToRightScreen = navigateToRightScreen,
|
modifier = it,
|
||||||
swapMeasurements = { viewModel.swapUnits() },
|
inputValue = uiState.inputValue,
|
||||||
processInput = { viewModel.processInput(it) },
|
calculatedValue = uiState.calculatedValue,
|
||||||
deleteDigit = { viewModel.deleteDigit() },
|
outputValue = uiState.resultValue,
|
||||||
clearInput = { viewModel.clearInput() },
|
unitFrom = uiState.unitFrom,
|
||||||
onOutputTextFieldClick = { viewModel.toggleFormatTime() },
|
unitTo = uiState.unitTo,
|
||||||
allowVibration = userPrefs.value.enableVibrations
|
networkLoading = uiState.showLoading,
|
||||||
|
networkError = uiState.showError,
|
||||||
|
navigateToLeftScreen = navigateToLeftScreen,
|
||||||
|
navigateToRightScreen = navigateToRightScreen,
|
||||||
|
swapUnits = swapMeasurements,
|
||||||
|
converterMode = uiState.mode,
|
||||||
|
formatTime = uiState.formatTime,
|
||||||
|
onOutputTextFieldClick = onOutputTextFieldClick
|
||||||
|
)
|
||||||
|
},
|
||||||
|
content2 = {
|
||||||
|
Keyboard(
|
||||||
|
modifier = it,
|
||||||
|
addDigit = processInput,
|
||||||
|
deleteDigit = deleteDigit,
|
||||||
|
clearInput = clearInput,
|
||||||
|
converterMode = uiState.mode,
|
||||||
|
allowVibration = uiState.allowVibration
|
||||||
|
)
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -135,48 +172,19 @@ internal fun ConverterScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
private fun ConverterScreenContent(
|
private fun PreviewConverterScreen() {
|
||||||
modifier: Modifier,
|
ConverterScreen(
|
||||||
uiState: ConverterUIState,
|
uiState = ConverterUIState(),
|
||||||
navigateToLeftScreen: (String) -> Unit,
|
navigateToLeftScreen = {},
|
||||||
navigateToRightScreen: (unitFrom: String, unitTo: String, input: String) -> Unit,
|
navigateToRightScreen = {_, _, _ -> },
|
||||||
swapMeasurements: () -> Unit = {},
|
navigateToSettings = {},
|
||||||
processInput: (String) -> Unit = {},
|
navigateToTools = {},
|
||||||
deleteDigit: () -> Unit = {},
|
swapMeasurements = {},
|
||||||
clearInput: () -> Unit = {},
|
processInput = {},
|
||||||
onOutputTextFieldClick: () -> Unit,
|
deleteDigit = {},
|
||||||
allowVibration: Boolean
|
clearInput = {},
|
||||||
) {
|
onOutputTextFieldClick = {}
|
||||||
PortraitLandscape(
|
|
||||||
modifier = modifier,
|
|
||||||
content1 = {
|
|
||||||
TopScreenPart(
|
|
||||||
modifier = it,
|
|
||||||
inputValue = uiState.inputValue,
|
|
||||||
calculatedValue = uiState.calculatedValue,
|
|
||||||
outputValue = uiState.resultValue,
|
|
||||||
unitFrom = uiState.unitFrom,
|
|
||||||
unitTo = uiState.unitTo,
|
|
||||||
networkLoading = uiState.showLoading,
|
|
||||||
networkError = uiState.showError,
|
|
||||||
navigateToLeftScreen = navigateToLeftScreen,
|
|
||||||
navigateToRightScreen = navigateToRightScreen,
|
|
||||||
swapUnits = swapMeasurements,
|
|
||||||
converterMode = uiState.mode,
|
|
||||||
formatTime = uiState.formatTime,
|
|
||||||
onOutputTextFieldClick = onOutputTextFieldClick
|
|
||||||
)
|
|
||||||
},
|
|
||||||
content2 = {
|
|
||||||
Keyboard(
|
|
||||||
modifier = it,
|
|
||||||
addDigit = processInput,
|
|
||||||
deleteDigit = deleteDigit,
|
|
||||||
clearInput = clearInput,
|
|
||||||
converterMode = uiState.mode,
|
|
||||||
allowVibration = allowVibration
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
@ -35,6 +35,7 @@ import com.sadellie.unitto.data.model.AbstractUnit
|
|||||||
* @property mode
|
* @property mode
|
||||||
* @property formatTime If true will format output when converting time.
|
* @property formatTime If true will format output when converting time.
|
||||||
* @property showTools If true will show tools button in TopBar.
|
* @property showTools If true will show tools button in TopBar.
|
||||||
|
* @property allowVibration When true will vibrate on button clicks.
|
||||||
*/
|
*/
|
||||||
data class ConverterUIState(
|
data class ConverterUIState(
|
||||||
val inputValue: String = KEY_0,
|
val inputValue: String = KEY_0,
|
||||||
@ -46,7 +47,8 @@ data class ConverterUIState(
|
|||||||
val unitTo: AbstractUnit? = null,
|
val unitTo: AbstractUnit? = null,
|
||||||
val mode: ConverterMode = ConverterMode.DEFAULT,
|
val mode: ConverterMode = ConverterMode.DEFAULT,
|
||||||
val formatTime: Boolean = true,
|
val formatTime: Boolean = true,
|
||||||
val showTools: Boolean = false
|
val showTools: Boolean = false,
|
||||||
|
val allowVibration: Boolean = false
|
||||||
)
|
)
|
||||||
|
|
||||||
enum class ConverterMode {
|
enum class ConverterMode {
|
||||||
|
@ -83,7 +83,7 @@ class ConverterViewModel @Inject constructor(
|
|||||||
private val allUnitsRepository: AllUnitsRepository
|
private val allUnitsRepository: AllUnitsRepository
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
val userPrefs = userPrefsRepository.userPreferencesFlow.stateIn(
|
private val _userPrefs = userPrefsRepository.userPreferencesFlow.stateIn(
|
||||||
viewModelScope,
|
viewModelScope,
|
||||||
SharingStarted.WhileSubscribed(5000),
|
SharingStarted.WhileSubscribed(5000),
|
||||||
com.sadellie.unitto.data.userprefs.UserPreferences()
|
com.sadellie.unitto.data.userprefs.UserPreferences()
|
||||||
@ -143,7 +143,7 @@ class ConverterViewModel @Inject constructor(
|
|||||||
_showLoading,
|
_showLoading,
|
||||||
_showError,
|
_showError,
|
||||||
_formatTime,
|
_formatTime,
|
||||||
userPrefs
|
_userPrefs
|
||||||
) { inputValue, unitFromValue, unitToValue, calculatedValue, resultValue, showLoadingValue, showErrorValue, formatTime, prefs ->
|
) { inputValue, unitFromValue, unitToValue, calculatedValue, resultValue, showLoadingValue, showErrorValue, formatTime, prefs ->
|
||||||
return@combine ConverterUIState(
|
return@combine ConverterUIState(
|
||||||
inputValue = inputValue,
|
inputValue = inputValue,
|
||||||
@ -159,7 +159,8 @@ class ConverterViewModel @Inject constructor(
|
|||||||
*/
|
*/
|
||||||
mode = if (_unitFrom.value is NumberBaseUnit) ConverterMode.BASE else ConverterMode.DEFAULT,
|
mode = if (_unitFrom.value is NumberBaseUnit) ConverterMode.BASE else ConverterMode.DEFAULT,
|
||||||
formatTime = formatTime,
|
formatTime = formatTime,
|
||||||
showTools = prefs.enableToolsExperiment
|
showTools = prefs.enableToolsExperiment,
|
||||||
|
allowVibration = prefs.enableVibrations
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.stateIn(
|
.stateIn(
|
||||||
@ -445,7 +446,7 @@ class ConverterViewModel @Inject constructor(
|
|||||||
// Now we evaluate expression in input
|
// Now we evaluate expression in input
|
||||||
val evaluationResult: BigDecimal = try {
|
val evaluationResult: BigDecimal = try {
|
||||||
Expressions().eval(cleanInput)
|
Expressions().eval(cleanInput)
|
||||||
.setScale(userPrefs.value.digitsPrecision, RoundingMode.HALF_EVEN)
|
.setScale(_userPrefs.value.digitsPrecision, RoundingMode.HALF_EVEN)
|
||||||
.trimZeros()
|
.trimZeros()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
when (e) {
|
when (e) {
|
||||||
@ -471,9 +472,9 @@ class ConverterViewModel @Inject constructor(
|
|||||||
} else {
|
} else {
|
||||||
_calculated.update {
|
_calculated.update {
|
||||||
evaluationResult
|
evaluationResult
|
||||||
.setMinimumRequiredScale(userPrefs.value.digitsPrecision)
|
.setMinimumRequiredScale(_userPrefs.value.digitsPrecision)
|
||||||
.trimZeros()
|
.trimZeros()
|
||||||
.toStringWith(userPrefs.value.outputFormat)
|
.toStringWith(_userPrefs.value.outputFormat)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -482,11 +483,11 @@ class ConverterViewModel @Inject constructor(
|
|||||||
val conversionResult: BigDecimal = unitFrom.convert(
|
val conversionResult: BigDecimal = unitFrom.convert(
|
||||||
unitTo,
|
unitTo,
|
||||||
evaluationResult,
|
evaluationResult,
|
||||||
userPrefs.value.digitsPrecision
|
_userPrefs.value.digitsPrecision
|
||||||
)
|
)
|
||||||
|
|
||||||
// Converted
|
// Converted
|
||||||
_result.update { conversionResult.toStringWith(userPrefs.value.outputFormat) }
|
_result.update { conversionResult.toStringWith(_userPrefs.value.outputFormat) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setInputSymbols(symbol: String, add: Boolean = true) {
|
private fun setInputSymbols(symbol: String, add: Boolean = true) {
|
||||||
@ -565,7 +566,7 @@ class ConverterViewModel @Inject constructor(
|
|||||||
|
|
||||||
private fun startObserving() {
|
private fun startObserving() {
|
||||||
viewModelScope.launch(Dispatchers.Default) {
|
viewModelScope.launch(Dispatchers.Default) {
|
||||||
merge(_input, _unitFrom, _unitTo, _showLoading, userPrefs).collectLatest {
|
merge(_input, _unitFrom, _unitTo, _showLoading, _userPrefs).collectLatest {
|
||||||
convertInput()
|
convertInput()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ package com.sadellie.unitto.feature.converter.navigation
|
|||||||
|
|
||||||
import androidx.navigation.NavGraphBuilder
|
import androidx.navigation.NavGraphBuilder
|
||||||
import androidx.navigation.compose.composable
|
import androidx.navigation.compose.composable
|
||||||
import com.sadellie.unitto.feature.converter.ConverterScreen
|
import com.sadellie.unitto.feature.converter.ConverterRoute
|
||||||
import com.sadellie.unitto.feature.converter.ConverterViewModel
|
import com.sadellie.unitto.feature.converter.ConverterViewModel
|
||||||
|
|
||||||
const val converterRoute = "converter_route"
|
const val converterRoute = "converter_route"
|
||||||
@ -33,12 +33,12 @@ fun NavGraphBuilder.converterScreen(
|
|||||||
viewModel: ConverterViewModel
|
viewModel: ConverterViewModel
|
||||||
) {
|
) {
|
||||||
composable(converterRoute) {
|
composable(converterRoute) {
|
||||||
ConverterScreen(
|
ConverterRoute(
|
||||||
|
viewModel = viewModel,
|
||||||
navigateToLeftScreen = navigateToLeftScreen,
|
navigateToLeftScreen = navigateToLeftScreen,
|
||||||
navigateToRightScreen = navigateToRightScreen,
|
navigateToRightScreen = navigateToRightScreen,
|
||||||
navigateToSettings = navigateToSettings,
|
navigateToSettings = navigateToSettings,
|
||||||
navigateToTools = navigateToTools,
|
navigateToTools = navigateToTools
|
||||||
viewModel = viewModel,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user