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