diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 7d242ee5..7fbeb1a2 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -90,9 +90,6 @@ android { kotlinOptions { jvmTarget = JavaVersion.VERSION_11.toString() - freeCompilerArgs = freeCompilerArgs + listOf( - "-opt-in=androidx.lifecycle.compose.ExperimentalLifecycleComposeApi" - ) } composeOptions { diff --git a/build-logic/convention/src/main/java/com/sadellie/unitto/ConfigureKotlinAndroid.kt b/build-logic/convention/src/main/java/com/sadellie/unitto/ConfigureKotlinAndroid.kt index 7ab7c66e..52c8d784 100644 --- a/build-logic/convention/src/main/java/com/sadellie/unitto/ConfigureKotlinAndroid.kt +++ b/build-logic/convention/src/main/java/com/sadellie/unitto/ConfigureKotlinAndroid.kt @@ -73,7 +73,6 @@ internal fun Project.configureKotlinAndroid( "-opt-in=androidx.compose.animation.ExperimentalAnimationApi", "-opt-in=androidx.compose.foundation.ExperimentalFoundationApi", "-opt-in=androidx.compose.ui.unit.ExperimentalUnitApi", - "-opt-in=androidx.lifecycle.compose.ExperimentalLifecycleComposeApi", "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi", ) jvmTarget = JavaVersion.VERSION_11.toString() diff --git a/core/base/build.gradle.kts b/core/base/build.gradle.kts index e3c29da2..1ee78dca 100644 --- a/core/base/build.gradle.kts +++ b/core/base/build.gradle.kts @@ -16,8 +16,6 @@ * along with this program. If not, see . */ -@file:Suppress("UnstableApiUsage") - plugins { id("unitto.library") } diff --git a/core/base/src/main/java/com/sadellie/unitto/core/base/Token.kt b/core/base/src/main/java/com/sadellie/unitto/core/base/Token.kt index dd20a0c8..dd392aa4 100644 --- a/core/base/src/main/java/com/sadellie/unitto/core/base/Token.kt +++ b/core/base/src/main/java/com/sadellie/unitto/core/base/Token.kt @@ -18,6 +18,7 @@ package com.sadellie.unitto.core.base +@Suppress("ObjectPropertyName") object Token { object Digit { const val _1 = "1" diff --git a/data/common/src/main/java/com/sadellie/unitto/data/common/FlowUtils.kt b/data/common/src/main/java/com/sadellie/unitto/data/common/FlowUtils.kt index c668de26..d7b745d3 100644 --- a/data/common/src/main/java/com/sadellie/unitto/data/common/FlowUtils.kt +++ b/data/common/src/main/java/com/sadellie/unitto/data/common/FlowUtils.kt @@ -117,5 +117,42 @@ fun combine( ) } +@Suppress("UNCHECKED_CAST", "UNUSED") +fun combine( + flow: Flow, + flow2: Flow, + flow3: Flow, + flow4: Flow, + flow5: Flow, + flow6: Flow, + flow7: Flow, + flow8: Flow, + flow9: Flow, + transform: suspend (T1, T2, T3, T4, T5, T6, T7, T8, T9) -> R, +): Flow = + kotlinx.coroutines.flow.combine( + flow, + flow2, + flow3, + flow4, + flow5, + flow6, + flow7, + flow8, + flow9 + ) { 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, + args[8] as T9, + ) + } + fun Flow.stateIn(scope: CoroutineScope, initialValue: T): StateFlow = stateIn(scope, SharingStarted.WhileSubscribed(5000L), initialValue) diff --git a/data/evaluatto/src/test/java/io/github/sadellie/evaluatto/Helpers.kt b/data/evaluatto/src/test/java/io/github/sadellie/evaluatto/Helpers.kt index 806c42e7..024eec3e 100644 --- a/data/evaluatto/src/test/java/io/github/sadellie/evaluatto/Helpers.kt +++ b/data/evaluatto/src/test/java/io/github/sadellie/evaluatto/Helpers.kt @@ -18,8 +18,8 @@ package io.github.sadellie.evaluatto -import org.junit.jupiter.api.Assertions.assertEquals -import org.junit.jupiter.api.Assertions.assertThrows +import org.junit.Assert.assertEquals +import org.junit.Assert.assertThrows import java.math.BigDecimal import java.math.RoundingMode diff --git a/data/units/src/main/java/com/sadellie/unitto/data/units/UnitsRepository.kt b/data/units/src/main/java/com/sadellie/unitto/data/units/UnitsRepository.kt index b9d953bc..d79cff95 100644 --- a/data/units/src/main/java/com/sadellie/unitto/data/units/UnitsRepository.kt +++ b/data/units/src/main/java/com/sadellie/unitto/data/units/UnitsRepository.kt @@ -245,7 +245,7 @@ class UnitsRepository @Inject constructor( favoritesOnly: Boolean, hideBrokenUnits: Boolean, sorting: UnitsListSorting, - shownUnitGroups: List, + shownUnitGroups: List = emptyList(), ): Map> { // Leave only shown unit groups var units: Sequence = if (unitGroup == null) { diff --git a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/ConverterViewModel.kt b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/ConverterViewModel.kt index 2e5c9085..edc214ab 100644 --- a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/ConverterViewModel.kt +++ b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/ConverterViewModel.kt @@ -45,7 +45,6 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.getAndUpdate import kotlinx.coroutines.flow.mapLatest @@ -73,8 +72,13 @@ internal class ConverterViewModel @Inject constructor( private val _unitFrom = MutableStateFlow(null) private val _unitTo = MutableStateFlow(null) - private val _leftSideUIState = MutableStateFlow(LeftSideUIState()) - private val _rightSideUIState = MutableStateFlow(RightSideUIState()) + private val _leftQuery = MutableStateFlow(TextFieldValue()) + private val _leftUnits = MutableStateFlow>>(emptyMap()) + private val _leftUnitGroup = MutableStateFlow(null) + + private val _rightQuery = MutableStateFlow(TextFieldValue()) + private val _rightUnits = MutableStateFlow>>(emptyMap()) + private val _currenciesState = MutableStateFlow(CurrencyRateUpdateState.Nothing) private var _loadCurrenciesJob: Job? = null @@ -153,19 +157,28 @@ internal class ConverterViewModel @Inject constructor( val leftSideUIState = combine( _unitFrom, - _leftSideUIState, + _leftQuery, + _leftUnits, + _leftUnitGroup, userPrefsRepository.converterPrefs, unitsRepo.allUnits - ) { unitFrom, ui, prefs, _ -> - return@combine ui.copy( + ) { unitFrom, query, units, unitGroup, prefs, _ -> + unitFrom ?: return@combine LeftSideUIState.Loading + + return@combine LeftSideUIState.Ready( unitFrom = unitFrom, sorting = prefs.unitConverterSorting, shownUnitGroups = prefs.shownUnitGroups, favorites = prefs.unitConverterFavoritesOnly, verticalList = prefs.enableToolsExperiment, + query = query, + units = units, + unitGroup = unitGroup ) } .mapLatest { + if (it !is LeftSideUIState.Ready) return@mapLatest it + filterUnitsLeft( query = it.query, unitGroup = it.unitGroup, @@ -175,19 +188,23 @@ internal class ConverterViewModel @Inject constructor( ) it } - .stateIn(viewModelScope, SharingStarted.Lazily, LeftSideUIState()) + .stateIn(viewModelScope, SharingStarted.Lazily, LeftSideUIState.Loading) val rightSideUIState = combine( _unitFrom, _unitTo, _input, _calculation, - _rightSideUIState, + _rightQuery, + _rightUnits, userPrefsRepository.converterPrefs, _currenciesState, unitsRepo.allUnits, - ) { unitFrom, unitTo, input, calculation, ui, prefs, currenciesState, _ -> - return@combine ui.copy( + ) { unitFrom, unitTo, input, calculation, query, units, prefs, currenciesState, _ -> + unitFrom ?: return@combine RightSideUIState.Loading + unitTo ?: return@combine RightSideUIState.Loading + + return@combine RightSideUIState.Ready( unitFrom = unitFrom, unitTo = unitTo, sorting = prefs.unitConverterSorting, @@ -196,20 +213,23 @@ internal class ConverterViewModel @Inject constructor( scale = prefs.precision, outputFormat = prefs.outputFormat, formatterSymbols = AllFormatterSymbols.getById(prefs.separator), - currencyRateUpdateState = currenciesState + currencyRateUpdateState = currenciesState, + query = query, + units = units, ) } .mapLatest { + if (it !is RightSideUIState.Ready) return@mapLatest it + filterUnitsRight( query = it.query, - unitGroup = it.unitTo?.group, + unitGroup = it.unitTo.group, favoritesOnly = it.favorites, sorting = it.sorting, - shownUnitGroups = emptyList(), ) it } - .stateIn(viewModelScope, SharingStarted.Lazily, RightSideUIState()) + .stateIn(viewModelScope, SharingStarted.Lazily, RightSideUIState.Loading) fun swapUnits() { _unitFrom @@ -303,21 +323,15 @@ internal class ConverterViewModel @Inject constructor( } } - fun queryChangeLeft(query: TextFieldValue) = _leftSideUIState.update { - it.copy(query = query) - } + fun queryChangeLeft(query: TextFieldValue) = _leftQuery.update { query } - fun queryChangeRight(query: TextFieldValue) = _rightSideUIState.update { - it.copy(query = query) - } + fun queryChangeRight(query: TextFieldValue) = _rightQuery.update { query } fun favoritesOnlyChange(enabled: Boolean) = viewModelScope.launch { userPrefsRepository.updateUnitConverterFavoritesOnly(enabled) } - fun updateUnitGroupLeft(unitGroup: UnitGroup?) = _leftSideUIState.update { - it.copy(unitGroup = unitGroup) - } + fun updateUnitGroupLeft(unitGroup: UnitGroup?) = _leftUnitGroup.update { unitGroup } fun favoriteUnit(unit: AbstractUnit) = viewModelScope.launch { unitsRepo.favorite(unit) @@ -330,16 +344,14 @@ internal class ConverterViewModel @Inject constructor( sorting: UnitsListSorting, shownUnitGroups: List, ) = viewModelScope.launch(Dispatchers.Default) { - _leftSideUIState.update { - it.copy( - units = unitsRepo.filterUnits( - query = query.text, - unitGroup = unitGroup, - favoritesOnly = favoritesOnly, - hideBrokenUnits = false, - sorting = sorting, - shownUnitGroups = shownUnitGroups - ) + _leftUnits.update { + unitsRepo.filterUnits( + query = query.text, + unitGroup = unitGroup, + favoritesOnly = favoritesOnly, + hideBrokenUnits = false, + sorting = sorting, + shownUnitGroups = shownUnitGroups ) } } @@ -349,18 +361,14 @@ internal class ConverterViewModel @Inject constructor( unitGroup: UnitGroup?, favoritesOnly: Boolean, sorting: UnitsListSorting, - shownUnitGroups: List, ) = viewModelScope.launch(Dispatchers.Default) { - _rightSideUIState.update { - it.copy( - units = unitsRepo.filterUnits( - query = query.text, - unitGroup = unitGroup, - favoritesOnly = favoritesOnly, - hideBrokenUnits = true, - sorting = sorting, - shownUnitGroups = shownUnitGroups - ) + _rightUnits.update { + unitsRepo.filterUnits( + query = query.text, + unitGroup = unitGroup, + favoritesOnly = favoritesOnly, + hideBrokenUnits = true, + sorting = sorting, ) } } diff --git a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/LeftSideScreen.kt b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/LeftSideScreen.kt index 02a87873..4ea78211 100644 --- a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/LeftSideScreen.kt +++ b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/LeftSideScreen.kt @@ -23,6 +23,7 @@ import androidx.compose.animation.animateColorAsState import androidx.compose.animation.core.LinearOutSlowInEasing import androidx.compose.animation.core.tween import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding @@ -49,13 +50,17 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.ui.common.UnittoSearchBar import com.sadellie.unitto.data.model.UnitGroup +import com.sadellie.unitto.data.model.UnitsListSorting import com.sadellie.unitto.data.model.unit.AbstractUnit +import com.sadellie.unitto.data.model.unit.NormalUnit +import com.sadellie.unitto.data.units.MyUnitIDS import com.sadellie.unitto.feature.converter.components.BasicUnitListItem import com.sadellie.unitto.feature.converter.components.ChipsFlexRow import com.sadellie.unitto.feature.converter.components.ChipsRow import com.sadellie.unitto.feature.converter.components.FavoritesButton import com.sadellie.unitto.feature.converter.components.SearchPlaceholder import com.sadellie.unitto.feature.converter.components.UnitGroupHeader +import java.math.BigDecimal @Composable internal fun LeftSideRoute( @@ -63,23 +68,28 @@ internal fun LeftSideRoute( navigateUp: () -> Unit, navigateToUnitGroups: () -> Unit, ) { - val uiState = viewModel.leftSideUIState.collectAsStateWithLifecycle() - - LeftSideScreen( - uiState = uiState.value, - onQueryChange = viewModel::queryChangeLeft, - toggleFavoritesOnly = viewModel::favoritesOnlyChange, - updateUnitFrom = viewModel::updateUnitFrom, - updateUnitGroup = viewModel::updateUnitGroupLeft, - favoriteUnit = viewModel::favoriteUnit, - navigateUp = navigateUp, - navigateToUnitGroups = navigateToUnitGroups, - ) + when ( + val uiState = viewModel.leftSideUIState.collectAsStateWithLifecycle().value + ) { + is LeftSideUIState.Loading -> { + Box(modifier = Modifier.fillMaxSize()) + } + is LeftSideUIState.Ready -> LeftSideScreen( + uiState = uiState, + onQueryChange = viewModel::queryChangeLeft, + toggleFavoritesOnly = viewModel::favoritesOnlyChange, + updateUnitFrom = viewModel::updateUnitFrom, + updateUnitGroup = viewModel::updateUnitGroupLeft, + favoriteUnit = viewModel::favoriteUnit, + navigateUp = navigateUp, + navigateToUnitGroups = navigateToUnitGroups, + ) + } } @Composable private fun LeftSideScreen( - uiState: LeftSideUIState, + uiState: LeftSideUIState.Ready, onQueryChange: (TextFieldValue) -> Unit, toggleFavoritesOnly: (Boolean) -> Unit, updateUnitFrom: (AbstractUnit) -> Unit, @@ -102,13 +112,12 @@ private fun LeftSideScreen( val chipsRowLazyListState = rememberLazyListState() LaunchedEffect(uiState.unitFrom, uiState.shownUnitGroups) { - if (uiState.unitFrom == null) return@LaunchedEffect updateUnitGroup(uiState.unitFrom.group) - val groupToSelect = uiState.shownUnitGroups.indexOf(uiState.unitFrom.group) - if (groupToSelect > -1) { - kotlin.runCatching { - chipsRowLazyListState.animateScrollToItem(groupToSelect) + kotlin.runCatching { + val groupToSelect = uiState.shownUnitGroups.indexOf(uiState.unitFrom.group) + if (groupToSelect > -1) { + chipsRowLazyListState.scrollToItem(groupToSelect) } } } @@ -153,13 +162,13 @@ private fun LeftSideScreen( } ) { paddingValues -> Crossfade( - targetState = uiState.units?.isNotEmpty(), + targetState = uiState.units.isNotEmpty(), modifier = Modifier.padding(paddingValues), label = "Units list" ) { hasUnits -> when (hasUnits) { true -> LazyColumn(Modifier.fillMaxSize()) { - uiState.units?.forEach { (unitGroup, units) -> + uiState.units.forEach { (unitGroup, units) -> item(unitGroup.name) { UnitGroupHeader(Modifier.animateItemPlacement(), unitGroup) } @@ -170,7 +179,7 @@ private fun LeftSideScreen( name = stringResource(it.displayName), supportLabel = stringResource(it.shortName), isFavorite = it.isFavorite, - isSelected = it.id == uiState.unitFrom?.id, + isSelected = it.id == uiState.unitFrom.id, onClick = { onQueryChange(TextFieldValue()) updateUnitFrom(it) @@ -183,7 +192,6 @@ private fun LeftSideScreen( } false -> SearchPlaceholder(navigateToSettingsAction = navigateToUnitGroups) - null -> {} } } } @@ -192,8 +200,29 @@ private fun LeftSideScreen( @Preview @Composable private fun LeftSideScreenPreview() { + val units: Map> = mapOf( + UnitGroup.LENGTH to listOf( + NormalUnit(MyUnitIDS.meter, BigDecimal.valueOf(1.0E+18), UnitGroup.LENGTH, R.string.meter, R.string.meter_short), + NormalUnit(MyUnitIDS.kilometer, BigDecimal.valueOf(1.0E+21), UnitGroup.LENGTH, R.string.kilometer, R.string.kilometer_short), + NormalUnit(MyUnitIDS.nautical_mile, BigDecimal.valueOf(1.852E+21), UnitGroup.LENGTH, R.string.nautical_mile, R.string.nautical_mile_short), + NormalUnit(MyUnitIDS.inch, BigDecimal.valueOf(25_400_000_000_000_000), UnitGroup.LENGTH, R.string.inch, R.string.inch_short), + NormalUnit(MyUnitIDS.foot, BigDecimal.valueOf(304_800_000_000_002_200), UnitGroup.LENGTH, R.string.foot, R.string.foot_short), + NormalUnit(MyUnitIDS.yard, BigDecimal.valueOf(914_400_000_000_006_400), UnitGroup.LENGTH, R.string.yard, R.string.yard_short), + NormalUnit(MyUnitIDS.mile, BigDecimal.valueOf(1_609_344_000_000_010_500_000.0), UnitGroup.LENGTH, R.string.mile, R.string.mile_short), + ) + ) + LeftSideScreen( - uiState = LeftSideUIState(), + uiState = LeftSideUIState.Ready( + unitFrom = units.values.first().first(), + units = units, + query = TextFieldValue(), + favorites = false, + shownUnitGroups = listOf(UnitGroup.LENGTH, UnitGroup.TEMPERATURE, UnitGroup.CURRENCY), + unitGroup = units.keys.toList().first(), + sorting = UnitsListSorting.USAGE, + verticalList = false + ), onQueryChange = {}, toggleFavoritesOnly = {}, updateUnitFrom = {}, diff --git a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/LeftSideUIState.kt b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/LeftSideUIState.kt index 151f7a7f..c9dafaa7 100644 --- a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/LeftSideUIState.kt +++ b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/LeftSideUIState.kt @@ -23,13 +23,17 @@ import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.UnitsListSorting import com.sadellie.unitto.data.model.unit.AbstractUnit -internal data class LeftSideUIState( - val unitFrom: AbstractUnit? = null, - val query: TextFieldValue = TextFieldValue(), - val units: Map>? = null, - val favorites: Boolean = false, - val shownUnitGroups: List = emptyList(), - val unitGroup: UnitGroup? = unitFrom?.group, - val sorting: UnitsListSorting = UnitsListSorting.USAGE, - val verticalList: Boolean = false, -) +internal sealed class LeftSideUIState { + data object Loading : LeftSideUIState() + + data class Ready( + val unitFrom: AbstractUnit, + val query: TextFieldValue, + val units: Map> = emptyMap(), + val favorites: Boolean, + val shownUnitGroups: List, + val unitGroup: UnitGroup?, + val sorting: UnitsListSorting, + val verticalList: Boolean, + ) : LeftSideUIState() +} diff --git a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/RightSideScreen.kt b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/RightSideScreen.kt index 34bbaf5c..6f565520 100644 --- a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/RightSideScreen.kt +++ b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/RightSideScreen.kt @@ -19,6 +19,7 @@ package com.sadellie.unitto.feature.converter import androidx.compose.animation.Crossfade +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn @@ -33,15 +34,19 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.tooling.preview.Preview import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.sadellie.unitto.core.base.OutputFormat import com.sadellie.unitto.core.base.R import com.sadellie.unitto.core.ui.common.UnittoSearchBar import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols import com.sadellie.unitto.core.ui.common.textfield.formatExpression import com.sadellie.unitto.data.common.format import com.sadellie.unitto.data.model.UnitGroup +import com.sadellie.unitto.data.model.UnitsListSorting import com.sadellie.unitto.data.model.unit.AbstractUnit import com.sadellie.unitto.data.model.unit.DefaultUnit +import com.sadellie.unitto.data.model.unit.NormalUnit import com.sadellie.unitto.data.model.unit.NumberBaseUnit +import com.sadellie.unitto.data.units.MyUnitIDS import com.sadellie.unitto.feature.converter.components.BasicUnitListItem import com.sadellie.unitto.feature.converter.components.FavoritesButton import com.sadellie.unitto.feature.converter.components.SearchPlaceholder @@ -54,22 +59,26 @@ internal fun RightSideRoute( navigateUp: () -> Unit, navigateToUnitGroups: () -> Unit, ) { - val uiState = viewModel.rightSideUIState.collectAsStateWithLifecycle() - - RightSideScreen( - uiState = uiState.value, - onQueryChange = viewModel::queryChangeRight, - toggleFavoritesOnly = viewModel::favoritesOnlyChange, - updateUnitTo = viewModel::updateUnitTo, - favoriteUnit = viewModel::favoriteUnit, - navigateUp = navigateUp, - navigateToUnitGroups = navigateToUnitGroups, - ) + when ( + val uiState = viewModel.rightSideUIState.collectAsStateWithLifecycle().value + ) { + is RightSideUIState.Loading -> Box(Modifier.fillMaxSize()) + is RightSideUIState.Ready -> + RightSideScreen( + uiState = uiState, + onQueryChange = viewModel::queryChangeRight, + toggleFavoritesOnly = viewModel::favoritesOnlyChange, + updateUnitTo = viewModel::updateUnitTo, + favoriteUnit = viewModel::favoriteUnit, + navigateUp = navigateUp, + navigateToUnitGroups = navigateToUnitGroups, + ) + } } @Composable private fun RightSideScreen( - uiState: RightSideUIState, + uiState: RightSideUIState.Ready, onQueryChange: (TextFieldValue) -> Unit, toggleFavoritesOnly: (Boolean) -> Unit, updateUnitTo: (AbstractUnit) -> Unit, @@ -98,13 +107,13 @@ private fun RightSideScreen( } ) { paddingValues -> Crossfade( - targetState = uiState.units?.isNotEmpty(), + targetState = uiState.units.isNotEmpty(), modifier = Modifier.padding(paddingValues), label = "Units list" ) { hasUnits -> when (hasUnits) { true -> LazyColumn(Modifier.fillMaxSize()) { - uiState.units?.forEach { (unitGroup, units) -> + uiState.units.forEach { (unitGroup, units) -> item(unitGroup.name) { UnitGroupHeader(Modifier.animateItemPlacement(), unitGroup) } @@ -124,7 +133,7 @@ private fun RightSideScreen( readyCurrencies = uiState.currencyRateUpdateState is CurrencyRateUpdateState.Ready, ), isFavorite = it.isFavorite, - isSelected = it.id == uiState.unitTo?.id, + isSelected = it.id == uiState.unitTo.id, onClick = { onQueryChange(TextFieldValue()) updateUnitTo(it) @@ -137,7 +146,6 @@ private fun RightSideScreen( } false -> SearchPlaceholder(navigateToSettingsAction = navigateToUnitGroups) - null -> {} } } } @@ -187,8 +195,32 @@ private fun formatUnitToSupportLabel( @Preview @Composable private fun RightSideScreenPreview() { + val units: Map> = mapOf( + UnitGroup.LENGTH to listOf( + NormalUnit(MyUnitIDS.meter, BigDecimal.valueOf(1.0E+18), UnitGroup.LENGTH, R.string.meter, R.string.meter_short), + NormalUnit(MyUnitIDS.kilometer, BigDecimal.valueOf(1.0E+21), UnitGroup.LENGTH, R.string.kilometer, R.string.kilometer_short), + NormalUnit(MyUnitIDS.nautical_mile, BigDecimal.valueOf(1.852E+21), UnitGroup.LENGTH, R.string.nautical_mile, R.string.nautical_mile_short), + NormalUnit(MyUnitIDS.inch, BigDecimal.valueOf(25_400_000_000_000_000), UnitGroup.LENGTH, R.string.inch, R.string.inch_short), + NormalUnit(MyUnitIDS.foot, BigDecimal.valueOf(304_800_000_000_002_200), UnitGroup.LENGTH, R.string.foot, R.string.foot_short), + NormalUnit(MyUnitIDS.yard, BigDecimal.valueOf(914_400_000_000_006_400), UnitGroup.LENGTH, R.string.yard, R.string.yard_short), + NormalUnit(MyUnitIDS.mile, BigDecimal.valueOf(1_609_344_000_000_010_500_000.0), UnitGroup.LENGTH, R.string.mile, R.string.mile_short), + ) + ) + RightSideScreen( - uiState = RightSideUIState(), + uiState = RightSideUIState.Ready( + unitFrom = units.values.first().first(), + units = units, + query = TextFieldValue(), + favorites = false, + sorting = UnitsListSorting.USAGE, + unitTo = units.values.first()[1], + input = "100", + scale = 3, + outputFormat = OutputFormat.PLAIN, + formatterSymbols = FormatterSymbols.Spaces, + currencyRateUpdateState = CurrencyRateUpdateState.Nothing + ), onQueryChange = {}, toggleFavoritesOnly = {}, updateUnitTo = {}, diff --git a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/RightSideUIState.kt b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/RightSideUIState.kt index 86483c40..8ecee3f3 100644 --- a/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/RightSideUIState.kt +++ b/feature/converter/src/main/java/com/sadellie/unitto/feature/converter/RightSideUIState.kt @@ -19,23 +19,25 @@ package com.sadellie.unitto.feature.converter import androidx.compose.ui.text.input.TextFieldValue -import com.sadellie.unitto.core.base.OutputFormat import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols import com.sadellie.unitto.data.model.UnitGroup import com.sadellie.unitto.data.model.UnitsListSorting import com.sadellie.unitto.data.model.unit.AbstractUnit -internal data class RightSideUIState( - val unitFrom: AbstractUnit? = null, - val unitTo: AbstractUnit? = null, - val query: TextFieldValue = TextFieldValue(), - val units: Map>? = null, - val favorites: Boolean = false, - val unitGroup: UnitGroup? = unitFrom?.group, - val sorting: UnitsListSorting = UnitsListSorting.USAGE, - val input: String = "", - val scale: Int = 3, - val outputFormat: Int = OutputFormat.PLAIN, - val formatterSymbols: FormatterSymbols = FormatterSymbols.Spaces, - val currencyRateUpdateState: CurrencyRateUpdateState = CurrencyRateUpdateState.Nothing -) +internal sealed class RightSideUIState { + data object Loading : RightSideUIState() + + data class Ready( + val unitFrom: AbstractUnit, + val unitTo: AbstractUnit, + val query: TextFieldValue, + val units: Map>, + val favorites: Boolean, + val sorting: UnitsListSorting, + val input: String, + val scale: Int, + val outputFormat: Int, + val formatterSymbols: FormatterSymbols, + val currencyRateUpdateState: CurrencyRateUpdateState, + ) : RightSideUIState() +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 13f8cb5e..d2cae78d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -6,19 +6,19 @@ ksp = "1.9.0-1.0.13" androidxCore = "1.12.0" androidGradlePlugin = "8.1.1" orgJetbrainsKotlinxCoroutinesTest = "1.7.2" -androidxCompose = "1.6.0-alpha05" +androidxCompose = "1.6.0-alpha06" androidxComposeCompiler = "1.5.0" -androidxComposeUi = "1.6.0-alpha05" -androidxComposeMaterial3 = "1.2.0-alpha07" -androidxNavigation = "2.7.2" +androidxComposeUi = "1.6.0-alpha06" +androidxComposeMaterial3 = "1.2.0-alpha08" +androidxNavigation = "2.7.3" androidxLifecycleRuntimeCompose = "2.6.2" androidxHilt = "1.0.0" androidxAppCompat = "1.6.1" comGoogleDagger = "2.47" -androidxComposeMaterialIconsExtended = "1.6.0-alpha05" +androidxComposeMaterialIconsExtended = "1.6.0-alpha06" androidxDatastore = "1.0.0" comGoogleAccompanist = "0.30.1" -androidxRoom = "2.6.0-beta01" +androidxRoom = "2.6.0-rc01" comSquareupMoshi = "1.15.0" comSquareupRetrofit2 = "2.9.0" comGithubSadellieThemmo = "1.0.0"