Skip to content

Commit

Permalink
Merge pull request #13290 from woocommerce/issue/12439-save-bottom-sh…
Browse files Browse the repository at this point in the history
…eets-state-viewmodel

Save bottom sheets state viewmodel
  • Loading branch information
atorresveiga authored Jan 14, 2025
2 parents 7fa5ee8 + 6c35f79 commit 68bcda6
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 73 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ 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
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalConfiguration
Expand All @@ -55,7 +54,6 @@ 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
import com.woocommerce.android.util.StringUtils
import kotlinx.coroutines.launch
import kotlinx.parcelize.Parcelize

@Composable
Expand All @@ -67,54 +65,48 @@ fun ShipmentDetails(
shippingAddresses: WooShippingAddresses,
shippingRateSummary: ShippingRateSummaryUI?,
modifier: Modifier = Modifier,
isShipmentDetailsExpanded: Boolean = false,
onShipmentDetailsExpandedChange: (Boolean) -> Boolean,
markOrderComplete: Boolean = false,
onMarkOrderCompleteChange: (Boolean) -> Unit = {},
handlerModifier: Modifier = Modifier,
isReadOnly: Boolean = false
) {
val scope = rememberCoroutineScope()
Column(
handlerModifier
.clickable(
onClick = {
scope.launch {
if (scaffoldState.bottomSheetState.isCollapsed) {
scaffoldState.bottomSheetState.expand()
} else {
scaffoldState.bottomSheetState.collapse()
}
}
Column {
Column(
handlerModifier
.clickable(
onClick = { onShipmentDetailsExpandedChange(isShipmentDetailsExpanded.not()) },
interactionSource = remember { MutableInteractionSource() },
indication = null
)
.fillMaxWidth()
.padding(dimensionResource(R.dimen.minor_100)),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Icon(
modifier = Modifier.padding(top = dimensionResource(R.dimen.minor_100)),
painter = if (scaffoldState.bottomSheetState.isExpanded) {
painterResource(R.drawable.ic_arrow_down_26)
} else {
painterResource(R.drawable.ic_arrow_up_26)
},
interactionSource = remember { MutableInteractionSource() },
indication = null
contentDescription = stringResource(R.string.order_creation_expand_collapse_order_totals),
tint = colorResource(id = R.color.color_primary),
)
.fillMaxWidth()
.padding(dimensionResource(R.dimen.minor_100)),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Icon(
modifier = Modifier.padding(top = dimensionResource(R.dimen.minor_100)),
painter = if (scaffoldState.bottomSheetState.isExpanded) {
painterResource(R.drawable.ic_arrow_down_26)
} else {
painterResource(R.drawable.ic_arrow_up_26)
},
contentDescription = stringResource(R.string.order_creation_expand_collapse_order_totals),
tint = colorResource(id = R.color.color_primary),
)
AnimatedVisibility(visible = scaffoldState.bottomSheetState.isCollapsed) {
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)))
AnimatedVisibility(isShipmentDetailsExpanded.not()) {
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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,15 @@ import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.BottomSheetScaffold
import androidx.compose.material.BottomSheetScaffoldDefaults
import androidx.compose.material.BottomSheetScaffoldState
import androidx.compose.material.BottomSheetState
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.ModalBottomSheetDefaults
import androidx.compose.material.ModalBottomSheetState
import androidx.compose.material.ModalBottomSheetValue
import androidx.compose.material.Surface
Expand All @@ -37,15 +40,14 @@ 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
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.painterResource
Expand Down Expand Up @@ -114,7 +116,6 @@ fun WooShippingLabelCreationScreen(viewModel: WooShippingLabelCreationViewModel)
}
}

@Suppress("UnusedParameter")
@Composable
fun WooShippingLabelCreationScreen(
shippableItems: ShippableItemsUI,
Expand All @@ -132,13 +133,13 @@ fun WooShippingLabelCreationScreen(
customWeight: String,
uiState: WooShippingLabelCreationViewModel.UIControlsState,
onMarkOrderCompleteChange: (Boolean) -> Unit,
onShipmentDetailsExpandedChange: (Boolean) -> Unit,
onSelectAddressExpandedChange: (Boolean) -> Unit,
onShipmentDetailsExpandedChange: (Boolean) -> Boolean,
onSelectAddressExpandedChange: (Boolean) -> Boolean,
purchaseState: WooShippingLabelCreationViewModel.PurchaseState,
onNavigateBack: () -> Unit,
modifier: Modifier = Modifier
) {
val shipmentDetailsInitialValue = if (uiState.isShipmentDetailsExpanded) {
val shipmentDetailsValue = if (uiState.isShipmentDetailsExpanded) {
BottomSheetValue.Expanded
} else {
BottomSheetValue.Collapsed
Expand All @@ -150,22 +151,25 @@ fun WooShippingLabelCreationScreen(
ModalBottomSheetValue.Hidden
}

val shipFromSelectionBottomSheetState =
rememberModalBottomSheetState(
initialValue = shipFromSelectionBottomSheetValue,
confirmValueChange = {
// onSelectAddressExpandedChange(it == ModalBottomSheetValue.Expanded)
true
}
)
val shipFromSelectionBottomSheetState = ModalBottomSheetState(
density = LocalDensity.current,
initialValue = shipFromSelectionBottomSheetValue,
animationSpec = ModalBottomSheetDefaults.AnimationSpec,
isSkipHalfExpanded = true,
confirmValueChange = {
onSelectAddressExpandedChange(it == ModalBottomSheetValue.Expanded)
}
)

val shipmentDetailsBottomSheetState = rememberBottomSheetState(
initialValue = shipmentDetailsInitialValue,
confirmStateChange = {
// onShipmentDetailsExpandedChange(it == BottomSheetValue.Expanded)
true
val shipmentDetailsBottomSheetState = BottomSheetState(
initialValue = shipmentDetailsValue,
animationSpec = BottomSheetScaffoldDefaults.AnimationSpec,
density = LocalDensity.current,
confirmValueChange = {
onShipmentDetailsExpandedChange(it == BottomSheetValue.Expanded)
}
)

val scaffoldState = rememberBottomSheetScaffoldState(
bottomSheetState = shipmentDetailsBottomSheetState
)
Expand All @@ -186,10 +190,11 @@ fun WooShippingLabelCreationScreen(
customWeight = customWeight,
onCustomWeightChange = onCustomWeightChange,
onSelectedSippingRateChanged = onSelectedSippingRateChanged,
markOrderComplete = uiState.markOrderComplete,
uiState = uiState,
onNavigateBack = onNavigateBack,
onMarkOrderCompleteChange = onMarkOrderCompleteChange,
shipFromSelectionBottomSheetState = shipFromSelectionBottomSheetState
shipFromSelectionBottomSheetState = shipFromSelectionBottomSheetState,
onShipmentDetailsExpandedChange = onShipmentDetailsExpandedChange
)
val isDarkTheme = isSystemInDarkTheme()
val isCollapsed = scaffoldState.bottomSheetState.isCollapsed
Expand Down Expand Up @@ -254,11 +259,12 @@ private fun LabelCreationScreenWithBottomSheet(
customWeight: String,
onCustomWeightChange: (String) -> Unit,
onSelectedSippingRateChanged: (rate: ShippingRateUI) -> Unit,
uiState: WooShippingLabelCreationViewModel.UIControlsState,
scaffoldState: BottomSheetScaffoldState,
shipFromSelectionBottomSheetState: ModalBottomSheetState,
markOrderComplete: Boolean,
onMarkOrderCompleteChange: (Boolean) -> Unit,
onNavigateBack: () -> Unit,
onShipmentDetailsExpandedChange: (Boolean) -> Boolean,
modifier: Modifier = Modifier
) {
val isPurchaseButtonDisplayed = shippingRatesState is WooShippingLabelCreationViewModel.ShippingRatesState.DataState
Expand All @@ -279,12 +285,14 @@ private fun LabelCreationScreenWithBottomSheet(
ShipmentDetails(
shippableItems = shippableItems,
shippingLines = shippingLines,
scaffoldState = scaffoldState,
shipFromSelectionBottomSheetState = shipFromSelectionBottomSheetState,
markOrderComplete = markOrderComplete,
onMarkOrderCompleteChange = onMarkOrderCompleteChange,
shippingAddresses = shippingAddresses,
shippingRateSummary = shippingRateSummary
shippingRateSummary = shippingRateSummary,
scaffoldState = scaffoldState,
isShipmentDetailsExpanded = uiState.isShipmentDetailsExpanded,
markOrderComplete = uiState.markOrderComplete,
onShipmentDetailsExpandedChange = onShipmentDetailsExpandedChange
)
}
},
Expand Down Expand Up @@ -636,8 +644,8 @@ private fun WooShippingLabelCreationScreenPreview() {
isShipmentDetailsExpanded = false,
isAddressSelectionExpanded = false
),
onShipmentDetailsExpandedChange = {},
onSelectAddressExpandedChange = {}
onShipmentDetailsExpandedChange = { true },
onSelectAddressExpandedChange = { true }
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -318,12 +318,18 @@ class WooShippingLabelCreationViewModel @Inject constructor(
uiState.update { it.copy(markOrderComplete = value) }
}

fun onShipmentDetailsExpandedChange(value: Boolean) {
uiState.update { it.copy(isShipmentDetailsExpanded = value) }
fun onShipmentDetailsExpandedChange(value: Boolean): Boolean {
return if (uiState.value.isAddressSelectionExpanded.not()) {
uiState.update { it.copy(isShipmentDetailsExpanded = value) }
true
} else {
false
}
}

fun onSelectAddressExpandedChange(value: Boolean) {
fun onSelectAddressExpandedChange(value: Boolean): Boolean {
uiState.update { it.copy(isAddressSelectionExpanded = value) }
return true
}

private fun getTotalPrice(items: List<ShippableItemModel>): String {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
Expand All @@ -58,6 +59,7 @@ import com.woocommerce.android.ui.orders.wooshippinglabels.ShipmentDetails
import com.woocommerce.android.ui.orders.wooshippinglabels.ShippableItemsUI
import com.woocommerce.android.ui.orders.wooshippinglabels.ShippingProductsCard
import com.woocommerce.android.ui.orders.wooshippinglabels.generateItems
import kotlinx.coroutines.launch

@Suppress("MagicNumber")
private val darkGreen = Color(0xFF005C12)
Expand Down Expand Up @@ -101,17 +103,29 @@ internal fun WooShippingLabelPurchasedWithBottomSheetScreen(
modifier: Modifier = Modifier,
) {
val scaffoldState = rememberBottomSheetScaffoldState()
val scope = rememberCoroutineScope()
BottomSheetScaffold(
sheetContent = {
shippingData?.let {
ShipmentDetails(
scaffoldState = scaffoldState,
shippableItems = shippingData.items,
shippingLines = shippingData.shippingLines,
shippingAddresses = shippingData.addresses,
shippingRateSummary = shippingData.rateSummary,
isReadOnly = true,
shipFromSelectionBottomSheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)
shipFromSelectionBottomSheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden),
scaffoldState = scaffoldState,
isShipmentDetailsExpanded = scaffoldState.bottomSheetState.isExpanded,
onShipmentDetailsExpandedChange = {
scope.launch {
if (scaffoldState.bottomSheetState.isExpanded) {
scaffoldState.bottomSheetState.collapse()
} else {
scaffoldState.bottomSheetState.expand()
}
}
true
}
)
}
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -672,4 +672,53 @@ class WooShippingLabelCreationViewModelTest : BaseUnitTest() {
val currentViewState = sut.viewState.value
assertThat(currentViewState).isInstanceOf(WooShippingViewState.Error::class.java)
}

@Test
fun `when address selection is collapsed then changes shipment details are allowed`() = testBlocking {
val order = OrderTestUtils.generateTestOrder(orderId = orderId)

whenever(orderDetailRepository.getOrderById(any())) doReturn order
whenever(observeOriginAddresses()) doReturn flowOf(defaultOriginAddresses)
whenever(observeStoreOptions()) doReturn flowOf(null)

createViewModel()

advanceUntilIdle()
// Collapse shipment details and select address
var changeAccepted = sut.onShipmentDetailsExpandedChange(false)
assertThat(changeAccepted).isTrue()
changeAccepted = sut.onSelectAddressExpandedChange(false)
assertThat(changeAccepted).isTrue()

// Check all changes are accepted
changeAccepted = sut.onShipmentDetailsExpandedChange(false)
assertThat(changeAccepted).isTrue()
changeAccepted = sut.onShipmentDetailsExpandedChange(true)
assertThat(changeAccepted).isTrue()
}

@Test
fun `when address selection is expanded then prevent any change on the shipment details`() = testBlocking {
val order = OrderTestUtils.generateTestOrder(orderId = orderId)

whenever(orderDetailRepository.getOrderById(any())) doReturn order
whenever(observeOriginAddresses()) doReturn flowOf(defaultOriginAddresses)
whenever(observeStoreOptions()) doReturn flowOf(null)

createViewModel()

advanceUntilIdle()
// Expand shipment details and select address
var changeAccepted = sut.onShipmentDetailsExpandedChange(true)
assertThat(changeAccepted).isTrue()

changeAccepted = sut.onSelectAddressExpandedChange(true)
assertThat(changeAccepted).isTrue()

// Check no changes are accepted when select address is expanded
changeAccepted = sut.onShipmentDetailsExpandedChange(false)
assertThat(changeAccepted).isFalse()
changeAccepted = sut.onShipmentDetailsExpandedChange(true)
assertThat(changeAccepted).isFalse()
}
}

0 comments on commit 68bcda6

Please sign in to comment.