From c2b18b0d66b0ebc340033f5135c7507651bb9f34 Mon Sep 17 00:00:00 2001 From: Andrey Date: Thu, 18 Jul 2024 16:57:21 +0200 Subject: [PATCH 01/12] Moved toolbar as part of the home screen from the root --- .../ui/woopos/home/WooPosHomeScreen.kt | 73 ++++++----- .../android/ui/woopos/home/WooPosHomeState.kt | 30 +++-- .../ui/woopos/home/WooPosHomeUIEvent.kt | 1 + .../ui/woopos/home/WooPosHomeViewModel.kt | 62 ++++++--- .../toolbar/WooPosHomeToolbar.kt} | 34 +++-- .../toolbar/WooPosHomeToolbarState.kt} | 12 +- .../home/toolbar/WooPosHomeToolbarUIEvent.kt | 8 ++ .../toolbar/WooPosHomeToolbarViewModel.kt | 104 +++++++++++++++ .../ui/woopos/root/WooPosRootScreen.kt | 68 +--------- .../ui/woopos/root/WooPosRootUIEvent.kt | 10 -- .../ui/woopos/root/WooPosRootViewModel.kt | 119 ------------------ .../root/navigation/WooPosNavigationEvent.kt | 1 - .../WooPosNavigationEventHandler.kt | 4 - .../ui/woopos/home/WooPosHomeViewModelTest.kt | 1 - .../ui/woopos/root/WooPosRootViewModelTest.kt | 2 - 15 files changed, 241 insertions(+), 288 deletions(-) rename WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/{root/WooPosRootFloatingToolbar.kt => home/toolbar/WooPosHomeToolbar.kt} (92%) rename WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/{root/WooPosRootScreenState.kt => home/toolbar/WooPosHomeToolbarState.kt} (58%) create mode 100644 WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeToolbarUIEvent.kt create mode 100644 WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeToolbarViewModel.kt delete mode 100644 WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/root/WooPosRootUIEvent.kt delete mode 100644 WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/root/WooPosRootViewModel.kt diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeScreen.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeScreen.kt index e8ed1649d7e..16aeeb14dfd 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeScreen.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeScreen.kt @@ -23,11 +23,13 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import com.woocommerce.android.ui.woopos.common.composeui.WooPosPreview import com.woocommerce.android.ui.woopos.common.composeui.WooPosTheme +import com.woocommerce.android.ui.woopos.common.composeui.component.WooPosConfirmationDialog import com.woocommerce.android.ui.woopos.common.composeui.isPreviewMode import com.woocommerce.android.ui.woopos.common.composeui.toAdaptivePadding import com.woocommerce.android.ui.woopos.home.cart.WooPosCartScreen @@ -56,13 +58,21 @@ fun WooPosHomeScreen( private fun WooPosHomeScreen( state: WooPosHomeState, onNavigationEvent: (WooPosNavigationEvent) -> Unit, - onHomeUIEvent: (WooPosHomeUIEvent) -> Boolean, + onHomeUIEvent: (WooPosHomeUIEvent) -> Unit, ) { BackHandler { - val result = onHomeUIEvent(WooPosHomeUIEvent.SystemBackClicked) - if (!result) { - onNavigationEvent(WooPosNavigationEvent.BackFromHomeClicked) - } + onHomeUIEvent(WooPosHomeUIEvent.SystemBackClicked) + } + + state.exitConfirmationDialog?.let { + WooPosConfirmationDialog( + title = stringResource(id = it.title), + message = stringResource(id = it.message), + confirmButtonText = stringResource(id = it.confirmButton), + dismissButtonText = stringResource(id = it.dismissButton), + onDismiss = { onHomeUIEvent(WooPosHomeUIEvent.ExitConfirmationDialogDismissed) }, + onConfirm = { onNavigationEvent(WooPosNavigationEvent.ExitPosClicked) } + ) } val current = LocalConfiguration.current @@ -72,35 +82,35 @@ private fun WooPosHomeScreen( val totalsWidthDp = remember(screenWidthDp, cartWidthDp) { screenWidthDp - cartWidthDp } val totalsWidthAnimatedDp by animateDpAsState( - when (state) { - is WooPosHomeState.Checkout.Paid -> screenWidthDp - is WooPosHomeState.Cart, - WooPosHomeState.Checkout.NotPaid -> totalsWidthDp + when (state.screenPositionState) { + is WooPosHomeState.ScreenPositionState.Checkout.Paid -> screenWidthDp + is WooPosHomeState.ScreenPositionState.Cart, + WooPosHomeState.ScreenPositionState.Checkout.NotPaid -> totalsWidthDp }, label = "totalsWidthAnimatedDp" ) val cartOverlayIntensityAnimated by animateFloatAsState( - when (state) { - is WooPosHomeState.Cart.Empty -> .6f - WooPosHomeState.Cart.NotEmpty, - WooPosHomeState.Checkout.NotPaid, - WooPosHomeState.Checkout.Paid -> 0f + when (state.screenPositionState) { + is WooPosHomeState.ScreenPositionState.Cart.Empty -> .6f + WooPosHomeState.ScreenPositionState.Cart.NotEmpty, + WooPosHomeState.ScreenPositionState.Checkout.NotPaid, + WooPosHomeState.ScreenPositionState.Checkout.Paid -> 0f }, label = "cartOverlayAnimated" ) val totalsStartPaddingAnimatedDp by animateDpAsState( - when (state) { - is WooPosHomeState.Cart, - WooPosHomeState.Checkout.NotPaid -> 0.dp.toAdaptivePadding() + when (state.screenPositionState) { + is WooPosHomeState.ScreenPositionState.Cart, + WooPosHomeState.ScreenPositionState.Checkout.NotPaid -> 0.dp.toAdaptivePadding() - WooPosHomeState.Checkout.Paid -> 24.dp.toAdaptivePadding() + WooPosHomeState.ScreenPositionState.Checkout.Paid -> 24.dp.toAdaptivePadding() }, label = "totalsStartPaddingAnimatedDp" ) - val scrollState = buildScrollStateForNavigationBetweenState(state) + val scrollState = buildScrollStateForNavigationBetweenState(state.screenPositionState) WooPosHomeScreen( scrollState = scrollState, productsWidthDp = productsWidthDp, @@ -191,19 +201,18 @@ private fun WooPosHomeScreenTotals(modifier: Modifier) { } @Composable -private fun buildScrollStateForNavigationBetweenState(state: WooPosHomeState): ScrollState { +private fun buildScrollStateForNavigationBetweenState(state: WooPosHomeState.ScreenPositionState): ScrollState { val scrollState = rememberScrollState() LaunchedEffect(state) { val animationSpec = spring(dampingRatio = 0.8f, stiffness = 200f) when (state) { - is WooPosHomeState.Cart -> { + is WooPosHomeState.ScreenPositionState.Cart -> scrollState.animateScrollTo( 0, animationSpec = animationSpec ) - } - is WooPosHomeState.Checkout -> scrollState.animateScrollTo( + is WooPosHomeState.ScreenPositionState.Checkout -> scrollState.animateScrollTo( scrollState.maxValue, animationSpec = animationSpec ) @@ -217,8 +226,8 @@ private fun buildScrollStateForNavigationBetweenState(state: WooPosHomeState): S fun WooPosHomeCartScreenPreview() { WooPosTheme { WooPosHomeScreen( - state = WooPosHomeState.Cart.NotEmpty, - onHomeUIEvent = { true }, + state = WooPosHomeState(screenPositionState = WooPosHomeState.ScreenPositionState.Cart.NotEmpty), + onHomeUIEvent = { }, onNavigationEvent = {}, ) } @@ -229,8 +238,10 @@ fun WooPosHomeCartScreenPreview() { fun WooPosHomeCartEmptyScreenPreview() { WooPosTheme { WooPosHomeScreen( - state = WooPosHomeState.Cart.Empty, - onHomeUIEvent = { true }, + state = WooPosHomeState( + screenPositionState = WooPosHomeState.ScreenPositionState.Cart.Empty + ), + onHomeUIEvent = { }, onNavigationEvent = {}, ) } @@ -241,8 +252,8 @@ fun WooPosHomeCartEmptyScreenPreview() { fun WooPosHomeCheckoutScreenPreview() { WooPosTheme { WooPosHomeScreen( - state = WooPosHomeState.Checkout.NotPaid, - onHomeUIEvent = { true }, + state = WooPosHomeState(screenPositionState = WooPosHomeState.ScreenPositionState.Checkout.NotPaid), + onHomeUIEvent = { }, onNavigationEvent = {}, ) } @@ -253,8 +264,8 @@ fun WooPosHomeCheckoutScreenPreview() { fun WooPosHomeCheckoutPaidScreenPreview() { WooPosTheme { WooPosHomeScreen( - state = WooPosHomeState.Checkout.Paid, - onHomeUIEvent = { true }, + state = WooPosHomeState(screenPositionState = WooPosHomeState.ScreenPositionState.Checkout.Paid), + onHomeUIEvent = { }, onNavigationEvent = {}, ) } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeState.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeState.kt index 78048a5767d..787f26cd847 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeState.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeState.kt @@ -1,13 +1,27 @@ package com.woocommerce.android.ui.woopos.home -sealed class WooPosHomeState { - sealed class Cart : WooPosHomeState() { - data object Empty : Cart() - data object NotEmpty : Cart() - } +import com.woocommerce.android.R + +data class WooPosHomeState( + val screenPositionState: ScreenPositionState, + val exitConfirmationDialog: WooPosExitConfirmationDialog? = null, +) { + sealed class ScreenPositionState { + sealed class Cart : ScreenPositionState() { + data object Empty : Cart() + data object NotEmpty : Cart() + } - sealed class Checkout : WooPosHomeState() { - data object NotPaid : Checkout() - data object Paid : Checkout() + sealed class Checkout : ScreenPositionState() { + data object NotPaid : Checkout() + data object Paid : Checkout() + } } } + +data object WooPosExitConfirmationDialog { + val title: Int = R.string.woopos_exit_confirmation_title + val message: Int = R.string.woopos_exit_confirmation_message + val confirmButton: Int = R.string.woopos_exit_confirmation_confirm_button + val dismissButton: Int = R.string.woopos_exit_confirmation_dismiss_button +} diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeUIEvent.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeUIEvent.kt index 5357e5e8a1d..2bad9be9ae1 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeUIEvent.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeUIEvent.kt @@ -2,4 +2,5 @@ package com.woocommerce.android.ui.woopos.home sealed class WooPosHomeUIEvent { data object SystemBackClicked : WooPosHomeUIEvent() + data object ExitConfirmationDialogDismissed : WooPosHomeUIEvent() } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeViewModel.kt index cbc166c874f..7ebf45520ea 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeViewModel.kt @@ -13,34 +13,45 @@ class WooPosHomeViewModel @Inject constructor( private val childrenToParentEventReceiver: WooPosChildrenToParentEventReceiver, private val parentToChildrenEventSender: WooPosParentToChildrenEventSender, ) : ViewModel() { - private val _state = MutableStateFlow(WooPosHomeState.Cart.Empty) + private val _state = MutableStateFlow( + WooPosHomeState( + screenPositionState = WooPosHomeState.ScreenPositionState.Cart.Empty, + exitConfirmationDialog = null + ) + ) val state: StateFlow = _state init { listenBottomEvents() } - fun onUIEvent(event: WooPosHomeUIEvent): Boolean { + fun onUIEvent(event: WooPosHomeUIEvent) { return when (event) { WooPosHomeUIEvent.SystemBackClicked -> { - when (_state.value) { - WooPosHomeState.Checkout.NotPaid -> { - _state.value = WooPosHomeState.Cart.NotEmpty + when (_state.value.screenPositionState) { + WooPosHomeState.ScreenPositionState.Checkout.NotPaid -> { + _state.value = _state.value.copy( + screenPositionState = WooPosHomeState.ScreenPositionState.Cart.NotEmpty + ) sendEventToChildren(ParentToChildrenEvent.BackFromCheckoutToCartClicked) - true } - WooPosHomeState.Checkout.Paid -> { - _state.value = WooPosHomeState.Cart.Empty + WooPosHomeState.ScreenPositionState.Checkout.Paid -> { + _state.value = _state.value.copy( + screenPositionState = WooPosHomeState.ScreenPositionState.Cart.Empty + ) sendEventToChildren(ParentToChildrenEvent.OrderSuccessfullyPaid) - true } - is WooPosHomeState.Cart -> { - false + is WooPosHomeState.ScreenPositionState.Cart -> { + _state.value = _state.value.copy( + exitConfirmationDialog = WooPosExitConfirmationDialog + ) } } } + + else -> {} } } @@ -49,12 +60,16 @@ class WooPosHomeViewModel @Inject constructor( childrenToParentEventReceiver.events.collect { event -> when (event) { is ChildToParentEvent.CheckoutClicked -> { - _state.value = WooPosHomeState.Checkout.NotPaid + _state.value = _state.value.copy( + screenPositionState = WooPosHomeState.ScreenPositionState.Checkout.NotPaid + ) sendEventToChildren(ParentToChildrenEvent.CheckoutClicked(event.productIds)) } is ChildToParentEvent.BackFromCheckoutToCartClicked -> { - _state.value = WooPosHomeState.Cart.NotEmpty + _state.value = _state.value.copy( + screenPositionState = WooPosHomeState.ScreenPositionState.Cart.NotEmpty + ) } is ChildToParentEvent.ItemClickedInProductSelector -> { @@ -64,24 +79,35 @@ class WooPosHomeViewModel @Inject constructor( } is ChildToParentEvent.NewTransactionClicked -> { - _state.value = WooPosHomeState.Cart.Empty + _state.value = _state.value.copy( + screenPositionState = WooPosHomeState.ScreenPositionState.Cart.Empty + ) sendEventToChildren(ParentToChildrenEvent.OrderSuccessfullyPaid) } is ChildToParentEvent.OrderSuccessfullyPaid -> { - _state.value = WooPosHomeState.Checkout.Paid + _state.value = _state.value.copy( + screenPositionState = WooPosHomeState.ScreenPositionState.Checkout.Paid + ) + } is ChildToParentEvent.CartStatusChanged -> { - if (_state.value is WooPosHomeState.Checkout.Paid) return@collect + if (_state.value.screenPositionState is WooPosHomeState.ScreenPositionState.Checkout.Paid) { + return@collect + } when (event) { ChildToParentEvent.CartStatusChanged.Empty -> { - _state.value = WooPosHomeState.Cart.Empty + _state.value = _state.value.copy( + screenPositionState = WooPosHomeState.ScreenPositionState.Cart.Empty + ) } ChildToParentEvent.CartStatusChanged.NotEmpty -> { - _state.value = WooPosHomeState.Cart.NotEmpty + _state.value = _state.value.copy( + screenPositionState = WooPosHomeState.ScreenPositionState.Cart.NotEmpty + ) } } } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/root/WooPosRootFloatingToolbar.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeToolbar.kt similarity index 92% rename from WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/root/WooPosRootFloatingToolbar.kt rename to WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeToolbar.kt index 7048390a4f8..03c5c8bb767 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/root/WooPosRootFloatingToolbar.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeToolbar.kt @@ -1,4 +1,4 @@ -package com.woocommerce.android.ui.woopos.root +package com.woocommerce.android.ui.woopos.home.toolbar import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.animateColor @@ -50,14 +50,12 @@ import com.woocommerce.android.R import com.woocommerce.android.ui.woopos.common.composeui.WooPosPreview import com.woocommerce.android.ui.woopos.common.composeui.WooPosTheme import com.woocommerce.android.ui.woopos.common.composeui.toAdaptivePadding -import com.woocommerce.android.ui.woopos.root.WooPosRootScreenState.Menu.MenuItem -import com.woocommerce.android.ui.woopos.root.WooPosRootScreenState.WooPosCardReaderStatus @Composable fun WooPosFloatingToolbar( modifier: Modifier = Modifier, - state: State, - onUIEvent: (WooPosRootUIEvent) -> Unit, + state: State, + onUIEvent: (WooPosHomeToolbarUIEvent) -> Unit, ) { val cardReaderStatus = state.value.cardReaderStatus val menu = state.value.menu @@ -70,15 +68,15 @@ fun WooPosFloatingToolbar( .clickable( indication = null, interactionSource = remember { MutableInteractionSource() } - ) { onUIEvent(WooPosRootUIEvent.OnOutsideOfToolbarMenuClicked) }, - isVisible = menu is WooPosRootScreenState.Menu.Visible, + ) { onUIEvent(WooPosHomeToolbarUIEvent.OnOutsideOfToolbarMenuClicked) }, + isVisible = menu is WooPosHomeToolbarScreenState.Menu.Visible, ) ConstraintLayout(modifier = modifier) { val (toolbar, popupMenu) = createRefs() when (menu) { - is WooPosRootScreenState.Menu.Hidden -> { + is WooPosHomeToolbarScreenState.Menu.Hidden -> { Toolbar( modifier = Modifier.constrainAs(toolbar) { bottom.linkTo(parent.bottom) @@ -90,7 +88,7 @@ fun WooPosFloatingToolbar( ) } - is WooPosRootScreenState.Menu.Visible -> { + is WooPosHomeToolbarScreenState.Menu.Visible -> { Toolbar( modifier = Modifier.constrainAs(toolbar) { bottom.linkTo(parent.bottom) @@ -109,7 +107,7 @@ fun WooPosFloatingToolbar( }, menuItems = menu.items, onClick = { menuItem -> - onUIEvent(WooPosRootUIEvent.MenuItemClicked(menuItem)) + onUIEvent(WooPosHomeToolbarUIEvent.MenuItemClicked(menuItem)) } ) } @@ -137,7 +135,7 @@ private fun Toolbar( modifier: Modifier, menuCardDisabled: Boolean, cardReaderStatus: WooPosCardReaderStatus, - onUIEvent: (WooPosRootUIEvent) -> Unit + onUIEvent: (WooPosHomeToolbarUIEvent) -> Unit ) { ConstraintLayout(modifier = modifier) { val (menuCard, cardReaderStatusCard) = createRefs() @@ -152,7 +150,7 @@ private fun Toolbar( bottom.linkTo(parent.bottom) }, state = cardReaderStatus, - ) { onUIEvent(WooPosRootUIEvent.ConnectToAReaderClicked) } + ) { onUIEvent(WooPosHomeToolbarUIEvent.ConnectToAReaderClicked) } MenuButtonWithPopUpMenu( modifier = Modifier @@ -163,7 +161,7 @@ private fun Toolbar( height = Dimension.fillToConstraints }, menuCardDisabled = menuCardDisabled, - ) { onUIEvent(WooPosRootUIEvent.OnToolbarMenuClicked) } + ) { onUIEvent(WooPosHomeToolbarUIEvent.OnToolbarMenuClicked) } } } @@ -346,9 +344,9 @@ private fun Circle( fun PreviewWooPosFloatingToolbarStatusNotConnected() { val state = remember { mutableStateOf( - WooPosRootScreenState( + WooPosHomeToolbarScreenState( WooPosCardReaderStatus.NotConnected, - menu = WooPosRootScreenState.Menu.Hidden, + menu = WooPosHomeToolbarScreenState.Menu.Hidden, null ) ) @@ -361,9 +359,9 @@ fun PreviewWooPosFloatingToolbarStatusNotConnected() { fun PreviewWooPosFloatingToolbarStatusConnectedWithMenu() { val state = remember { mutableStateOf( - WooPosRootScreenState( + WooPosHomeToolbarScreenState( WooPosCardReaderStatus.Connected, - menu = WooPosRootScreenState.Menu.Visible( + menu = WooPosHomeToolbarScreenState.Menu.Visible( listOf( MenuItem( title = R.string.woopos_exit_confirmation_message, @@ -383,7 +381,7 @@ fun PreviewWooPosFloatingToolbarStatusConnectedWithMenu() { } @Composable -private fun Preview(state: MutableState) { +private fun Preview(state: MutableState) { WooPosTheme { Box( modifier = Modifier.fillMaxSize(), diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/root/WooPosRootScreenState.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeToolbarState.kt similarity index 58% rename from WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/root/WooPosRootScreenState.kt rename to WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeToolbarState.kt index b38977b7385..7bb8e4764f4 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/root/WooPosRootScreenState.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeToolbarState.kt @@ -1,26 +1,18 @@ -package com.woocommerce.android.ui.woopos.root +package com.woocommerce.android.ui.woopos.home.toolbar import androidx.annotation.DrawableRes import androidx.annotation.StringRes import com.woocommerce.android.R -data class WooPosRootScreenState( +data class WooPosHomeToolbarState( val cardReaderStatus: WooPosCardReaderStatus, val menu: Menu, - val exitConfirmationDialog: WooPosExitConfirmationDialog?, ) { sealed class WooPosCardReaderStatus(@StringRes val title: Int) { data object NotConnected : WooPosCardReaderStatus(title = R.string.woopos_reader_disconnected) data object Connected : WooPosCardReaderStatus(title = R.string.woopos_reader_connected) } - data object WooPosExitConfirmationDialog { - val title: Int = R.string.woopos_exit_confirmation_title - val message: Int = R.string.woopos_exit_confirmation_message - val confirmButton: Int = R.string.woopos_exit_confirmation_confirm_button - val dismissButton: Int = R.string.woopos_exit_confirmation_dismiss_button - } - sealed class Menu { data object Hidden : Menu() data class Visible(val items: List) : Menu() diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeToolbarUIEvent.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeToolbarUIEvent.kt new file mode 100644 index 00000000000..6555a324c3d --- /dev/null +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeToolbarUIEvent.kt @@ -0,0 +1,8 @@ +package com.woocommerce.android.ui.woopos.home.toolbar + +sealed class WooPosHomeToolbarUIEvent { + data object OnToolbarMenuClicked : WooPosHomeToolbarUIEvent() + data object OnOutsideOfToolbarMenuClicked : WooPosHomeToolbarUIEvent() + data object ConnectToAReaderClicked : WooPosHomeToolbarUIEvent() + data class MenuItemClicked(val menuItem: WooPosHomeToolbarState.Menu.MenuItem) : WooPosHomeToolbarUIEvent() +} diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeToolbarViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeToolbarViewModel.kt new file mode 100644 index 00000000000..6e7e45d8aeb --- /dev/null +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeToolbarViewModel.kt @@ -0,0 +1,104 @@ +package com.woocommerce.android.ui.woopos.home.toolbar + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.woocommerce.android.R +import com.woocommerce.android.cardreader.connection.CardReaderStatus +import com.woocommerce.android.cardreader.connection.CardReaderStatus.Connected +import com.woocommerce.android.cardreader.connection.CardReaderStatus.Connecting +import com.woocommerce.android.cardreader.connection.CardReaderStatus.NotConnected +import com.woocommerce.android.ui.woopos.cardreader.WooPosCardReaderFacade +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class WooPosHomeToolbarViewModel @Inject constructor( + private val cardReaderFacade: WooPosCardReaderFacade, +) : ViewModel() { + private val _state = MutableStateFlow( + WooPosHomeToolbarState( + cardReaderStatus = WooPosHomeToolbarState.WooPosCardReaderStatus.NotConnected, + menu = WooPosHomeToolbarState.Menu.Hidden, + ) + ) + val state: StateFlow = _state + + init { + viewModelScope.launch { + cardReaderFacade.readerStatus.collect { + _state.value = _state.value.copy( + cardReaderStatus = mapCardReaderStatusToUiState(it) + ) + } + } + } + + fun onUiEvent(event: WooPosHomeToolbarUIEvent) { + val currentState = _state.value + if (currentState.menu is WooPosHomeToolbarState.Menu.Visible && event !is WooPosHomeToolbarUIEvent.MenuItemClicked) { + hideMenu() + return + } + + when (event) { + is WooPosHomeToolbarUIEvent.OnToolbarMenuClicked -> { + _state.value = currentState.copy( + menu = WooPosHomeToolbarState.Menu.Visible(toolbarMenuItems) + ) + } + + WooPosHomeToolbarUIEvent.ConnectToAReaderClicked -> handleConnectToReaderButtonClicked() + + is WooPosHomeToolbarUIEvent.MenuItemClicked -> handleMenuItemClicked(event) + + is WooPosHomeToolbarUIEvent.OnOutsideOfToolbarMenuClicked -> { + // Do nothing as the menu is hidden already, but we need to pass the event here anyway + } + } + } + + private fun handleMenuItemClicked(event: WooPosHomeToolbarUIEvent.MenuItemClicked) { + hideMenu() + + when (event.menuItem.title) { + R.string.woopos_get_support_title -> TODO() + R.string.woopos_exit_confirmation_title -> + _state.value = _state.value.copy( + exitConfirmationDialog = WooPosHomeToolbarState.WooPosExitConfirmationDialog + ) + } + } + + private fun hideMenu() { + _state.value = _state.value.copy(menu = WooPosHomeToolbarState.Menu.Hidden) + } + + private fun handleConnectToReaderButtonClicked() { + if (_state.value.cardReaderStatus != WooPosHomeToolbarState.WooPosCardReaderStatus.Connected) { + cardReaderFacade.connectToReader() + } + } + + private fun mapCardReaderStatusToUiState(status: CardReaderStatus): WooPosHomeToolbarState.WooPosCardReaderStatus { + return when (status) { + is Connected -> WooPosHomeToolbarState.WooPosCardReaderStatus.Connected + is NotConnected, Connecting -> WooPosHomeToolbarState.WooPosCardReaderStatus.NotConnected + } + } + + private companion object { + val toolbarMenuItems = listOf( + WooPosHomeToolbarState.Menu.MenuItem( + title = R.string.woopos_get_support_title, + icon = R.drawable.woopos_ic_get_support, + ), + WooPosHomeToolbarState.Menu.MenuItem( + title = R.string.woopos_exit_confirmation_title, + icon = R.drawable.woopos_ic_exit_pos, + ), + ) + } +} diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/root/WooPosRootScreen.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/root/WooPosRootScreen.kt index 93b701da677..f046f0f78ca 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/root/WooPosRootScreen.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/root/WooPosRootScreen.kt @@ -4,98 +4,34 @@ import androidx.activity.ComponentActivity import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding import androidx.compose.material.MaterialTheme import androidx.compose.runtime.Composable -import androidx.compose.runtime.State -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.LocalContext -import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp -import androidx.hilt.navigation.compose.hiltViewModel import androidx.navigation.compose.rememberNavController import com.woocommerce.android.ui.woopos.common.composeui.WooPosPreview import com.woocommerce.android.ui.woopos.common.composeui.WooPosTheme -import com.woocommerce.android.ui.woopos.common.composeui.component.WooPosConfirmationDialog -import com.woocommerce.android.ui.woopos.common.composeui.toAdaptivePadding -import com.woocommerce.android.ui.woopos.root.navigation.WooPosNavigationEvent import com.woocommerce.android.ui.woopos.root.navigation.WooPosRootHost import com.woocommerce.android.ui.woopos.root.navigation.handleNavigationEvent @Composable fun WooPosRootScreen(modifier: Modifier = Modifier) { - val viewModel: WooPosRootViewModel = hiltViewModel() - WooPosRootScreen( - modifier = modifier, - state = viewModel.rootScreenState.collectAsState(), - onUIEvent = viewModel::onUiEvent, - ) -} - -@Composable -private fun WooPosRootScreen( - modifier: Modifier = Modifier, - state: State, - onUIEvent: (WooPosRootUIEvent) -> Unit -) { val rootController = rememberNavController() val activity = LocalContext.current as ComponentActivity - state.value.exitConfirmationDialog?.let { - WooPosConfirmationDialog( - title = stringResource(id = it.title), - message = stringResource(id = it.message), - confirmButtonText = stringResource(id = it.confirmButton), - dismissButtonText = stringResource(id = it.dismissButton), - onDismiss = { onUIEvent(WooPosRootUIEvent.ExitConfirmationDialogDismissed) }, - onConfirm = { - rootController.handleNavigationEvent( - WooPosNavigationEvent.ExitPosClicked, - activity, - onUIEvent - ) - } - ) - } - Box(modifier = modifier.background(MaterialTheme.colors.background)) { WooPosRootHost( modifier = Modifier.fillMaxSize(), rootController = rootController, onNavigationEvent = { event -> - rootController.handleNavigationEvent(event, activity, onUIEvent) + rootController.handleNavigationEvent(event, activity) } ) - WooPosFloatingToolbar( - modifier = Modifier - .padding(24.dp.toAdaptivePadding()) - .align(Alignment.BottomStart), - state, - onUIEvent, - ) } } @WooPosPreview @Composable fun PreviewWooPosRootScreen() { - val state = remember { - mutableStateOf( - WooPosRootScreenState( - WooPosRootScreenState.WooPosCardReaderStatus.NotConnected, - menu = WooPosRootScreenState.Menu.Hidden, - null, - ) - ) - } - WooPosTheme { - WooPosRootScreen( - state = state, - onUIEvent = {} - ) - } + WooPosTheme { WooPosRootScreen() } } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/root/WooPosRootUIEvent.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/root/WooPosRootUIEvent.kt deleted file mode 100644 index ab960fa09c6..00000000000 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/root/WooPosRootUIEvent.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.woocommerce.android.ui.woopos.root - -sealed class WooPosRootUIEvent { - data object OnToolbarMenuClicked : WooPosRootUIEvent() - data object OnOutsideOfToolbarMenuClicked : WooPosRootUIEvent() - data object ConnectToAReaderClicked : WooPosRootUIEvent() - data object ExitConfirmationDialogDismissed : WooPosRootUIEvent() - data object OnBackFromHomeClicked : WooPosRootUIEvent() - data class MenuItemClicked(val menuItem: WooPosRootScreenState.Menu.MenuItem) : WooPosRootUIEvent() -} diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/root/WooPosRootViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/root/WooPosRootViewModel.kt deleted file mode 100644 index 2bb268bf217..00000000000 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/root/WooPosRootViewModel.kt +++ /dev/null @@ -1,119 +0,0 @@ -package com.woocommerce.android.ui.woopos.root - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.woocommerce.android.R -import com.woocommerce.android.cardreader.connection.CardReaderStatus -import com.woocommerce.android.cardreader.connection.CardReaderStatus.Connected -import com.woocommerce.android.cardreader.connection.CardReaderStatus.Connecting -import com.woocommerce.android.cardreader.connection.CardReaderStatus.NotConnected -import com.woocommerce.android.ui.woopos.cardreader.WooPosCardReaderFacade -import com.woocommerce.android.ui.woopos.root.WooPosRootScreenState.Menu.MenuItem -import com.woocommerce.android.ui.woopos.root.WooPosRootUIEvent.ConnectToAReaderClicked -import com.woocommerce.android.ui.woopos.root.WooPosRootUIEvent.ExitConfirmationDialogDismissed -import com.woocommerce.android.ui.woopos.root.WooPosRootUIEvent.MenuItemClicked -import com.woocommerce.android.ui.woopos.root.WooPosRootUIEvent.OnBackFromHomeClicked -import com.woocommerce.android.ui.woopos.root.WooPosRootUIEvent.OnToolbarMenuClicked -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.launch -import javax.inject.Inject - -@HiltViewModel -class WooPosRootViewModel @Inject constructor( - private val cardReaderFacade: WooPosCardReaderFacade, -) : ViewModel() { - private val _rootScreenState = MutableStateFlow( - WooPosRootScreenState( - cardReaderStatus = WooPosRootScreenState.WooPosCardReaderStatus.NotConnected, - menu = WooPosRootScreenState.Menu.Hidden, - exitConfirmationDialog = null, - ) - ) - val rootScreenState: StateFlow = _rootScreenState - - init { - viewModelScope.launch { - cardReaderFacade.readerStatus.collect { - _rootScreenState.value = _rootScreenState.value.copy( - cardReaderStatus = mapCardReaderStatusToUiState(it) - ) - } - } - } - - fun onUiEvent(event: WooPosRootUIEvent) { - val currentState = _rootScreenState.value - if (currentState.menu is WooPosRootScreenState.Menu.Visible && event !is MenuItemClicked) { - hideMenu() - return - } - - when (event) { - is OnToolbarMenuClicked -> { - _rootScreenState.value = currentState.copy( - menu = WooPosRootScreenState.Menu.Visible(toolbarMenuItems) - ) - } - - ConnectToAReaderClicked -> handleConnectToReaderButtonClicked() - - ExitConfirmationDialogDismissed -> _rootScreenState.value = currentState.copy( - exitConfirmationDialog = null - ) - - OnBackFromHomeClicked -> _rootScreenState.value = currentState.copy( - exitConfirmationDialog = WooPosRootScreenState.WooPosExitConfirmationDialog - ) - - is MenuItemClicked -> handleMenuItemClicked(event) - - is WooPosRootUIEvent.OnOutsideOfToolbarMenuClicked -> { - // Do nothing as the menu is hidden already, but we need to pass the event here anyway - } - } - } - - private fun handleMenuItemClicked(event: MenuItemClicked) { - hideMenu() - - when (event.menuItem.title) { - R.string.woopos_get_support_title -> TODO() - R.string.woopos_exit_confirmation_title -> - _rootScreenState.value = _rootScreenState.value.copy( - exitConfirmationDialog = WooPosRootScreenState.WooPosExitConfirmationDialog - ) - } - } - - private fun hideMenu() { - _rootScreenState.value = _rootScreenState.value.copy(menu = WooPosRootScreenState.Menu.Hidden) - } - - private fun handleConnectToReaderButtonClicked() { - if (_rootScreenState.value.cardReaderStatus != WooPosRootScreenState.WooPosCardReaderStatus.Connected) { - cardReaderFacade.connectToReader() - } - } - - private fun mapCardReaderStatusToUiState(status: CardReaderStatus): WooPosRootScreenState.WooPosCardReaderStatus { - return when (status) { - is Connected -> WooPosRootScreenState.WooPosCardReaderStatus.Connected - is NotConnected, Connecting -> WooPosRootScreenState.WooPosCardReaderStatus.NotConnected - } - } - - private companion object { - val toolbarMenuItems = listOf( - MenuItem( - title = R.string.woopos_get_support_title, - icon = R.drawable.woopos_ic_get_support, - ), - MenuItem( - title = R.string.woopos_exit_confirmation_title, - icon = R.drawable.woopos_ic_exit_pos, - ), - ) - } -} diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/root/navigation/WooPosNavigationEvent.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/root/navigation/WooPosNavigationEvent.kt index 92986d30b3b..7c57b8c6838 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/root/navigation/WooPosNavigationEvent.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/root/navigation/WooPosNavigationEvent.kt @@ -2,7 +2,6 @@ package com.woocommerce.android.ui.woopos.root.navigation sealed class WooPosNavigationEvent { data object ExitPosClicked : WooPosNavigationEvent() - data object BackFromHomeClicked : WooPosNavigationEvent() data object BackFromSplashClicked : WooPosNavigationEvent() data object OpenHomeFromSplash : WooPosNavigationEvent() } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/root/navigation/WooPosNavigationEventHandler.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/root/navigation/WooPosNavigationEventHandler.kt index 4981841ba55..867b71b97f9 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/root/navigation/WooPosNavigationEventHandler.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/root/navigation/WooPosNavigationEventHandler.kt @@ -3,19 +3,15 @@ package com.woocommerce.android.ui.woopos.root.navigation import androidx.activity.ComponentActivity import androidx.navigation.NavHostController import com.woocommerce.android.ui.woopos.home.navigateToHomeScreen -import com.woocommerce.android.ui.woopos.root.WooPosRootUIEvent -import com.woocommerce.android.ui.woopos.root.WooPosRootUIEvent.OnBackFromHomeClicked fun NavHostController.handleNavigationEvent( event: WooPosNavigationEvent, activity: ComponentActivity, - onUIEvent: (WooPosRootUIEvent) -> Unit, ) { when (event) { is WooPosNavigationEvent.ExitPosClicked, is WooPosNavigationEvent.BackFromSplashClicked -> activity.finish() - is WooPosNavigationEvent.BackFromHomeClicked -> onUIEvent(OnBackFromHomeClicked) is WooPosNavigationEvent.OpenHomeFromSplash -> navigateToHomeScreen() } } diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeViewModelTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeViewModelTest.kt index ec85e8fe4cb..8ebe2feda34 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeViewModelTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeViewModelTest.kt @@ -1,6 +1,5 @@ package com.woocommerce.android.ui.woopos.home -import com.woocommerce.android.ui.woopos.home.WooPosHomeUIEvent.SystemBackClicked import com.woocommerce.android.viewmodel.BaseUnitTest import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.flowOf diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/root/WooPosRootViewModelTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/root/WooPosRootViewModelTest.kt index 9697655151f..bb929282f29 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/root/WooPosRootViewModelTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/root/WooPosRootViewModelTest.kt @@ -5,8 +5,6 @@ import com.woocommerce.android.cardreader.connection.CardReader import com.woocommerce.android.cardreader.connection.CardReaderStatus.Connected import com.woocommerce.android.cardreader.connection.CardReaderStatus.NotConnected import com.woocommerce.android.ui.woopos.cardreader.WooPosCardReaderFacade -import com.woocommerce.android.ui.woopos.root.WooPosRootScreenState.Menu.MenuItem -import com.woocommerce.android.ui.woopos.root.WooPosRootScreenState.WooPosCardReaderStatus import com.woocommerce.android.viewmodel.BaseUnitTest import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.drop From 2db6df3f3b984a94e6c09051676d948e3a9aa3c2 Mon Sep 17 00:00:00 2001 From: Andrey Date: Fri, 19 Jul 2024 16:04:19 +0200 Subject: [PATCH 02/12] Changes to accomodate VM states modifications --- .../WooPosHomeChildToParentCommunication.kt | 1 + .../ui/woopos/home/WooPosHomeViewModel.kt | 6 +++ .../woopos/home/toolbar/WooPosHomeToolbar.kt | 53 ++++++++++++------- .../toolbar/WooPosHomeToolbarViewModel.kt | 25 +++++---- 4 files changed, 56 insertions(+), 29 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeChildToParentCommunication.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeChildToParentCommunication.kt index 1fc093e7c06..b03713dd438 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeChildToParentCommunication.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeChildToParentCommunication.kt @@ -23,6 +23,7 @@ sealed class ChildToParentEvent { data class ItemClickedInProductSelector(val productId: Long) : ChildToParentEvent() data object NewTransactionClicked : ChildToParentEvent() data object OrderSuccessfullyPaid : ChildToParentEvent() + data object ExitPosClicked : ChildToParentEvent() sealed class CartStatusChanged : ChildToParentEvent() { data object Empty : CartStatusChanged() data object NotEmpty : CartStatusChanged() diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeViewModel.kt index 7ebf45520ea..002a9461fde 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeViewModel.kt @@ -111,6 +111,12 @@ class WooPosHomeViewModel @Inject constructor( } } } + + ChildToParentEvent.ExitPosClicked -> { + _state.value = _state.value.copy( + exitConfirmationDialog = WooPosExitConfirmationDialog + ) + } } } } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeToolbar.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeToolbar.kt index 03c5c8bb767..712ebdd6152 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeToolbar.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeToolbar.kt @@ -32,6 +32,7 @@ import androidx.compose.material.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.MutableState import androidx.compose.runtime.State +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember @@ -46,15 +47,29 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.constraintlayout.compose.ConstraintLayout import androidx.constraintlayout.compose.Dimension +import androidx.hilt.navigation.compose.hiltViewModel import com.woocommerce.android.R import com.woocommerce.android.ui.woopos.common.composeui.WooPosPreview import com.woocommerce.android.ui.woopos.common.composeui.WooPosTheme import com.woocommerce.android.ui.woopos.common.composeui.toAdaptivePadding +import com.woocommerce.android.ui.woopos.home.toolbar.WooPosHomeToolbarState.Menu +import com.woocommerce.android.ui.woopos.home.toolbar.WooPosHomeToolbarState.WooPosCardReaderStatus @Composable -fun WooPosFloatingToolbar( +private fun WooPosFloatingToolbar(modifier: Modifier = Modifier) { + val viewModel: WooPosHomeToolbarViewModel = hiltViewModel() + WooPosFloatingToolbar( + modifier = modifier, + state = viewModel.state.collectAsState(), + ) { uiEvent -> + viewModel.onUiEvent(uiEvent) + } +} + +@Composable +private fun WooPosFloatingToolbar( modifier: Modifier = Modifier, - state: State, + state: State, onUIEvent: (WooPosHomeToolbarUIEvent) -> Unit, ) { val cardReaderStatus = state.value.cardReaderStatus @@ -69,14 +84,14 @@ fun WooPosFloatingToolbar( indication = null, interactionSource = remember { MutableInteractionSource() } ) { onUIEvent(WooPosHomeToolbarUIEvent.OnOutsideOfToolbarMenuClicked) }, - isVisible = menu is WooPosHomeToolbarScreenState.Menu.Visible, + isVisible = menu is Menu.Visible, ) ConstraintLayout(modifier = modifier) { val (toolbar, popupMenu) = createRefs() when (menu) { - is WooPosHomeToolbarScreenState.Menu.Hidden -> { + is Menu.Hidden -> { Toolbar( modifier = Modifier.constrainAs(toolbar) { bottom.linkTo(parent.bottom) @@ -88,7 +103,7 @@ fun WooPosFloatingToolbar( ) } - is WooPosHomeToolbarScreenState.Menu.Visible -> { + is Menu.Visible -> { Toolbar( modifier = Modifier.constrainAs(toolbar) { bottom.linkTo(parent.bottom) @@ -206,8 +221,8 @@ private fun MenuButtonWithPopUpMenu( @Composable private fun PopUpMenu( modifier: Modifier, - menuItems: List, - onClick: (MenuItem) -> Unit + menuItems: List, + onClick: (Menu.MenuItem) -> Unit ) { Card( modifier = modifier.width(214.dp), @@ -225,8 +240,8 @@ private fun PopUpMenu( @Composable private fun PopUpMenuItem( - menuItem: MenuItem, - onClick: (MenuItem) -> Unit + menuItem: Menu.MenuItem, + onClick: (Menu.MenuItem) -> Unit ) { TextButton(onClick = { onClick(menuItem) }) { Spacer(modifier = Modifier.width(20.dp.toAdaptivePadding())) @@ -344,10 +359,9 @@ private fun Circle( fun PreviewWooPosFloatingToolbarStatusNotConnected() { val state = remember { mutableStateOf( - WooPosHomeToolbarScreenState( - WooPosCardReaderStatus.NotConnected, - menu = WooPosHomeToolbarScreenState.Menu.Hidden, - null + WooPosHomeToolbarState( + cardReaderStatus = WooPosCardReaderStatus.NotConnected, + menu = Menu.Hidden ) ) } @@ -359,21 +373,20 @@ fun PreviewWooPosFloatingToolbarStatusNotConnected() { fun PreviewWooPosFloatingToolbarStatusConnectedWithMenu() { val state = remember { mutableStateOf( - WooPosHomeToolbarScreenState( - WooPosCardReaderStatus.Connected, - menu = WooPosHomeToolbarScreenState.Menu.Visible( + WooPosHomeToolbarState( + cardReaderStatus = WooPosCardReaderStatus.Connected, + menu = Menu.Visible( listOf( - MenuItem( + Menu.MenuItem( title = R.string.woopos_exit_confirmation_message, icon = R.drawable.woopos_ic_exit_pos, ), - MenuItem( + Menu.MenuItem( title = R.string.woopos_get_support_title, icon = R.drawable.woopos_ic_get_support, ) ) ), - null ) ) } @@ -381,7 +394,7 @@ fun PreviewWooPosFloatingToolbarStatusConnectedWithMenu() { } @Composable -private fun Preview(state: MutableState) { +private fun Preview(state: MutableState) { WooPosTheme { Box( modifier = Modifier.fillMaxSize(), diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeToolbarViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeToolbarViewModel.kt index 6e7e45d8aeb..4af687a4628 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeToolbarViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeToolbarViewModel.kt @@ -8,6 +8,12 @@ import com.woocommerce.android.cardreader.connection.CardReaderStatus.Connected import com.woocommerce.android.cardreader.connection.CardReaderStatus.Connecting import com.woocommerce.android.cardreader.connection.CardReaderStatus.NotConnected import com.woocommerce.android.ui.woopos.cardreader.WooPosCardReaderFacade +import com.woocommerce.android.ui.woopos.home.ChildToParentEvent +import com.woocommerce.android.ui.woopos.home.WooPosChildrenToParentEventSender +import com.woocommerce.android.ui.woopos.home.toolbar.WooPosHomeToolbarUIEvent.ConnectToAReaderClicked +import com.woocommerce.android.ui.woopos.home.toolbar.WooPosHomeToolbarUIEvent.MenuItemClicked +import com.woocommerce.android.ui.woopos.home.toolbar.WooPosHomeToolbarUIEvent.OnOutsideOfToolbarMenuClicked +import com.woocommerce.android.ui.woopos.home.toolbar.WooPosHomeToolbarUIEvent.OnToolbarMenuClicked import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -17,6 +23,7 @@ import javax.inject.Inject @HiltViewModel class WooPosHomeToolbarViewModel @Inject constructor( private val cardReaderFacade: WooPosCardReaderFacade, + private val childrenToParentEventSender: WooPosChildrenToParentEventSender, ) : ViewModel() { private val _state = MutableStateFlow( WooPosHomeToolbarState( @@ -38,37 +45,37 @@ class WooPosHomeToolbarViewModel @Inject constructor( fun onUiEvent(event: WooPosHomeToolbarUIEvent) { val currentState = _state.value - if (currentState.menu is WooPosHomeToolbarState.Menu.Visible && event !is WooPosHomeToolbarUIEvent.MenuItemClicked) { + if (currentState.menu is WooPosHomeToolbarState.Menu.Visible && event !is MenuItemClicked) { hideMenu() return } when (event) { - is WooPosHomeToolbarUIEvent.OnToolbarMenuClicked -> { + is OnToolbarMenuClicked -> { _state.value = currentState.copy( menu = WooPosHomeToolbarState.Menu.Visible(toolbarMenuItems) ) } - WooPosHomeToolbarUIEvent.ConnectToAReaderClicked -> handleConnectToReaderButtonClicked() + ConnectToAReaderClicked -> handleConnectToReaderButtonClicked() - is WooPosHomeToolbarUIEvent.MenuItemClicked -> handleMenuItemClicked(event) + is MenuItemClicked -> handleMenuItemClicked(event) - is WooPosHomeToolbarUIEvent.OnOutsideOfToolbarMenuClicked -> { + is OnOutsideOfToolbarMenuClicked -> { // Do nothing as the menu is hidden already, but we need to pass the event here anyway } } } - private fun handleMenuItemClicked(event: WooPosHomeToolbarUIEvent.MenuItemClicked) { + private fun handleMenuItemClicked(event: MenuItemClicked) { hideMenu() when (event.menuItem.title) { R.string.woopos_get_support_title -> TODO() R.string.woopos_exit_confirmation_title -> - _state.value = _state.value.copy( - exitConfirmationDialog = WooPosHomeToolbarState.WooPosExitConfirmationDialog - ) + viewModelScope.launch { + childrenToParentEventSender.sendToParent(ChildToParentEvent.ExitPosClicked) + } } } From 2ca5e70e46950ee00ee7f26a25514aac4fbc0df9 Mon Sep 17 00:00:00 2001 From: Andrey Date: Fri, 19 Jul 2024 16:12:13 +0200 Subject: [PATCH 03/12] Placed detached toolbar to the home screen --- .../ui/woopos/home/WooPosHomeScreen.kt | 83 ++++++++++++------- .../woopos/home/toolbar/WooPosHomeToolbar.kt | 2 +- 2 files changed, 53 insertions(+), 32 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeScreen.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeScreen.kt index 16aeeb14dfd..2ecb281488e 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeScreen.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeScreen.kt @@ -11,6 +11,7 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width @@ -21,6 +22,7 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue 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.res.stringResource @@ -36,6 +38,8 @@ import com.woocommerce.android.ui.woopos.home.cart.WooPosCartScreen import com.woocommerce.android.ui.woopos.home.cart.WooPosCartScreenProductsPreview import com.woocommerce.android.ui.woopos.home.products.WooPosProductsScreen import com.woocommerce.android.ui.woopos.home.products.WooPosProductsScreenPreview +import com.woocommerce.android.ui.woopos.home.toolbar.PreviewWooPosFloatingToolbarStatusConnectedWithMenu +import com.woocommerce.android.ui.woopos.home.toolbar.WooPosFloatingToolbar import com.woocommerce.android.ui.woopos.home.totals.WooPosTotalsScreen import com.woocommerce.android.ui.woopos.home.totals.WooPosTotalsScreenPreview import com.woocommerce.android.ui.woopos.root.navigation.WooPosNavigationEvent @@ -130,46 +134,54 @@ private fun WooPosHomeScreen( totalsWidthDp: Dp, totalsStartPaddingDp: Dp, ) { - Row( - modifier = Modifier - .horizontalScroll(scrollState, enabled = false) - .fillMaxWidth(), - ) { - Row(modifier = Modifier.width(productsWidthDp)) { - WooPosHomeScreenProducts( - modifier = Modifier - .width(productsWidthDp) - ) - } + Box(modifier = Modifier.fillMaxSize()) { Row( modifier = Modifier - .width(cartWidthDp) - .background(MaterialTheme.colors.surface) + .horizontalScroll(scrollState, enabled = false) + .fillMaxWidth(), ) { - Box { - WooPosHomeScreenCart( + Row(modifier = Modifier.width(productsWidthDp)) { + WooPosHomeScreenProducts( modifier = Modifier - .width(cartWidthDp) + .width(productsWidthDp) ) - Box( + } + Row( + modifier = Modifier + .width(cartWidthDp) + .background(MaterialTheme.colors.surface) + ) { + Box { + WooPosHomeScreenCart( + modifier = Modifier + .width(cartWidthDp) + ) + Box( + modifier = Modifier + .width(cartWidthDp) + .fillMaxHeight() + .background( + color = MaterialTheme.colors.background.copy(alpha = cartOverlayIntensity), + ) + ) + } + } + Row(modifier = Modifier.width(totalsWidthDp)) { + Spacer(modifier = Modifier.width(totalsStartPaddingDp)) + WooPosHomeScreenTotals( modifier = Modifier - .width(cartWidthDp) - .fillMaxHeight() - .background( - color = MaterialTheme.colors.background.copy(alpha = cartOverlayIntensity), - ) + .width(totalsWidthDp - 24.dp.toAdaptivePadding() - totalsStartPaddingDp) + .padding(vertical = 24.dp.toAdaptivePadding()) ) + Spacer(modifier = Modifier.width(24.dp.toAdaptivePadding())) } } - Row(modifier = Modifier.width(totalsWidthDp)) { - Spacer(modifier = Modifier.width(totalsStartPaddingDp)) - WooPosHomeScreenTotals( - modifier = Modifier - .width(totalsWidthDp - 24.dp.toAdaptivePadding() - totalsStartPaddingDp) - .padding(vertical = 24.dp.toAdaptivePadding()) - ) - Spacer(modifier = Modifier.width(24.dp.toAdaptivePadding())) - } + + WooPosHomeScreenToolbar( + modifier = Modifier + .padding(24.dp.toAdaptivePadding()) + .align(Alignment.BottomStart), + ) } } @@ -200,6 +212,15 @@ private fun WooPosHomeScreenTotals(modifier: Modifier) { } } +@Composable +private fun WooPosHomeScreenToolbar(modifier: Modifier) { + if (isPreviewMode()) { + PreviewWooPosFloatingToolbarStatusConnectedWithMenu() + } else { + WooPosFloatingToolbar(modifier = modifier) + } +} + @Composable private fun buildScrollStateForNavigationBetweenState(state: WooPosHomeState.ScreenPositionState): ScrollState { val scrollState = rememberScrollState() diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeToolbar.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeToolbar.kt index 712ebdd6152..e085a237d4f 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeToolbar.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeToolbar.kt @@ -56,7 +56,7 @@ import com.woocommerce.android.ui.woopos.home.toolbar.WooPosHomeToolbarState.Men import com.woocommerce.android.ui.woopos.home.toolbar.WooPosHomeToolbarState.WooPosCardReaderStatus @Composable -private fun WooPosFloatingToolbar(modifier: Modifier = Modifier) { +fun WooPosFloatingToolbar(modifier: Modifier = Modifier) { val viewModel: WooPosHomeToolbarViewModel = hiltViewModel() WooPosFloatingToolbar( modifier = modifier, From 412468d9083f655146c71a72538f73dd76f866bd Mon Sep 17 00:00:00 2001 From: Andrey Date: Fri, 19 Jul 2024 16:16:53 +0200 Subject: [PATCH 04/12] Fixed formatting --- .../woocommerce/android/ui/woopos/home/WooPosHomeViewModel.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeViewModel.kt index 002a9461fde..f14c3a3da17 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeViewModel.kt @@ -89,7 +89,6 @@ class WooPosHomeViewModel @Inject constructor( _state.value = _state.value.copy( screenPositionState = WooPosHomeState.ScreenPositionState.Checkout.Paid ) - } is ChildToParentEvent.CartStatusChanged -> { From b94951a1d7f77ffbd7a4c8a8ad5cf955485b361d Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 22 Jul 2024 10:59:03 +0200 Subject: [PATCH 05/12] Handle confirmation dialog dismiss action --- .../woocommerce/android/ui/woopos/home/WooPosHomeViewModel.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeViewModel.kt index f14c3a3da17..7bf89c98432 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeViewModel.kt @@ -51,7 +51,9 @@ class WooPosHomeViewModel @Inject constructor( } } - else -> {} + WooPosHomeUIEvent.ExitConfirmationDialogDismissed -> { + _state.value = _state.value.copy(exitConfirmationDialog = null) + } } } From ccd2e5b6285bf99bd7c07d444a746403b7d613c2 Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 22 Jul 2024 11:04:30 +0200 Subject: [PATCH 06/12] Fixed WooPosHomeViewModelTest --- .../ui/woopos/home/WooPosHomeViewModelTest.kt | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeViewModelTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeViewModelTest.kt index 0136b02e110..f83622fdde0 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeViewModelTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeViewModelTest.kt @@ -6,18 +6,14 @@ import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest import org.junit.Rule -import org.junit.runner.RunWith -import org.mockito.junit.MockitoJUnitRunner import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.whenever import kotlin.test.Test -import kotlin.test.assertFalse +import org.assertj.core.api.Assertions.assertThat @ExperimentalCoroutinesApi -@RunWith(MockitoJUnitRunner::class) class WooPosHomeViewModelTest { - @Rule @JvmField val coroutinesTestRule = WooPosCoroutineTestRule() @@ -35,24 +31,26 @@ class WooPosHomeViewModelTest { val viewModel = createViewModel() // WHEN - viewModel.onUIEvent(SystemBackClicked) + viewModel.onUIEvent(WooPosHomeUIEvent.SystemBackClicked) // THEN verify(parentToChildrenEventSender).sendToChildren(ParentToChildrenEvent.BackFromCheckoutToCartClicked) + assertThat(viewModel.state.value.screenPositionState) + .isEqualTo(WooPosHomeState.ScreenPositionState.Cart.NotEmpty) } @Test - fun `given state is Cart, when SystemBackClicked passed, then should propagate the event down`() = runTest { + fun `given state is Cart, when SystemBackClicked passed, then should show exit confirmation dialog`() = runTest { // GIVEN val eventsFlow = MutableSharedFlow() whenever(childrenToParentEventReceiver.events).thenReturn(eventsFlow) val viewModel = createViewModel() // WHEN - val result = viewModel.onUIEvent(SystemBackClicked) + viewModel.onUIEvent(WooPosHomeUIEvent.SystemBackClicked) // THEN - assertFalse(result) + assertThat(viewModel.state.value.exitConfirmationDialog).isEqualTo(WooPosExitConfirmationDialog) } @Test @@ -64,10 +62,12 @@ class WooPosHomeViewModelTest { val viewModel = createViewModel() // WHEN - viewModel.onUIEvent(SystemBackClicked) + viewModel.onUIEvent(WooPosHomeUIEvent.SystemBackClicked) // THEN verify(parentToChildrenEventSender).sendToChildren(ParentToChildrenEvent.OrderSuccessfullyPaid) + assertThat(viewModel.state.value.screenPositionState) + .isEqualTo(WooPosHomeState.ScreenPositionState.Cart.Empty) } private fun createViewModel() = WooPosHomeViewModel( From 40d270883e229f34f7db32bcd0424356bd1d1981 Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 22 Jul 2024 11:07:19 +0200 Subject: [PATCH 07/12] Removed WooPosRootViewModelTest.kt as the VM doesn't exist anymore --- .../ui/woopos/root/WooPosRootViewModelTest.kt | 255 ------------------ 1 file changed, 255 deletions(-) delete mode 100644 WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/root/WooPosRootViewModelTest.kt diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/root/WooPosRootViewModelTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/root/WooPosRootViewModelTest.kt deleted file mode 100644 index 32bc4ce21ab..00000000000 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/root/WooPosRootViewModelTest.kt +++ /dev/null @@ -1,255 +0,0 @@ -package com.woocommerce.android.ui.woopos.root - -import com.woocommerce.android.R -import com.woocommerce.android.cardreader.connection.CardReader -import com.woocommerce.android.cardreader.connection.CardReaderStatus.Connected -import com.woocommerce.android.cardreader.connection.CardReaderStatus.NotConnected -import com.woocommerce.android.ui.woopos.cardreader.WooPosCardReaderFacade -import com.woocommerce.android.ui.woopos.util.WooPosCoroutineTestRule -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.drop -import kotlinx.coroutines.flow.flow -import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.launch -import kotlinx.coroutines.test.runTest -import org.assertj.core.api.Assertions.assertThat -import org.junit.Rule -import org.mockito.kotlin.mock -import org.mockito.kotlin.never -import org.mockito.kotlin.verify -import org.mockito.kotlin.whenever -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertNotEquals - -@ExperimentalCoroutinesApi -class WooPosRootViewModelTest { - private val cardReaderFacade: WooPosCardReaderFacade = mock { - onBlocking { readerStatus }.thenReturn(flowOf(NotConnected())) - } - - @Rule - @JvmField - val coroutinesTestRule = WooPosCoroutineTestRule() - - @Test - fun `given reader disconnected, when status button clicked, then should connect`() = runTest { - whenever(cardReaderFacade.readerStatus).thenReturn(flowOf(NotConnected())) - val sut = createSut() - assertNotEquals(WooPosCardReaderStatus.Connected, sut.rootScreenState.value.cardReaderStatus) - - sut.onUiEvent(WooPosRootUIEvent.ConnectToAReaderClicked) - - verify(cardReaderFacade).connectToReader() - } - - @Test - fun `given reader connected, when status button clicked, then should not connect`() = runTest { - whenever(cardReaderFacade.readerStatus).thenReturn(flowOf(NotConnected())) - val cardReader: CardReader = mock() - whenever(cardReaderFacade.readerStatus).thenReturn(flow { emit(Connected(cardReader)) }) - val sut = createSut() - val job = launch { - sut.rootScreenState.drop(1).collect { - assertEquals(WooPosCardReaderStatus.Connected, it.cardReaderStatus) - } - } - - sut.onUiEvent(WooPosRootUIEvent.ConnectToAReaderClicked) - - verify(cardReaderFacade, never()).connectToReader() - job.cancel() - } - - @Test - fun `when exit confirmation dialog dismissed, then dialog should be null`() = runTest { - // GIVEN - whenever(cardReaderFacade.readerStatus).thenReturn(flowOf(NotConnected())) - val sut = createSut() - sut.onUiEvent(WooPosRootUIEvent.ExitConfirmationDialogDismissed) - - // THEN - assertThat(sut.rootScreenState.value.exitConfirmationDialog).isNull() - } - - @Test - fun `when exit confirmation dialog clicked, then dialog should be shown`() = runTest { - // GIVEN - whenever(cardReaderFacade.readerStatus).thenReturn(flowOf(NotConnected())) - val sut = createSut() - - // WHEN - sut.onUiEvent(WooPosRootUIEvent.OnBackFromHomeClicked) - - // THEN - assertThat(sut.rootScreenState.value.exitConfirmationDialog).isEqualTo( - WooPosRootScreenState.WooPosExitConfirmationDialog - ) - assertThat(sut.rootScreenState.value.exitConfirmationDialog?.confirmButton).isEqualTo( - R.string.woopos_exit_confirmation_confirm_button - ) - assertThat(sut.rootScreenState.value.exitConfirmationDialog?.dismissButton).isEqualTo( - R.string.woopos_exit_confirmation_dismiss_button - ) - assertThat(sut.rootScreenState.value.exitConfirmationDialog?.message).isEqualTo( - R.string.woopos_exit_confirmation_message - ) - assertThat(sut.rootScreenState.value.exitConfirmationDialog?.title).isEqualTo( - R.string.woopos_exit_confirmation_title - ) - } - - @Test - fun `given OnBackFromHomeClicked, should update exit confirmation dialog`() { - // GIVEN - whenever(cardReaderFacade.readerStatus).thenReturn(flowOf(NotConnected())) - val sut = createSut() - - // WHEN - sut.onUiEvent(WooPosRootUIEvent.OnBackFromHomeClicked) - - // THEN - assertThat(sut.rootScreenState.value.exitConfirmationDialog).isEqualTo( - WooPosRootScreenState.WooPosExitConfirmationDialog - ) - } - - @Test - fun `given reader status as not connected, should update state with not connected status`() = runTest { - // GIVEN - whenever(cardReaderFacade.readerStatus).thenReturn(flowOf(NotConnected())) - val notConnectedStatus = NotConnected() - - whenever(cardReaderFacade.readerStatus).thenReturn(flowOf(notConnectedStatus)) - - val sut = createSut() - val job = launch { - sut.rootScreenState.drop(1).collect { - val state = it.cardReaderStatus as WooPosCardReaderStatus.NotConnected - assertThat(state.title).isEqualTo(R.string.woopos_reader_disconnected) - } - } - - job.cancel() - } - - @Test - fun `given reader status as connected, should update state with connected status`() = runTest { - // GIVEN - val cardReader: CardReader = mock() - val connectedStatus = Connected(cardReader) - - whenever(cardReaderFacade.readerStatus).thenReturn(flowOf(connectedStatus)) - - val sut = createSut() - val job = launch { - sut.rootScreenState.drop(1).collect { - assertEquals(WooPosCardReaderStatus.Connected, it.cardReaderStatus) - } - } - - job.cancel() - } - - @Test - fun `when toolbar menu clicked, should show menu`() = runTest { - // GIVEN - val sut = createSut() - - // WHEN - sut.onUiEvent(WooPosRootUIEvent.OnToolbarMenuClicked) - - // THEN - assertThat(sut.rootScreenState.value.menu).isEqualTo( - WooPosRootScreenState.Menu.Visible( - listOf( - MenuItem( - title = R.string.woopos_get_support_title, - icon = R.drawable.woopos_ic_get_support, - ), - MenuItem( - title = R.string.woopos_exit_confirmation_title, - icon = R.drawable.woopos_ic_exit_pos, - ), - ) - ) - ) - } - - @Test - fun `when menu item clicked, should handle accordingly`() = runTest { - // GIVEN - val sut = createSut() - - // WHEN - val menuItem = MenuItem( - title = R.string.woopos_exit_confirmation_title, - icon = R.drawable.woopos_ic_exit_pos - ) - sut.onUiEvent(WooPosRootUIEvent.MenuItemClicked(menuItem)) - - // THEN - assertThat(sut.rootScreenState.value.exitConfirmationDialog).isEqualTo( - WooPosRootScreenState.WooPosExitConfirmationDialog - ) - assertThat(sut.rootScreenState.value.menu).isEqualTo(WooPosRootScreenState.Menu.Hidden) - } - - @Test - fun `when OnOutsideOfToolbarMenuClicked, should hide menu if visible`() = runTest { - // GIVEN - val sut = createSut() - sut.onUiEvent(WooPosRootUIEvent.OnToolbarMenuClicked) - assertThat(sut.rootScreenState.value.menu).isEqualTo( - WooPosRootScreenState.Menu.Visible( - listOf( - MenuItem( - title = R.string.woopos_get_support_title, - icon = R.drawable.woopos_ic_get_support, - ), - MenuItem( - title = R.string.woopos_exit_confirmation_title, - icon = R.drawable.woopos_ic_exit_pos, - ), - ) - ) - ) - - // WHEN - sut.onUiEvent(WooPosRootUIEvent.OnOutsideOfToolbarMenuClicked) - - // THEN - assertThat(sut.rootScreenState.value.menu).isEqualTo(WooPosRootScreenState.Menu.Hidden) - } - - @Test - fun `when OnOutsideOfToolbarMenuClicked, should do nothing if menu already hidden`() = runTest { - // GIVEN - val sut = createSut() - assertThat(sut.rootScreenState.value.menu).isEqualTo(WooPosRootScreenState.Menu.Hidden) - - // WHEN - sut.onUiEvent(WooPosRootUIEvent.OnOutsideOfToolbarMenuClicked) - - // THEN - assertThat(sut.rootScreenState.value.menu).isEqualTo(WooPosRootScreenState.Menu.Hidden) - } - - @Test - fun `when ExitConfirmationDialogDismissed, should set exitConfirmationDialog to null`() = runTest { - // GIVEN - val sut = createSut() - sut.onUiEvent(WooPosRootUIEvent.OnBackFromHomeClicked) - assertThat(sut.rootScreenState.value.exitConfirmationDialog).isEqualTo( - WooPosRootScreenState.WooPosExitConfirmationDialog - ) - - // WHEN - sut.onUiEvent(WooPosRootUIEvent.ExitConfirmationDialogDismissed) - - // THEN - assertThat(sut.rootScreenState.value.exitConfirmationDialog).isNull() - } - - private fun createSut() = WooPosRootViewModel(cardReaderFacade) -} From a1e567a013b6f11f19cb9f99a90add73a4d76a10 Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 22 Jul 2024 11:07:29 +0200 Subject: [PATCH 08/12] Some missing tests to WooPosHomeViewModelTest --- .../ui/woopos/home/WooPosHomeViewModelTest.kt | 48 ++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeViewModelTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeViewModelTest.kt index f83622fdde0..afad87ff8f3 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeViewModelTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/WooPosHomeViewModelTest.kt @@ -5,12 +5,12 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest +import org.assertj.core.api.Assertions.assertThat import org.junit.Rule import org.mockito.kotlin.mock import org.mockito.kotlin.verify import org.mockito.kotlin.whenever import kotlin.test.Test -import org.assertj.core.api.Assertions.assertThat @ExperimentalCoroutinesApi class WooPosHomeViewModelTest { @@ -70,6 +70,52 @@ class WooPosHomeViewModelTest { .isEqualTo(WooPosHomeState.ScreenPositionState.Cart.Empty) } + @Test + fun `given state is Checkout NotPaid, when ExitConfirmationDialogDismissed passed, then exit confirmation dialog should be dismissed`() = runTest { + // GIVEN + whenever(childrenToParentEventReceiver.events).thenReturn( + flowOf(ChildToParentEvent.CheckoutClicked(emptyList())) + ) + val viewModel = createViewModel() + + // WHEN + viewModel.onUIEvent(WooPosHomeUIEvent.ExitConfirmationDialogDismissed) + + // THEN + assertThat(viewModel.state.value.exitConfirmationDialog).isNull() + } + + @Test + fun `given state is Cart Empty, when ExitPosClicked passed, then exit confirmation dialog should be shown`() = runTest { + // GIVEN + val eventsFlow = MutableSharedFlow() + whenever(childrenToParentEventReceiver.events).thenReturn(eventsFlow) + val viewModel = createViewModel() + + // WHEN + eventsFlow.emit(ChildToParentEvent.ExitPosClicked) + + // THEN + assertThat(viewModel.state.value.exitConfirmationDialog) + .isEqualTo(WooPosExitConfirmationDialog) + } + + @Test + fun `given state is Cart NotEmpty, when ExitPosClicked passed, then exit confirmation dialog should be shown`() = runTest { + // GIVEN + val eventsFlow = MutableSharedFlow() + whenever(childrenToParentEventReceiver.events).thenReturn(eventsFlow) + val viewModel = createViewModel() + + // WHEN + eventsFlow.emit(ChildToParentEvent.CartStatusChanged.NotEmpty) + eventsFlow.emit(ChildToParentEvent.ExitPosClicked) + + // THEN + assertThat(viewModel.state.value.exitConfirmationDialog) + .isEqualTo(WooPosExitConfirmationDialog) + } + private fun createViewModel() = WooPosHomeViewModel( childrenToParentEventReceiver, parentToChildrenEventSender From c90096ecaf5272afc83e5f201a5de3c9605707de Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 22 Jul 2024 11:13:49 +0200 Subject: [PATCH 09/12] Renamed Toolbar related classes --- .../home/toolbar/WooPosHomeToolbarUIEvent.kt | 8 ---- ...{WooPosHomeToolbar.kt => WooPosToolbar.kt} | 26 ++++++------- ...eToolbarState.kt => WooPosToolbarState.kt} | 2 +- .../home/toolbar/WooPosToolbarUIEvent.kt | 8 ++++ ...ViewModel.kt => WooPosToolbarViewModel.kt} | 38 +++++++++---------- 5 files changed, 41 insertions(+), 41 deletions(-) delete mode 100644 WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeToolbarUIEvent.kt rename WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/{WooPosHomeToolbar.kt => WooPosToolbar.kt} (93%) rename WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/{WooPosHomeToolbarState.kt => WooPosToolbarState.kt} (95%) create mode 100644 WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosToolbarUIEvent.kt rename WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/{WooPosHomeToolbarViewModel.kt => WooPosToolbarViewModel.kt} (67%) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeToolbarUIEvent.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeToolbarUIEvent.kt deleted file mode 100644 index 6555a324c3d..00000000000 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeToolbarUIEvent.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.woocommerce.android.ui.woopos.home.toolbar - -sealed class WooPosHomeToolbarUIEvent { - data object OnToolbarMenuClicked : WooPosHomeToolbarUIEvent() - data object OnOutsideOfToolbarMenuClicked : WooPosHomeToolbarUIEvent() - data object ConnectToAReaderClicked : WooPosHomeToolbarUIEvent() - data class MenuItemClicked(val menuItem: WooPosHomeToolbarState.Menu.MenuItem) : WooPosHomeToolbarUIEvent() -} diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeToolbar.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosToolbar.kt similarity index 93% rename from WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeToolbar.kt rename to WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosToolbar.kt index e085a237d4f..6bf507260ae 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeToolbar.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosToolbar.kt @@ -52,12 +52,12 @@ import com.woocommerce.android.R import com.woocommerce.android.ui.woopos.common.composeui.WooPosPreview import com.woocommerce.android.ui.woopos.common.composeui.WooPosTheme import com.woocommerce.android.ui.woopos.common.composeui.toAdaptivePadding -import com.woocommerce.android.ui.woopos.home.toolbar.WooPosHomeToolbarState.Menu -import com.woocommerce.android.ui.woopos.home.toolbar.WooPosHomeToolbarState.WooPosCardReaderStatus +import com.woocommerce.android.ui.woopos.home.toolbar.WooPosToolbarState.Menu +import com.woocommerce.android.ui.woopos.home.toolbar.WooPosToolbarState.WooPosCardReaderStatus @Composable fun WooPosFloatingToolbar(modifier: Modifier = Modifier) { - val viewModel: WooPosHomeToolbarViewModel = hiltViewModel() + val viewModel: WooPosToolbarViewModel = hiltViewModel() WooPosFloatingToolbar( modifier = modifier, state = viewModel.state.collectAsState(), @@ -69,8 +69,8 @@ fun WooPosFloatingToolbar(modifier: Modifier = Modifier) { @Composable private fun WooPosFloatingToolbar( modifier: Modifier = Modifier, - state: State, - onUIEvent: (WooPosHomeToolbarUIEvent) -> Unit, + state: State, + onUIEvent: (WooPosToolbarUIEvent) -> Unit, ) { val cardReaderStatus = state.value.cardReaderStatus val menu = state.value.menu @@ -83,7 +83,7 @@ private fun WooPosFloatingToolbar( .clickable( indication = null, interactionSource = remember { MutableInteractionSource() } - ) { onUIEvent(WooPosHomeToolbarUIEvent.OnOutsideOfToolbarMenuClicked) }, + ) { onUIEvent(WooPosToolbarUIEvent.OnOutsideOfToolbarMenuClicked) }, isVisible = menu is Menu.Visible, ) @@ -122,7 +122,7 @@ private fun WooPosFloatingToolbar( }, menuItems = menu.items, onClick = { menuItem -> - onUIEvent(WooPosHomeToolbarUIEvent.MenuItemClicked(menuItem)) + onUIEvent(WooPosToolbarUIEvent.MenuItemClicked(menuItem)) } ) } @@ -150,7 +150,7 @@ private fun Toolbar( modifier: Modifier, menuCardDisabled: Boolean, cardReaderStatus: WooPosCardReaderStatus, - onUIEvent: (WooPosHomeToolbarUIEvent) -> Unit + onUIEvent: (WooPosToolbarUIEvent) -> Unit ) { ConstraintLayout(modifier = modifier) { val (menuCard, cardReaderStatusCard) = createRefs() @@ -165,7 +165,7 @@ private fun Toolbar( bottom.linkTo(parent.bottom) }, state = cardReaderStatus, - ) { onUIEvent(WooPosHomeToolbarUIEvent.ConnectToAReaderClicked) } + ) { onUIEvent(WooPosToolbarUIEvent.ConnectToAReaderClicked) } MenuButtonWithPopUpMenu( modifier = Modifier @@ -176,7 +176,7 @@ private fun Toolbar( height = Dimension.fillToConstraints }, menuCardDisabled = menuCardDisabled, - ) { onUIEvent(WooPosHomeToolbarUIEvent.OnToolbarMenuClicked) } + ) { onUIEvent(WooPosToolbarUIEvent.OnToolbarMenuClicked) } } } @@ -359,7 +359,7 @@ private fun Circle( fun PreviewWooPosFloatingToolbarStatusNotConnected() { val state = remember { mutableStateOf( - WooPosHomeToolbarState( + WooPosToolbarState( cardReaderStatus = WooPosCardReaderStatus.NotConnected, menu = Menu.Hidden ) @@ -373,7 +373,7 @@ fun PreviewWooPosFloatingToolbarStatusNotConnected() { fun PreviewWooPosFloatingToolbarStatusConnectedWithMenu() { val state = remember { mutableStateOf( - WooPosHomeToolbarState( + WooPosToolbarState( cardReaderStatus = WooPosCardReaderStatus.Connected, menu = Menu.Visible( listOf( @@ -394,7 +394,7 @@ fun PreviewWooPosFloatingToolbarStatusConnectedWithMenu() { } @Composable -private fun Preview(state: MutableState) { +private fun Preview(state: MutableState) { WooPosTheme { Box( modifier = Modifier.fillMaxSize(), diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeToolbarState.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosToolbarState.kt similarity index 95% rename from WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeToolbarState.kt rename to WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosToolbarState.kt index 7bb8e4764f4..dd3ec45ab82 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeToolbarState.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosToolbarState.kt @@ -4,7 +4,7 @@ import androidx.annotation.DrawableRes import androidx.annotation.StringRes import com.woocommerce.android.R -data class WooPosHomeToolbarState( +data class WooPosToolbarState( val cardReaderStatus: WooPosCardReaderStatus, val menu: Menu, ) { diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosToolbarUIEvent.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosToolbarUIEvent.kt new file mode 100644 index 00000000000..7085daada3e --- /dev/null +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosToolbarUIEvent.kt @@ -0,0 +1,8 @@ +package com.woocommerce.android.ui.woopos.home.toolbar + +sealed class WooPosToolbarUIEvent { + data object OnToolbarMenuClicked : WooPosToolbarUIEvent() + data object OnOutsideOfToolbarMenuClicked : WooPosToolbarUIEvent() + data object ConnectToAReaderClicked : WooPosToolbarUIEvent() + data class MenuItemClicked(val menuItem: WooPosToolbarState.Menu.MenuItem) : WooPosToolbarUIEvent() +} diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeToolbarViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosToolbarViewModel.kt similarity index 67% rename from WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeToolbarViewModel.kt rename to WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosToolbarViewModel.kt index 4af687a4628..73782fcd6d1 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeToolbarViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosToolbarViewModel.kt @@ -10,10 +10,10 @@ import com.woocommerce.android.cardreader.connection.CardReaderStatus.NotConnect import com.woocommerce.android.ui.woopos.cardreader.WooPosCardReaderFacade import com.woocommerce.android.ui.woopos.home.ChildToParentEvent import com.woocommerce.android.ui.woopos.home.WooPosChildrenToParentEventSender -import com.woocommerce.android.ui.woopos.home.toolbar.WooPosHomeToolbarUIEvent.ConnectToAReaderClicked -import com.woocommerce.android.ui.woopos.home.toolbar.WooPosHomeToolbarUIEvent.MenuItemClicked -import com.woocommerce.android.ui.woopos.home.toolbar.WooPosHomeToolbarUIEvent.OnOutsideOfToolbarMenuClicked -import com.woocommerce.android.ui.woopos.home.toolbar.WooPosHomeToolbarUIEvent.OnToolbarMenuClicked +import com.woocommerce.android.ui.woopos.home.toolbar.WooPosToolbarUIEvent.ConnectToAReaderClicked +import com.woocommerce.android.ui.woopos.home.toolbar.WooPosToolbarUIEvent.MenuItemClicked +import com.woocommerce.android.ui.woopos.home.toolbar.WooPosToolbarUIEvent.OnOutsideOfToolbarMenuClicked +import com.woocommerce.android.ui.woopos.home.toolbar.WooPosToolbarUIEvent.OnToolbarMenuClicked import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -21,17 +21,17 @@ import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel -class WooPosHomeToolbarViewModel @Inject constructor( +class WooPosToolbarViewModel @Inject constructor( private val cardReaderFacade: WooPosCardReaderFacade, private val childrenToParentEventSender: WooPosChildrenToParentEventSender, ) : ViewModel() { private val _state = MutableStateFlow( - WooPosHomeToolbarState( - cardReaderStatus = WooPosHomeToolbarState.WooPosCardReaderStatus.NotConnected, - menu = WooPosHomeToolbarState.Menu.Hidden, + WooPosToolbarState( + cardReaderStatus = WooPosToolbarState.WooPosCardReaderStatus.NotConnected, + menu = WooPosToolbarState.Menu.Hidden, ) ) - val state: StateFlow = _state + val state: StateFlow = _state init { viewModelScope.launch { @@ -43,9 +43,9 @@ class WooPosHomeToolbarViewModel @Inject constructor( } } - fun onUiEvent(event: WooPosHomeToolbarUIEvent) { + fun onUiEvent(event: WooPosToolbarUIEvent) { val currentState = _state.value - if (currentState.menu is WooPosHomeToolbarState.Menu.Visible && event !is MenuItemClicked) { + if (currentState.menu is WooPosToolbarState.Menu.Visible && event !is MenuItemClicked) { hideMenu() return } @@ -53,7 +53,7 @@ class WooPosHomeToolbarViewModel @Inject constructor( when (event) { is OnToolbarMenuClicked -> { _state.value = currentState.copy( - menu = WooPosHomeToolbarState.Menu.Visible(toolbarMenuItems) + menu = WooPosToolbarState.Menu.Visible(toolbarMenuItems) ) } @@ -80,29 +80,29 @@ class WooPosHomeToolbarViewModel @Inject constructor( } private fun hideMenu() { - _state.value = _state.value.copy(menu = WooPosHomeToolbarState.Menu.Hidden) + _state.value = _state.value.copy(menu = WooPosToolbarState.Menu.Hidden) } private fun handleConnectToReaderButtonClicked() { - if (_state.value.cardReaderStatus != WooPosHomeToolbarState.WooPosCardReaderStatus.Connected) { + if (_state.value.cardReaderStatus != WooPosToolbarState.WooPosCardReaderStatus.Connected) { cardReaderFacade.connectToReader() } } - private fun mapCardReaderStatusToUiState(status: CardReaderStatus): WooPosHomeToolbarState.WooPosCardReaderStatus { + private fun mapCardReaderStatusToUiState(status: CardReaderStatus): WooPosToolbarState.WooPosCardReaderStatus { return when (status) { - is Connected -> WooPosHomeToolbarState.WooPosCardReaderStatus.Connected - is NotConnected, Connecting -> WooPosHomeToolbarState.WooPosCardReaderStatus.NotConnected + is Connected -> WooPosToolbarState.WooPosCardReaderStatus.Connected + is NotConnected, Connecting -> WooPosToolbarState.WooPosCardReaderStatus.NotConnected } } private companion object { val toolbarMenuItems = listOf( - WooPosHomeToolbarState.Menu.MenuItem( + WooPosToolbarState.Menu.MenuItem( title = R.string.woopos_get_support_title, icon = R.drawable.woopos_ic_get_support, ), - WooPosHomeToolbarState.Menu.MenuItem( + WooPosToolbarState.Menu.MenuItem( title = R.string.woopos_exit_confirmation_title, icon = R.drawable.woopos_ic_exit_pos, ), From c87af82d66f8fb53db5b3c85060c9aecaa2ef844 Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 22 Jul 2024 11:26:15 +0200 Subject: [PATCH 10/12] WooPosToolbarViewModel --- .../home/cart/WooPosCartViewModelTest.kt | 4 - .../products/WooPosProductsViewModelTest.kt | 3 - .../toolbar/WooPosToolbarViewModelTest.kt | 136 ++++++++++++++++++ .../home/totals/WooPosTotalsViewModelTest.kt | 3 - 4 files changed, 136 insertions(+), 10 deletions(-) create mode 100644 WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosToolbarViewModelTest.kt diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/cart/WooPosCartViewModelTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/cart/WooPosCartViewModelTest.kt index c788d7bb759..a04323fb393 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/cart/WooPosCartViewModelTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/cart/WooPosCartViewModelTest.kt @@ -17,8 +17,6 @@ import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.test.runTest import org.assertj.core.api.Assertions.assertThat import org.junit.Rule -import org.junit.runner.RunWith -import org.mockito.junit.MockitoJUnitRunner import org.mockito.kotlin.eq import org.mockito.kotlin.mock import org.mockito.kotlin.whenever @@ -26,9 +24,7 @@ import java.math.BigDecimal import kotlin.test.Test @ExperimentalCoroutinesApi -@RunWith(MockitoJUnitRunner::class) class WooPosCartViewModelTest { - @Rule @JvmField val rule = InstantTaskExecutorRule() diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/products/WooPosProductsViewModelTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/products/WooPosProductsViewModelTest.kt index 35699339bd9..3d20d903534 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/products/WooPosProductsViewModelTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/products/WooPosProductsViewModelTest.kt @@ -10,8 +10,6 @@ import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest import org.assertj.core.api.Assertions.assertThat import org.junit.Rule -import org.junit.runner.RunWith -import org.mockito.junit.MockitoJUnitRunner import org.mockito.kotlin.any import org.mockito.kotlin.eq import org.mockito.kotlin.mock @@ -21,7 +19,6 @@ import java.math.BigDecimal import kotlin.test.Test @ExperimentalCoroutinesApi -@RunWith(MockitoJUnitRunner::class) class WooPosProductsViewModelTest { @Rule diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosToolbarViewModelTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosToolbarViewModelTest.kt new file mode 100644 index 00000000000..3c3fbc7cc2f --- /dev/null +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosToolbarViewModelTest.kt @@ -0,0 +1,136 @@ +package com.woocommerce.android.ui.woopos.home.toolbar + +import com.woocommerce.android.R +import com.woocommerce.android.cardreader.connection.CardReaderStatus +import com.woocommerce.android.ui.woopos.cardreader.WooPosCardReaderFacade +import com.woocommerce.android.ui.woopos.home.ChildToParentEvent +import com.woocommerce.android.ui.woopos.home.WooPosChildrenToParentEventSender +import com.woocommerce.android.ui.woopos.util.WooPosCoroutineTestRule +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.runTest +import org.assertj.core.api.Assertions.assertThat +import org.junit.Rule +import org.junit.Test +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever + +@ExperimentalCoroutinesApi +class WooPosToolbarViewModelTest { + @Rule + @JvmField + val coroutinesTestRule = WooPosCoroutineTestRule() + private val cardReaderFacade: WooPosCardReaderFacade = mock { + onBlocking { readerStatus }.thenReturn(flowOf(CardReaderStatus.NotConnected())) + } + private val childrenToParentEventSender: WooPosChildrenToParentEventSender = mock() + + @Test + fun `given card reader status is NotConnected, when initialized, then state should be NotConnected`() = runTest { + // GIVEN + whenever(cardReaderFacade.readerStatus).thenReturn(flowOf(CardReaderStatus.NotConnected())) + val viewModel = createViewModel() + + // THEN + assertThat(viewModel.state.value.cardReaderStatus) + .isEqualTo(WooPosToolbarState.WooPosCardReaderStatus.NotConnected) + } + + @Test + fun `given card reader status is Connected, when initialized, then state should be Connected`() = runTest { + // GIVEN + whenever(cardReaderFacade.readerStatus).thenReturn(flowOf(CardReaderStatus.Connected(mock()))) + val viewModel = createViewModel() + + // THEN + assertThat(viewModel.state.value.cardReaderStatus) + .isEqualTo(WooPosToolbarState.WooPosCardReaderStatus.Connected) + } + + @Test + fun `given card reader status is Connecting, when initialized, then state should be NotConnected`() = runTest { + // GIVEN + whenever(cardReaderFacade.readerStatus).thenReturn(flowOf(CardReaderStatus.Connecting)) + val viewModel = createViewModel() + + // THEN + assertThat(viewModel.state.value.cardReaderStatus) + .isEqualTo(WooPosToolbarState.WooPosCardReaderStatus.NotConnected) + } + + @Test + fun `when OnToolbarMenuClicked passed, then menu should be visible`() = runTest { + // GIVEN + val viewModel = createViewModel() + + // WHEN + viewModel.onUiEvent(WooPosToolbarUIEvent.OnToolbarMenuClicked) + + // THEN + assertThat(viewModel.state.value.menu) + .isEqualTo( + WooPosToolbarState.Menu.Visible( + listOf( + WooPosToolbarState.Menu.MenuItem( + title = R.string.woopos_get_support_title, + icon = R.drawable.woopos_ic_get_support, + ), + WooPosToolbarState.Menu.MenuItem( + title = R.string.woopos_exit_confirmation_title, + icon = R.drawable.woopos_ic_exit_pos, + ), + ) + ) + ) + } + + @Test + fun `when OnOutsideOfToolbarMenuClicked passed and menu is visible, then menu should be hidden`() = runTest { + // GIVEN + val viewModel = createViewModel() + viewModel.onUiEvent(WooPosToolbarUIEvent.OnToolbarMenuClicked) + + // WHEN + viewModel.onUiEvent(WooPosToolbarUIEvent.OnOutsideOfToolbarMenuClicked) + + // THEN + assertThat(viewModel.state.value.menu) + .isEqualTo(WooPosToolbarState.Menu.Hidden) + } + + @Test + fun `when ConnectToAReaderClicked passed, then connect to reader should be called`() = runTest { + // GIVEN + whenever(cardReaderFacade.readerStatus).thenReturn(flowOf(CardReaderStatus.NotConnected())) + val viewModel = createViewModel() + + // WHEN + viewModel.onUiEvent(WooPosToolbarUIEvent.ConnectToAReaderClicked) + + // THEN + verify(cardReaderFacade).connectToReader() + } + + @Test + fun `when MenuItemClicked with ExitPosClicked, then ExitPosClicked event should be sent`() = runTest { + // GIVEN + val viewModel = createViewModel() + val menuItem = WooPosToolbarState.Menu.MenuItem( + title = R.string.woopos_exit_confirmation_title, + icon = R.drawable.woopos_ic_exit_pos + ) + + // WHEN + viewModel.onUiEvent(WooPosToolbarUIEvent.MenuItemClicked(menuItem)) + + // THEN + verify(childrenToParentEventSender).sendToParent(ChildToParentEvent.ExitPosClicked) + assertThat(viewModel.state.value.menu).isEqualTo(WooPosToolbarState.Menu.Hidden) + } + + private fun createViewModel() = WooPosToolbarViewModel( + cardReaderFacade, + childrenToParentEventSender + ) +} diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/totals/WooPosTotalsViewModelTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/totals/WooPosTotalsViewModelTest.kt index 4e49d529d78..3ca80dd620f 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/totals/WooPosTotalsViewModelTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/totals/WooPosTotalsViewModelTest.kt @@ -15,8 +15,6 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.runTest import org.assertj.core.api.Assertions.assertThat import org.junit.Rule -import org.junit.runner.RunWith -import org.mockito.junit.MockitoJUnitRunner import org.mockito.kotlin.mock import org.mockito.kotlin.verify import java.math.BigDecimal @@ -24,7 +22,6 @@ import java.util.Date import kotlin.test.Test @ExperimentalCoroutinesApi -@RunWith(MockitoJUnitRunner.Silent::class) class WooPosTotalsViewModelTest { @Rule From a7777e16a4a8a6fff4ac654280c995a2939f1524 Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 22 Jul 2024 12:19:53 +0200 Subject: [PATCH 11/12] Smaller toolbar. Border to it as per changes in the design --- .../ui/woopos/home/toolbar/WooPosToolbar.kt | 34 ++++++++++++++----- WooCommerce/src/main/res/values/strings.xml | 2 +- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosToolbar.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosToolbar.kt index 6bf507260ae..15ef819740a 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosToolbar.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosToolbar.kt @@ -8,6 +8,7 @@ import androidx.compose.animation.core.updateTransition import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.foundation.background +import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Arrangement @@ -20,7 +21,6 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width -import androidx.compose.foundation.layout.wrapContentSize import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.ButtonDefaults @@ -294,9 +294,7 @@ private fun CardReaderStatusButton( ) { status -> when (status) { WooPosCardReaderStatus.Connected -> MaterialTheme.colors.secondary - WooPosCardReaderStatus.NotConnected -> MaterialTheme.colors.secondaryVariant.copy( - alpha = 0.8f - ) + WooPosCardReaderStatus.NotConnected -> MaterialTheme.colors.primary } } @@ -307,14 +305,34 @@ private fun CardReaderStatusButton( } ) + val borderColor by transition.animateColor( + transitionSpec = { tween(durationMillis = animationDuration) }, + label = "BorderColorTransition" + ) { status -> + when (status) { + WooPosCardReaderStatus.Connected -> Color.Transparent + WooPosCardReaderStatus.NotConnected -> MaterialTheme.colors.primary + } + } + Card( - modifier = modifier - .wrapContentSize(Alignment.Center), + modifier = modifier. + height(56.dp), backgroundColor = MaterialTheme.colors.surface, elevation = 8.dp, shape = RoundedCornerShape(8.dp), ) { - TextButton(onClick = onClick) { + TextButton( + onClick = onClick, + modifier = Modifier + .padding(8.dp.toAdaptivePadding()) + .border( + width = 2.dp, + color = borderColor, + shape = RoundedCornerShape(4.dp) + ) + .height(40.dp), + ) { Spacer(modifier = Modifier.width(16.dp.toAdaptivePadding())) Circle(size = 12.dp, color = illustrationColor) Spacer(modifier = Modifier.width(4.dp.toAdaptivePadding())) @@ -335,7 +353,7 @@ private fun ReaderStatusText( color: Color ) { Text( - modifier = modifier.padding(8.dp.toAdaptivePadding()), + modifier = modifier.padding(horizontal = 8.dp.toAdaptivePadding()), text = title, color = color, style = MaterialTheme.typography.button diff --git a/WooCommerce/src/main/res/values/strings.xml b/WooCommerce/src/main/res/values/strings.xml index 7cc3bfdfc5e..fee6993cbac 100644 --- a/WooCommerce/src/main/res/values/strings.xml +++ b/WooCommerce/src/main/res/values/strings.xml @@ -4170,7 +4170,7 @@ --> Exit POS Reader Connected - Reader Disconnected + Connect your reader Checkout Reader Status Unknown Check out From 6d7d4bd99c47de547fcae898b6d6e04b47d34d52 Mon Sep 17 00:00:00 2001 From: Andrey Date: Mon, 22 Jul 2024 12:22:20 +0200 Subject: [PATCH 12/12] Fixed formatting --- .../android/ui/woopos/home/toolbar/WooPosToolbar.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosToolbar.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosToolbar.kt index 15ef819740a..c3cd283c908 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosToolbar.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosToolbar.kt @@ -316,8 +316,8 @@ private fun CardReaderStatusButton( } Card( - modifier = modifier. - height(56.dp), + modifier = modifier + .height(56.dp), backgroundColor = MaterialTheme.colors.surface, elevation = 8.dp, shape = RoundedCornerShape(8.dp),