From c22ae80f84796e9954b457a86371c80ff4f210dd Mon Sep 17 00:00:00 2001 From: Alejo Date: Thu, 2 Jan 2025 17:56:05 -0300 Subject: [PATCH 1/3] wrap ui state into UIControlsState --- .../WooShippingLabelCreationViewModel.kt | 36 ++++++++++++++----- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/WooShippingLabelCreationViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/WooShippingLabelCreationViewModel.kt index 1eeb961476d..2971034b863 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/WooShippingLabelCreationViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/WooShippingLabelCreationViewModel.kt @@ -67,7 +67,13 @@ class WooShippingLabelCreationViewModel @Inject constructor( private val packageWeight = MutableStateFlow(null) private val packageSelection = MutableStateFlow(NotSelected) - private val markOrderComplete = MutableStateFlow(false) + private val uiState = MutableStateFlow( + UIControlsState( + markOrderComplete = false, + isShipmentDetailsExpanded = true, + isAddressSelectionExpanded = false + ) + ) private val selectedRatesSortOrder = MutableStateFlow(ShippingSortOption.FASTEST) private val refreshShippingRates = MutableSharedFlow() @@ -252,9 +258,9 @@ class WooShippingLabelCreationViewModel @Inject constructor( shippingAddresses.drop(1), shippingRatesState, packageSelection, - markOrderComplete, - purchaseState - ) { storeOptions, order, addresses, shippingRates, packageSelection, markOrderComplete, purchaseState -> + uiState, + purchaseState, + ) { storeOptions, order, addresses, shippingRates, packageSelection, uiState, purchaseState -> if (order == null || storeOptions == null || addresses == null || purchaseState is PurchaseState.Error) { return@combine WooShippingViewState.Error } @@ -278,7 +284,7 @@ class WooShippingLabelCreationViewModel @Inject constructor( shippingAddresses = addresses, shippingRates = shippingRates, packageSelection = packageSelection, - markOrderComplete = markOrderComplete, + uiState = uiState, purchaseState = purchaseState ) }.collectLatest { @@ -309,7 +315,15 @@ class WooShippingLabelCreationViewModel @Inject constructor( } fun onMarkOrderCompleteChange(value: Boolean) { - markOrderComplete.value = value + uiState.update { it.copy(markOrderComplete = value) } + } + + fun onShipmentDetailsExpandedChange(value: Boolean) { + uiState.update { it.copy(isShipmentDetailsExpanded = value) } + } + + fun onSelectAddressExpandedChange(value: Boolean) { + uiState.update { it.copy(isAddressSelectionExpanded = value) } } private fun getTotalPrice(items: List): String { @@ -348,7 +362,7 @@ class WooShippingLabelCreationViewModel @Inject constructor( if (selectedPackage == null || addresses == null || shippingRate == null || weight == null) return val orderId = navArgs.orderId - val lastOrderComplete = markOrderComplete.value + val lastOrderComplete = uiState.value.markOrderComplete val shippableItemsIdList = shippableItems.value.map { it.productId } purchaseState.value = PurchaseState.InProgress @@ -441,7 +455,7 @@ class WooShippingLabelCreationViewModel @Inject constructor( val shippingAddresses: WooShippingAddresses, val shippingRates: ShippingRatesState, val packageSelection: PackageSelectionState, - val markOrderComplete: Boolean, + val uiState: UIControlsState, val purchaseState: PurchaseState ) : WooShippingViewState() } @@ -488,6 +502,12 @@ class WooShippingLabelCreationViewModel @Inject constructor( get() = customWeight ?: defaultWeight } + data class UIControlsState( + val markOrderComplete: Boolean, + val isShipmentDetailsExpanded: Boolean, + val isAddressSelectionExpanded: Boolean + ) + data class ShippingRatesInfo( val orderId: Long, val packageSelected: PackageData, From 66826524a47fcd56565f37de6f95e3e6c137d057 Mon Sep 17 00:00:00 2001 From: Alejo Date: Thu, 2 Jan 2025 17:57:18 -0300 Subject: [PATCH 2/3] display address selection bottom sheet --- .../wooshippinglabels/ShipmentDetails.kt | 102 +++++---- .../WooShippingLabelCreationScreen.kt | 102 ++++++--- .../address/AddressSection.kt | 208 ++++++++++++------ .../WooShippingLabelPurchasedScreen.kt | 5 +- .../rates/ui/ShippingRatesCard.kt | 7 +- 5 files changed, 280 insertions(+), 144 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/ShipmentDetails.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/ShipmentDetails.kt index b2731736b26..5107529452e 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/ShipmentDetails.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/ShipmentDetails.kt @@ -16,6 +16,7 @@ import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.sizeIn import androidx.compose.foundation.layout.width import androidx.compose.foundation.rememberScrollState @@ -24,8 +25,11 @@ import androidx.compose.material.BottomSheetScaffoldState import androidx.compose.material.Divider import androidx.compose.material.Icon import androidx.compose.material.MaterialTheme +import androidx.compose.material.ModalBottomSheetState +import androidx.compose.material.ModalBottomSheetValue import androidx.compose.material.Surface import androidx.compose.material.Text +import androidx.compose.material.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope @@ -43,7 +47,6 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.woocommerce.android.R import com.woocommerce.android.extensions.appendWithIfNotEmpty -import com.woocommerce.android.model.Address import com.woocommerce.android.ui.compose.animations.SkeletonView import com.woocommerce.android.ui.compose.theme.WooThemeWithBackground import com.woocommerce.android.ui.orders.wooshippinglabels.address.AddressSectionLandscape @@ -58,6 +61,7 @@ import kotlinx.parcelize.Parcelize @Composable fun ShipmentDetails( scaffoldState: BottomSheetScaffoldState, + shipFromSelectionBottomSheetState: ModalBottomSheetState, shippableItems: ShippableItemsUI, shippingLines: List, shippingAddresses: WooShippingAddresses, @@ -65,8 +69,6 @@ fun ShipmentDetails( modifier: Modifier = Modifier, markOrderComplete: Boolean = false, onMarkOrderCompleteChange: (Boolean) -> Unit = {}, - onShippingFromAddressChange: (OriginShippingAddress) -> Unit = {}, - onShippingToAddressChange: (Address) -> Unit = {}, handlerModifier: Modifier = Modifier, isReadOnly: Boolean = false ) { @@ -102,39 +104,41 @@ fun ShipmentDetails( tint = colorResource(id = R.color.color_primary), ) AnimatedVisibility(visible = scaffoldState.bottomSheetState.isCollapsed) { - Text( - text = stringResource(R.string.shipping_label_shipment_details_title), - color = MaterialTheme.colors.primary, - modifier = Modifier - .padding(top = dimensionResource(R.dimen.minor_100)) + Column { + Text( + text = stringResource(R.string.shipping_label_shipment_details_title), + color = MaterialTheme.colors.primary, + modifier = Modifier + .padding(top = dimensionResource(R.dimen.minor_100)) + ) + Spacer(modifier = Modifier.size(dimensionResource(id = R.dimen.major_200))) + } + } + + if (LocalConfiguration.current.orientation == Configuration.ORIENTATION_LANDSCAPE) { + ShipmentDetailsLandscape( + shippableItems = shippableItems, + shippingLines = shippingLines, + shippingAddresses = shippingAddresses, + shippingRateSummary = shippingRateSummary, + modifier = modifier.padding(top = dimensionResource(R.dimen.major_100)), + isReadOnly = isReadOnly, + shipFromSelectionBottomSheetState = shipFromSelectionBottomSheetState + ) + } else { + ShipmentDetailsPortrait( + shippableItems = shippableItems, + shippingLines = shippingLines, + markOrderComplete = markOrderComplete, + onMarkOrderCompleteChange = onMarkOrderCompleteChange, + shippingAddresses = shippingAddresses, + shippingRateSummary = shippingRateSummary, + modifier = modifier.padding(top = dimensionResource(R.dimen.minor_100)), + isReadOnly = isReadOnly, + shipFromSelectionBottomSheetState = shipFromSelectionBottomSheetState ) } } - if (LocalConfiguration.current.orientation == Configuration.ORIENTATION_LANDSCAPE) { - ShipmentDetailsLandscape( - shippableItems = shippableItems, - shippingLines = shippingLines, - shippingAddresses = shippingAddresses, - onShippingFromAddressChange = onShippingFromAddressChange, - onShippingToAddressChange = onShippingToAddressChange, - shippingRateSummary = shippingRateSummary, - modifier = modifier, - isReadOnly = isReadOnly - ) - } else { - ShipmentDetailsPortrait( - shippableItems = shippableItems, - shippingLines = shippingLines, - markOrderComplete = markOrderComplete, - onMarkOrderCompleteChange = onMarkOrderCompleteChange, - shippingAddresses = shippingAddresses, - onShippingFromAddressChange = onShippingFromAddressChange, - onShippingToAddressChange = onShippingToAddressChange, - shippingRateSummary = shippingRateSummary, - modifier = modifier, - isReadOnly = isReadOnly - ) - } } @Composable @@ -145,28 +149,25 @@ private fun ShipmentDetailsPortrait( markOrderComplete: Boolean, onMarkOrderCompleteChange: (Boolean) -> Unit, shippingRateSummary: ShippingRateSummaryUI?, + shipFromSelectionBottomSheetState: ModalBottomSheetState, modifier: Modifier = Modifier, - onShippingFromAddressChange: (OriginShippingAddress) -> Unit = {}, - onShippingToAddressChange: (Address) -> Unit = {}, isReadOnly: Boolean = false ) { Column(modifier) { Column( Modifier .fillMaxWidth() - .padding(top = dimensionResource(R.dimen.major_200)) .weight(1f) .verticalScroll(rememberScrollState()) ) { OrderDetailsSection( shippingAddresses = shippingAddresses, - onShippingFromAddressChange = onShippingFromAddressChange, - onShippingToAddressChange = onShippingToAddressChange, totalItems = shippableItems.shippableItems.size, totalItemsCost = shippableItems.formattedTotalPrice, shippingLines = shippingLines, - isReadOnly = isReadOnly + isReadOnly = isReadOnly, + shipFromSelectionBottomSheetState = shipFromSelectionBottomSheetState ) Divider(modifier = Modifier.padding(horizontal = dimensionResource(R.dimen.major_100))) ShipmentCostSection( @@ -190,26 +191,23 @@ private fun ShipmentDetailsLandscape( shippingLines: List, shippingAddresses: WooShippingAddresses, shippingRateSummary: ShippingRateSummaryUI?, + shipFromSelectionBottomSheetState: ModalBottomSheetState, modifier: Modifier = Modifier, - onShippingFromAddressChange: (OriginShippingAddress) -> Unit = {}, - onShippingToAddressChange: (Address) -> Unit = {}, isReadOnly: Boolean = false ) { Column(modifier) { Column( Modifier .fillMaxWidth() - .padding(top = dimensionResource(R.dimen.major_200)) .weight(1f) .verticalScroll(rememberScrollState()) ) { AddressSectionLandscape( shippingAddresses = shippingAddresses, - onShippingFromAddressChange = onShippingFromAddressChange, - onShippingToAddressChange = onShippingToAddressChange, modifier = Modifier.padding(horizontal = dimensionResource(R.dimen.major_100)), - isReadOnly = isReadOnly + isReadOnly = isReadOnly, + shipFromSelectionBottomSheetState = shipFromSelectionBottomSheetState ) Row( modifier = Modifier @@ -235,7 +233,7 @@ private fun ShipmentDetailsLandscape( } @Composable -private fun ShipmentDetailsSectionTitle( +internal fun ShipmentDetailsSectionTitle( title: String, modifier: Modifier = Modifier ) { @@ -258,11 +256,10 @@ private fun ShipmentDetailsSectionTitlePreview() { @Composable private fun OrderDetailsSection( shippingAddresses: WooShippingAddresses, - onShippingFromAddressChange: (OriginShippingAddress) -> Unit, - onShippingToAddressChange: (Address) -> Unit, totalItems: Int, totalItemsCost: String, shippingLines: List, + shipFromSelectionBottomSheetState: ModalBottomSheetState, modifier: Modifier = Modifier, isReadOnly: Boolean = false ) { @@ -274,11 +271,9 @@ private fun OrderDetailsSection( Spacer(modifier = Modifier.height(dimensionResource(R.dimen.major_100))) AddressSectionPortrait( shippingAddresses = shippingAddresses, - originAddresses = shippingAddresses.originAddresses, - onShippingFromAddressChange = onShippingFromAddressChange, - onShippingToAddressChange = onShippingToAddressChange, modifier = Modifier.padding(horizontal = dimensionResource(R.dimen.major_100)), - isReadOnly = isReadOnly + isReadOnly = isReadOnly, + shipFromSelectionBottomSheetState = shipFromSelectionBottomSheetState ) TotalCard( totalItems = totalItems, @@ -330,7 +325,8 @@ fun ShipmentDetailsLandscapePreview() { shipTo = getShipTo(), originAddresses = listOf(getShipFrom()) ), - shippingRateSummary = null + shippingRateSummary = null, + shipFromSelectionBottomSheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden) ) } } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/WooShippingLabelCreationScreen.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/WooShippingLabelCreationScreen.kt index dd55c39225d..123cfc57696 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/WooShippingLabelCreationScreen.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/WooShippingLabelCreationScreen.kt @@ -21,10 +21,13 @@ import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll import androidx.compose.material.BottomSheetScaffold import androidx.compose.material.BottomSheetScaffoldState +import androidx.compose.material.BottomSheetValue import androidx.compose.material.CircularProgressIndicator import androidx.compose.material.Icon import androidx.compose.material.IconButton import androidx.compose.material.MaterialTheme +import androidx.compose.material.ModalBottomSheetState +import androidx.compose.material.ModalBottomSheetValue import androidx.compose.material.Surface import androidx.compose.material.Text import androidx.compose.material.TopAppBar @@ -34,6 +37,8 @@ import androidx.compose.material.icons.filled.Edit import androidx.compose.material.icons.filled.Star import androidx.compose.material.icons.outlined.Star import androidx.compose.material.rememberBottomSheetScaffoldState +import androidx.compose.material.rememberBottomSheetState +import androidx.compose.material.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.mutableStateOf @@ -52,13 +57,13 @@ import androidx.compose.ui.tooling.preview.Devices import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.woocommerce.android.R -import com.woocommerce.android.model.Address import com.woocommerce.android.ui.compose.component.WCColoredButton import com.woocommerce.android.ui.compose.modifiers.dashedBorder import com.woocommerce.android.ui.compose.theme.WooThemeWithBackground import com.woocommerce.android.ui.orders.wooshippinglabels.WooShippingLabelCreationViewModel.PackageSelectionState import com.woocommerce.android.ui.orders.wooshippinglabels.WooShippingLabelCreationViewModel.PackageSelectionState.DataAvailable import com.woocommerce.android.ui.orders.wooshippinglabels.WooShippingLabelCreationViewModel.PackageSelectionState.NotSelected +import com.woocommerce.android.ui.orders.wooshippinglabels.address.AddressSelection import com.woocommerce.android.ui.orders.wooshippinglabels.address.getShipFrom import com.woocommerce.android.ui.orders.wooshippinglabels.address.getShipTo import com.woocommerce.android.ui.orders.wooshippinglabels.models.OriginShippingAddress @@ -87,16 +92,17 @@ fun WooShippingLabelCreationScreen(viewModel: WooShippingLabelCreationViewModel) shippingRatesState = viewState.shippingRates, packageSelectionState = viewState.packageSelection, onShippingFromAddressChange = viewModel::onShippingFromAddressChange, - onShippingToAddressChange = viewModel::onShippingToAddressChange, onSelectedRateSortOrderChanged = viewModel::onSelectedRateSortOrderChanged, onRefreshShippingRates = viewModel::onRefreshShippingRates, onSelectedSippingRateChanged = viewModel::onSelectedSippingRateChanged, customWeight = viewModel.customWeight, onCustomWeightChange = viewModel::onCustomWeightChange, - markOrderComplete = viewState.markOrderComplete, + uiState = viewState.uiState, onMarkOrderCompleteChange = viewModel::onMarkOrderCompleteChange, onNavigateBack = viewModel::onNavigateBack, - purchaseState = viewState.purchaseState + purchaseState = viewState.purchaseState, + onShipmentDetailsExpandedChange = viewModel::onShipmentDetailsExpandedChange, + onSelectAddressExpandedChange = viewModel::onSelectAddressExpandedChange ) } @@ -108,6 +114,7 @@ fun WooShippingLabelCreationScreen(viewModel: WooShippingLabelCreationViewModel) } } +@Suppress("UnusedParameter") @Composable fun WooShippingLabelCreationScreen( shippableItems: ShippableItemsUI, @@ -116,7 +123,6 @@ fun WooShippingLabelCreationScreen( packageSelectionState: PackageSelectionState, shippingAddresses: WooShippingAddresses, onShippingFromAddressChange: (OriginShippingAddress) -> Unit, - onShippingToAddressChange: (Address) -> Unit, onSelectPackageClick: () -> Unit, onPurchaseShippingLabel: () -> Unit, onSelectedRateSortOrderChanged: (ShippingSortOption) -> Unit, @@ -124,13 +130,46 @@ fun WooShippingLabelCreationScreen( onCustomWeightChange: (String) -> Unit, onSelectedSippingRateChanged: (rate: ShippingRateUI) -> Unit, customWeight: String, - markOrderComplete: Boolean, + uiState: WooShippingLabelCreationViewModel.UIControlsState, onMarkOrderCompleteChange: (Boolean) -> Unit, + onShipmentDetailsExpandedChange: (Boolean) -> Unit, + onSelectAddressExpandedChange: (Boolean) -> Unit, purchaseState: WooShippingLabelCreationViewModel.PurchaseState, onNavigateBack: () -> Unit, modifier: Modifier = Modifier ) { - val scaffoldState = rememberBottomSheetScaffoldState() + val shipmentDetailsInitialValue = if (uiState.isShipmentDetailsExpanded) { + BottomSheetValue.Expanded + } else { + BottomSheetValue.Collapsed + } + + val shipFromSelectionBottomSheetValue = if (uiState.isAddressSelectionExpanded) { + ModalBottomSheetValue.Expanded + } else { + ModalBottomSheetValue.Hidden + } + + val shipFromSelectionBottomSheetState = + rememberModalBottomSheetState( + initialValue = shipFromSelectionBottomSheetValue, + confirmValueChange = { + // onSelectAddressExpandedChange(it == ModalBottomSheetValue.Expanded) + true + } + ) + + val shipmentDetailsBottomSheetState = rememberBottomSheetState( + initialValue = shipmentDetailsInitialValue, + confirmStateChange = { + // onShipmentDetailsExpandedChange(it == BottomSheetValue.Expanded) + true + } + ) + val scaffoldState = rememberBottomSheetScaffoldState( + bottomSheetState = shipmentDetailsBottomSheetState + ) + Box(modifier = Modifier.fillMaxSize()) { LabelCreationScreenWithBottomSheet( shippableItems = shippableItems, @@ -142,15 +181,15 @@ fun WooShippingLabelCreationScreen( shippingRatesState = shippingRatesState, packageSelectionState = packageSelectionState, onShippingFromAddressChange = onShippingFromAddressChange, - onShippingToAddressChange = onShippingToAddressChange, onSelectedRateSortOrderChanged = onSelectedRateSortOrderChanged, onRefreshShippingRates = onRefreshShippingRates, customWeight = customWeight, onCustomWeightChange = onCustomWeightChange, onSelectedSippingRateChanged = onSelectedSippingRateChanged, - markOrderComplete = markOrderComplete, + markOrderComplete = uiState.markOrderComplete, onNavigateBack = onNavigateBack, - onMarkOrderCompleteChange = onMarkOrderCompleteChange + onMarkOrderCompleteChange = onMarkOrderCompleteChange, + shipFromSelectionBottomSheetState = shipFromSelectionBottomSheetState ) val isDarkTheme = isSystemInDarkTheme() val isCollapsed = scaffoldState.bottomSheetState.isCollapsed @@ -170,7 +209,7 @@ fun WooShippingLabelCreationScreen( if (LocalConfiguration.current.orientation == Configuration.ORIENTATION_LANDSCAPE) { PurchasesSectionLandscape( total = shippingRatesState.selectedRate?.selectedOption?.formatedPrice, - markOrderComplete = markOrderComplete, + markOrderComplete = uiState.markOrderComplete, onMarkOrderCompleteChange = onMarkOrderCompleteChange, onPurchaseShippingLabel = onPurchaseShippingLabel ) @@ -210,13 +249,13 @@ private fun LabelCreationScreenWithBottomSheet( onSelectPackageClick: () -> Unit, shippingAddresses: WooShippingAddresses, onShippingFromAddressChange: (OriginShippingAddress) -> Unit, - onShippingToAddressChange: (Address) -> Unit, onSelectedRateSortOrderChanged: (ShippingSortOption) -> Unit, onRefreshShippingRates: () -> Unit, customWeight: String, onCustomWeightChange: (String) -> Unit, onSelectedSippingRateChanged: (rate: ShippingRateUI) -> Unit, scaffoldState: BottomSheetScaffoldState, + shipFromSelectionBottomSheetState: ModalBottomSheetState, markOrderComplete: Boolean, onMarkOrderCompleteChange: (Boolean) -> Unit, onNavigateBack: () -> Unit, @@ -224,24 +263,30 @@ private fun LabelCreationScreenWithBottomSheet( ) { val isPurchaseButtonDisplayed = shippingRatesState is WooShippingLabelCreationViewModel.ShippingRatesState.DataState val bottomSheetPeekHeight = if (isPurchaseButtonDisplayed) 132.dp else 76.dp - val paddingBottom = if (isPurchaseButtonDisplayed) 74.dp else 0.dp + val paddingBottom = if (isPurchaseButtonDisplayed) 72.dp else 0.dp val shippingRateSummary = (shippingRatesState as? WooShippingLabelCreationViewModel.ShippingRatesState.DataState)?.selectedRate?.summary BottomSheetScaffold( sheetContent = { - ShipmentDetails( - shippableItems = shippableItems, - shippingLines = shippingLines, - scaffoldState = scaffoldState, - markOrderComplete = markOrderComplete, - onMarkOrderCompleteChange = onMarkOrderCompleteChange, - shippingAddresses = shippingAddresses, + AddressSelection( + shipFrom = shippingAddresses.shipFrom, + originAddresses = shippingAddresses.originAddresses, onShippingFromAddressChange = onShippingFromAddressChange, - onShippingToAddressChange = onShippingToAddressChange, - shippingRateSummary = shippingRateSummary, + modalBottomSheetState = shipFromSelectionBottomSheetState, modifier = Modifier.padding(bottom = paddingBottom), - ) + ) { + ShipmentDetails( + shippableItems = shippableItems, + shippingLines = shippingLines, + scaffoldState = scaffoldState, + shipFromSelectionBottomSheetState = shipFromSelectionBottomSheetState, + markOrderComplete = markOrderComplete, + onMarkOrderCompleteChange = onMarkOrderCompleteChange, + shippingAddresses = shippingAddresses, + shippingRateSummary = shippingRateSummary + ) + } }, sheetPeekHeight = bottomSheetPeekHeight, scaffoldState = scaffoldState, @@ -578,16 +623,21 @@ private fun WooShippingLabelCreationScreenPreview() { shippingRatesState = WooShippingLabelCreationViewModel.ShippingRatesState.NoAvailable, packageSelectionState = NotSelected, onShippingFromAddressChange = {}, - onShippingToAddressChange = {}, onRefreshShippingRates = {}, onSelectedRateSortOrderChanged = {}, customWeight = "", onCustomWeightChange = {}, onSelectedSippingRateChanged = {}, - markOrderComplete = true, onMarkOrderCompleteChange = {}, onNavigateBack = {}, - purchaseState = WooShippingLabelCreationViewModel.PurchaseState.NoStarted + purchaseState = WooShippingLabelCreationViewModel.PurchaseState.NoStarted, + uiState = WooShippingLabelCreationViewModel.UIControlsState( + markOrderComplete = false, + isShipmentDetailsExpanded = false, + isAddressSelectionExpanded = false + ), + onShipmentDetailsExpandedChange = {}, + onSelectAddressExpandedChange = {} ) } } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/address/AddressSection.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/address/AddressSection.kt index c59c721b07b..5bbd956ecce 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/address/AddressSection.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/address/AddressSection.kt @@ -1,54 +1,60 @@ package com.woocommerce.android.ui.orders.wooshippinglabels.address +import androidx.compose.animation.animateColorAsState +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.IntrinsicSize import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.sizeIn +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.Divider -import androidx.compose.material.DropdownMenu -import androidx.compose.material.DropdownMenuItem import androidx.compose.material.Icon import androidx.compose.material.IconButton import androidx.compose.material.MaterialTheme +import androidx.compose.material.ModalBottomSheetLayout +import androidx.compose.material.ModalBottomSheetState +import androidx.compose.material.ModalBottomSheetValue import androidx.compose.material.Text import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.MoreHoriz +import androidx.compose.material.rememberModalBottomSheetState import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.res.colorResource import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp import androidx.constraintlayout.compose.ConstraintLayout import androidx.constraintlayout.compose.Dimension import com.woocommerce.android.R import com.woocommerce.android.model.Address import com.woocommerce.android.model.AmbiguousLocation import com.woocommerce.android.model.Location +import com.woocommerce.android.ui.compose.component.BottomSheetHandle import com.woocommerce.android.ui.compose.theme.WooThemeWithBackground import com.woocommerce.android.ui.orders.wooshippinglabels.RoundedCornerBoxWithBorder +import com.woocommerce.android.ui.orders.wooshippinglabels.ShipmentDetailsSectionTitle import com.woocommerce.android.ui.orders.wooshippinglabels.VerticalDivider import com.woocommerce.android.ui.orders.wooshippinglabels.WooShippingAddresses import com.woocommerce.android.ui.orders.wooshippinglabels.models.OriginShippingAddress +import com.woocommerce.android.ui.orders.wooshippinglabels.rates.ui.shippingSelectedBackgroundColor import com.woocommerce.android.ui.orders.wooshippinglabels.toShippingFromString +import kotlinx.coroutines.launch @Composable @Suppress("DestructuringDeclarationWithTooManyEntries", "UnusedParameter") internal fun AddressSectionPortrait( shippingAddresses: WooShippingAddresses, - originAddresses: List, - onShippingFromAddressChange: (OriginShippingAddress) -> Unit, - onShippingToAddressChange: (Address) -> Unit, + shipFromSelectionBottomSheetState: ModalBottomSheetState, modifier: Modifier = Modifier, isReadOnly: Boolean = false ) { @@ -66,7 +72,7 @@ internal fun AddressSectionPortrait( val barrier = createEndBarrier(shipFromLabel, shipToLabel) val endBarrier = createStartBarrier(shipFromSelect) - var expanded by remember { mutableStateOf(false) } + val scope = rememberCoroutineScope() Text( text = stringResource(id = R.string.orderdetail_shipping_label_item_shipfrom), @@ -103,7 +109,13 @@ internal fun AddressSectionPortrait( ) if (isReadOnly.not()) { IconButton( - onClick = { expanded = true }, + onClick = { + if (shipFromSelectionBottomSheetState.isVisible.not()) { + scope.launch { + shipFromSelectionBottomSheetState.show() + } + } + }, modifier = Modifier .constrainAs(shipFromSelect) { top.linkTo(shipFromLabel.top) @@ -119,24 +131,6 @@ internal fun AddressSectionPortrait( contentDescription = null, tint = MaterialTheme.colors.primary ) - DropdownMenu( - expanded = expanded, - onDismissRequest = { expanded = false }, - modifier = Modifier.sizeIn(minWidth = 150.dp) - ) { - originAddresses.forEach { option -> - DropdownMenuItem(onClick = { - onShippingFromAddressChange(option) - expanded = false - }) { - Text( - text = option.toShippingFromString().uppercase(), - style = MaterialTheme.typography.subtitle1, - modifier = Modifier.padding(8.dp) - ) - } - } - } } } Divider( @@ -207,9 +201,8 @@ private fun AddressSectionPortraitPreview() { shipTo = getShipTo(), originAddresses = listOf(getShipFrom()) ), - originAddresses = listOf(getShipFrom()), - onShippingFromAddressChange = {}, - onShippingToAddressChange = {} + shipFromSelectionBottomSheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden), + isReadOnly = false ) } } @@ -219,8 +212,7 @@ private fun AddressSectionPortraitPreview() { @Suppress("UnusedParameter") internal fun AddressSectionLandscape( shippingAddresses: WooShippingAddresses, - onShippingFromAddressChange: (OriginShippingAddress) -> Unit, - onShippingToAddressChange: (Address) -> Unit, + shipFromSelectionBottomSheetState: ModalBottomSheetState, modifier: Modifier = Modifier, isReadOnly: Boolean = false ) { @@ -232,9 +224,8 @@ internal fun AddressSectionLandscape( ) { ShipFromSelection( shipFrom = shippingAddresses.shipFrom, - originAddresses = shippingAddresses.originAddresses, - onShippingFromAddressChange = onShippingFromAddressChange, modifier = Modifier.weight(1f), + shipFromSelectionBottomSheetState = shipFromSelectionBottomSheetState, isReadOnly = isReadOnly ) VerticalDivider() @@ -293,12 +284,10 @@ internal fun AddressSectionLandscape( @Composable private fun ShipFromSelection( shipFrom: OriginShippingAddress, - originAddresses: List, - onShippingFromAddressChange: (OriginShippingAddress) -> Unit, + shipFromSelectionBottomSheetState: ModalBottomSheetState, modifier: Modifier = Modifier, isReadOnly: Boolean = false ) { - var expanded by remember { mutableStateOf(false) } Row(modifier = modifier) { Text( text = stringResource(id = R.string.orderdetail_shipping_label_item_shipfrom), @@ -324,8 +313,15 @@ private fun ShipFromSelection( .weight(1f) ) if (isReadOnly.not()) { + val scope = rememberCoroutineScope() IconButton( - onClick = { expanded = true }, + onClick = { + if (shipFromSelectionBottomSheetState.isVisible.not()) { + scope.launch { + shipFromSelectionBottomSheetState.show() + } + } + }, modifier = Modifier .padding( top = dimensionResource(R.dimen.minor_50), @@ -337,24 +333,112 @@ private fun ShipFromSelection( contentDescription = null, tint = MaterialTheme.colors.primary ) - DropdownMenu( - expanded = expanded, - onDismissRequest = { expanded = false }, - modifier = Modifier.sizeIn(minWidth = 150.dp) - ) { - originAddresses.forEach { option -> - DropdownMenuItem(onClick = { - onShippingFromAddressChange(option) - expanded = false - }) { - Text( - text = option.toShippingFromString().uppercase(), - style = MaterialTheme.typography.subtitle1, - modifier = Modifier.padding(8.dp) - ) - } - } - } + } + } + } +} + +@Composable +fun AddressSelection( + modalBottomSheetState: ModalBottomSheetState, + shipFrom: OriginShippingAddress, + originAddresses: List, + onShippingFromAddressChange: (OriginShippingAddress) -> Unit, + modifier: Modifier = Modifier, + content: @Composable () -> Unit = {} +) { + ModalBottomSheetLayout( + modifier = modifier, + sheetState = modalBottomSheetState, + sheetContent = { + BottomSheetHandle( + modifier = Modifier + .align(Alignment.CenterHorizontally) + .padding(top = dimensionResource(id = R.dimen.minor_100)) + ) + ShipmentDetailsSectionTitle( + title = stringResource(R.string.orderdetail_shipping_label_item_shipfrom), + modifier = Modifier.padding( + start = dimensionResource(id = R.dimen.major_100), + top = dimensionResource(id = R.dimen.major_100), + bottom = dimensionResource(id = R.dimen.minor_100) + ) + ) + originAddresses.forEach { option -> + val isSelected = option == shipFrom + AddressSelectionItem( + address = option, + isSelected = isSelected, + onClick = { + onShippingFromAddressChange(option) + }, + modifier = Modifier.padding( + top = dimensionResource(id = R.dimen.minor_100), + start = dimensionResource(id = R.dimen.major_100), + end = dimensionResource(id = R.dimen.major_100) + ) + ) + } + Spacer(modifier = Modifier.height(dimensionResource(id = R.dimen.major_100))) + }, + sheetShape = RoundedCornerShape( + topStart = dimensionResource(id = R.dimen.corner_radius_large), + topEnd = dimensionResource(id = R.dimen.corner_radius_large) + ), + content = content + ) +} + +@Composable +fun AddressSelectionItem( + address: OriginShippingAddress, + isSelected: Boolean, + onClick: () -> Unit, + modifier: Modifier = Modifier +) { + val borderColor = if (isSelected) { + MaterialTheme.colors.primary + } else { + colorResource(R.color.divider_color) + } + + val backgroundColor = if (isSelected) { + animateColorAsState( + targetValue = MaterialTheme.colors.shippingSelectedBackgroundColor, + label = "colorAnimation" + ) + } else { + animateColorAsState(targetValue = MaterialTheme.colors.surface, label = "colorAnimation") + } + + RoundedCornerBoxWithBorder( + modifier = modifier, + innerModifier = Modifier + .clickable { onClick() } + .padding(dimensionResource(id = R.dimen.major_100)), + borderColor = borderColor, + backgroundColor = backgroundColor.value + ) { + Row { + Column(modifier = Modifier.weight(1f)) { + Text( + text = "Address Name", + fontWeight = FontWeight.Bold, + modifier = Modifier + ) + Text( + text = address.toShippingFromString(), + modifier = Modifier.padding(top = dimensionResource(id = R.dimen.minor_100)) + ) + } + IconButton( + onClick = { } + ) { + Icon( + painter = painterResource(id = R.drawable.ic_edit_pencil), + contentDescription = null, + tint = MaterialTheme.colors.primary, + ) } } } @@ -371,8 +455,8 @@ private fun AddressSectionLandscapePreview() { shipTo = getShipTo(), originAddresses = listOf(getShipFrom()) ), - onShippingFromAddressChange = {}, - onShippingToAddressChange = {} + shipFromSelectionBottomSheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden), + isReadOnly = false ) } } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/purchased/WooShippingLabelPurchasedScreen.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/purchased/WooShippingLabelPurchasedScreen.kt index 06cdf36fbc1..386f3803296 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/purchased/WooShippingLabelPurchasedScreen.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/purchased/WooShippingLabelPurchasedScreen.kt @@ -22,6 +22,7 @@ import androidx.compose.material.DropdownMenu import androidx.compose.material.DropdownMenuItem import androidx.compose.material.Icon import androidx.compose.material.MaterialTheme +import androidx.compose.material.ModalBottomSheetValue import androidx.compose.material.Surface import androidx.compose.material.Text import androidx.compose.material.icons.Icons @@ -29,6 +30,7 @@ import androidx.compose.material.icons.automirrored.outlined.OpenInNew import androidx.compose.material.icons.filled.ArrowDropDown import androidx.compose.material.icons.outlined.Info import androidx.compose.material.rememberBottomSheetScaffoldState +import androidx.compose.material.rememberModalBottomSheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState @@ -108,7 +110,8 @@ internal fun WooShippingLabelPurchasedWithBottomSheetScreen( shippingLines = shippingData.shippingLines, shippingAddresses = shippingData.addresses, shippingRateSummary = shippingData.rateSummary, - isReadOnly = true + isReadOnly = true, + shipFromSelectionBottomSheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden) ) } }, diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/rates/ui/ShippingRatesCard.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/rates/ui/ShippingRatesCard.kt index 475a9dc9f5c..6233c06dd06 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/rates/ui/ShippingRatesCard.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/rates/ui/ShippingRatesCard.kt @@ -68,7 +68,7 @@ import java.math.BigDecimal import kotlin.random.Random @Suppress("MagicNumber") -val Colors.selectedRateBackgroundColor: Color get() = if (isLight) Color(0xFFF2EDFF) else Color(0x22F2EDFF) +val Colors.shippingSelectedBackgroundColor: Color get() = if (isLight) Color(0xFFF2EDFF) else Color(0x22F2EDFF) @Composable internal fun ShippingRatesCard( @@ -324,7 +324,10 @@ private fun ShippingRateItem( } val backgroundColor = if (isSelected) { - animateColorAsState(targetValue = MaterialTheme.colors.selectedRateBackgroundColor, label = "colorAnimation") + animateColorAsState( + targetValue = MaterialTheme.colors.shippingSelectedBackgroundColor, + label = "colorAnimation" + ) } else { animateColorAsState(targetValue = MaterialTheme.colors.surface, label = "colorAnimation") } From 52521c7b676f065e19d87e760d07cd6d53e57390 Mon Sep 17 00:00:00 2001 From: Alejo Date: Thu, 2 Jan 2025 18:26:36 -0300 Subject: [PATCH 3/3] default state to false (collapse) --- .../wooshippinglabels/WooShippingLabelCreationViewModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/WooShippingLabelCreationViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/WooShippingLabelCreationViewModel.kt index 2971034b863..d308365fdb7 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/WooShippingLabelCreationViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/wooshippinglabels/WooShippingLabelCreationViewModel.kt @@ -70,7 +70,7 @@ class WooShippingLabelCreationViewModel @Inject constructor( private val uiState = MutableStateFlow( UIControlsState( markOrderComplete = false, - isShipmentDetailsExpanded = true, + isShipmentDetailsExpanded = false, isAddressSelectionExpanded = false ) )