Skip to content

Commit 8b8a2f3

Browse files
authored
Merge pull request #13539 from woocommerce/issue/fix-missing-done-button-in-service-packages
[Shipping Labels] Fix missing done button in service packages
2 parents 26fb14d + 7258f78 commit 8b8a2f3

File tree

7 files changed

+108
-83
lines changed

7 files changed

+108
-83
lines changed

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/shippinglabels/creation/ShippingLabelCreateCustomPackageFragment.kt

Lines changed: 12 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
package com.woocommerce.android.ui.orders.shippinglabels.creation
22

33
import android.os.Bundle
4-
import android.view.MenuItem
54
import android.view.View
65
import androidx.annotation.StringRes
7-
import androidx.appcompat.content.res.AppCompatResources
86
import androidx.fragment.app.viewModels
9-
import androidx.navigation.fragment.findNavController
7+
import androidx.lifecycle.lifecycleScope
108
import com.woocommerce.android.R
119
import com.woocommerce.android.databinding.FragmentShippingLabelCreateCustomPackageBinding
1210
import com.woocommerce.android.extensions.takeIfNotEqualTo
@@ -16,9 +14,13 @@ import com.woocommerce.android.ui.base.UIMessageResolver
1614
import com.woocommerce.android.ui.main.AppBarStatus
1715
import com.woocommerce.android.ui.orders.shippinglabels.creation.ShippingLabelCreateCustomPackageViewModel.InputName
1816
import com.woocommerce.android.ui.orders.shippinglabels.creation.ShippingLabelCreateCustomPackageViewModel.PackageSuccessfullyMadeEvent
17+
import com.woocommerce.android.ui.orders.shippinglabels.creation.ShippingLabelCreatePackageViewModel.PackageType.CUSTOM
1918
import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.ShowSnackbar
2019
import com.woocommerce.android.widgets.CustomProgressDialog
2120
import dagger.hilt.android.AndroidEntryPoint
21+
import kotlinx.coroutines.flow.filter
22+
import kotlinx.coroutines.flow.launchIn
23+
import kotlinx.coroutines.flow.onEach
2224
import org.wordpress.android.util.ActivityUtils
2325
import javax.inject.Inject
2426

@@ -42,27 +44,11 @@ class ShippingLabelCreateCustomPackageFragment :
4244
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
4345
super.onViewCreated(view, savedInstanceState)
4446
_binding = FragmentShippingLabelCreateCustomPackageBinding.bind(view)
45-
setupToolbar()
4647

4748
initializeInputFields()
4849
setupObservers()
4950
}
5051

51-
private fun setupToolbar() {
52-
binding.toolbar.title = getString(R.string.shipping_label_package_selector_title)
53-
binding.toolbar.setOnMenuItemClickListener { menuItem ->
54-
onMenuItemSelected(menuItem)
55-
}
56-
binding.toolbar.navigationIcon = AppCompatResources.getDrawable(
57-
requireActivity(),
58-
R.drawable.ic_back_24dp
59-
)
60-
binding.toolbar.setNavigationOnClickListener {
61-
findNavController().navigateUp()
62-
}
63-
binding.toolbar.inflateMenu(R.menu.menu_done)
64-
}
65-
6652
private fun initializeInputFields() {
6753
// Initialize spinner
6854
binding.typeSpinner.setup(
@@ -154,6 +140,13 @@ class ShippingLabelCreateCustomPackageFragment :
154140
else -> event.isHandled = false
155141
}
156142
}
143+
144+
parentViewModel.creationDoneFlow
145+
.filter { it.selectedTab == CUSTOM }
146+
.onEach {
147+
ActivityUtils.hideKeyboard(activity)
148+
viewModel.onCustomFormDoneMenuClicked()
149+
}.launchIn(viewLifecycleOwner.lifecycleScope)
157150
}
158151

159152
private fun showProgressDialog(@StringRes title: Int, @StringRes message: Int) {
@@ -169,15 +162,4 @@ class ShippingLabelCreateCustomPackageFragment :
169162
progressDialog?.dismiss()
170163
progressDialog = null
171164
}
172-
173-
private fun onMenuItemSelected(item: MenuItem): Boolean {
174-
return when (item.itemId) {
175-
R.id.menu_done -> {
176-
ActivityUtils.hideKeyboard(activity)
177-
viewModel.onCustomFormDoneMenuClicked()
178-
true
179-
}
180-
else -> false
181-
}
182-
}
183165
}

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/shippinglabels/creation/ShippingLabelCreatePackageFragment.kt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import com.woocommerce.android.extensions.navigateBackWithResult
1414
import com.woocommerce.android.ui.base.BaseFragment
1515
import com.woocommerce.android.ui.base.UIMessageResolver
1616
import com.woocommerce.android.ui.main.AppBarStatus
17+
import com.woocommerce.android.ui.orders.shippinglabels.creation.ShippingLabelCreatePackageViewModel.PackageType
1718
import com.woocommerce.android.ui.orders.shippinglabels.creation.ShippingPackageSelectorFragment.Companion.SELECTED_PACKAGE_RESULT
1819
import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.ExitWithResult
1920
import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.ShowSnackbar
@@ -40,6 +41,7 @@ class ShippingLabelCreatePackageFragment : BaseFragment(R.layout.fragment_shippi
4041
viewPager.adapter = adapter
4142

4243
initializeTabs(tabLayout, viewPager)
44+
initializePageChangeCallback(viewPager)
4345
setupObservers(viewModel)
4446
}
4547

@@ -52,6 +54,16 @@ class ShippingLabelCreatePackageFragment : BaseFragment(R.layout.fragment_shippi
5254
binding.toolbar.setNavigationOnClickListener {
5355
findNavController().navigateUp()
5456
}
57+
binding.toolbar.setOnMenuItemClickListener { item ->
58+
when (item.itemId) {
59+
R.id.menu_done -> {
60+
viewModel.onDoneButtonClicked()
61+
true
62+
}
63+
else -> false
64+
}
65+
}
66+
binding.toolbar.inflateMenu(R.menu.menu_done)
5567
}
5668

5769
private fun initializeTabs(tabLayout: TabLayout, viewPager: ViewPager2) {
@@ -61,6 +73,14 @@ class ShippingLabelCreatePackageFragment : BaseFragment(R.layout.fragment_shippi
6173
}.attach()
6274
}
6375

76+
private fun initializePageChangeCallback(viewPager: ViewPager2) {
77+
viewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
78+
override fun onPageSelected(position: Int) {
79+
viewModel.onSelectedPageChanged(PackageType.fromOrdinal(position))
80+
}
81+
})
82+
}
83+
6484
private fun setupObservers(viewModel: ShippingLabelCreatePackageViewModel) {
6585
viewModel.event.observe(viewLifecycleOwner) { event ->
6686
when (event) {

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/shippinglabels/creation/ShippingLabelCreatePackageViewModel.kt

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,36 @@
11
package com.woocommerce.android.ui.orders.shippinglabels.creation
22

3-
import android.os.Parcelable
43
import androidx.lifecycle.SavedStateHandle
4+
import androidx.lifecycle.viewModelScope
55
import com.woocommerce.android.R
66
import com.woocommerce.android.analytics.AnalyticsEvent
77
import com.woocommerce.android.analytics.AnalyticsTracker
88
import com.woocommerce.android.model.ShippingPackage
9-
import com.woocommerce.android.viewmodel.LiveDataDelegate
9+
import com.woocommerce.android.viewmodel.MultiLiveEvent
1010
import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.ExitWithResult
1111
import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.ShowSnackbar
1212
import com.woocommerce.android.viewmodel.ScopedViewModel
13+
import com.woocommerce.android.viewmodel.getStateFlow
1314
import com.woocommerce.android.viewmodel.navArgs
1415
import dagger.hilt.android.lifecycle.HiltViewModel
15-
import kotlinx.parcelize.Parcelize
16+
import kotlinx.coroutines.flow.Flow
17+
import kotlinx.coroutines.flow.MutableSharedFlow
18+
import kotlinx.coroutines.flow.asSharedFlow
19+
import kotlinx.coroutines.flow.update
20+
import kotlinx.coroutines.launch
1621
import javax.inject.Inject
1722

1823
@HiltViewModel
1924
class ShippingLabelCreatePackageViewModel @Inject constructor(
2025
savedState: SavedStateHandle
2126
) : ScopedViewModel(savedState) {
2227
private val arguments: ShippingLabelCreatePackageFragmentArgs by savedState.navArgs()
28+
private val selectedTabType = savedState.getStateFlow(
29+
scope = viewModelScope,
30+
initialValue = PackageType.CUSTOM
31+
)
32+
private val _creationDoneFlow = MutableSharedFlow<OnDoneButtonClicked>()
33+
val creationDoneFlow: Flow<OnDoneButtonClicked> = _creationDoneFlow.asSharedFlow()
2334

2435
fun onPackageCreated(madePackage: ShippingPackage) {
2536
val type = if (madePackage.category == ShippingPackage.CUSTOM_PACKAGE_CATEGORY) "custom" else "predefined"
@@ -45,21 +56,24 @@ class ShippingLabelCreatePackageViewModel @Inject constructor(
4556
)
4657
}
4758

48-
/**
49-
* Saving more data than necessary into the SavedState has associated risks which were not known at the time this
50-
* field was implemented - after we ensure we don't save unnecessary data, we can replace @Suppress("OPT_IN_USAGE")
51-
* with @OptIn(LiveDelegateSavedStateAPI::class).
52-
*/
53-
@Suppress("OPT_IN_USAGE")
54-
val viewStateData = LiveDataDelegate(savedState, ShippingLabelCreatePackageViewState())
59+
fun onSelectedPageChanged(selectedTab: PackageType) {
60+
selectedTabType.update { selectedTab }
61+
}
5562

56-
@Parcelize
57-
data class ShippingLabelCreatePackageViewState(
58-
val selectedTab: PackageType = PackageType.CUSTOM
59-
) : Parcelable
63+
fun onDoneButtonClicked() {
64+
launch { _creationDoneFlow.emit(OnDoneButtonClicked(selectedTab = selectedTabType.value)) }
65+
}
6066

6167
enum class PackageType {
6268
CUSTOM,
63-
SERVICE
69+
SERVICE;
70+
71+
companion object {
72+
fun fromOrdinal(ordinal: Int) = entries[ordinal]
73+
}
6474
}
75+
76+
data class OnDoneButtonClicked(
77+
val selectedTab: PackageType
78+
) : MultiLiveEvent.Event()
6579
}

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/shippinglabels/creation/ShippingLabelCreatePackageViewPagerAdapter.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ class ShippingLabelCreatePackageViewPagerAdapter(container: Fragment) : Fragment
88
override fun getItemCount(): Int = PackageType.values().size
99

1010
override fun createFragment(position: Int): Fragment {
11-
return when (PackageType.values()[position]) {
11+
return when (PackageType.entries[position]) {
1212
PackageType.CUSTOM -> ShippingLabelCreateCustomPackageFragment()
1313
PackageType.SERVICE -> ShippingLabelCreateServicePackageFragment()
1414
}

WooCommerce/src/main/kotlin/com/woocommerce/android/ui/orders/shippinglabels/creation/ShippingLabelCreateServicePackageFragment.kt

Lines changed: 10 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,34 @@
11
package com.woocommerce.android.ui.orders.shippinglabels.creation
22

33
import android.os.Bundle
4-
import android.view.Menu
5-
import android.view.MenuInflater
64
import android.view.MenuItem
75
import android.view.View
86
import androidx.annotation.StringRes
9-
import androidx.core.view.MenuProvider
107
import androidx.core.view.isVisible
118
import androidx.fragment.app.viewModels
9+
import androidx.lifecycle.lifecycleScope
1210
import androidx.recyclerview.widget.LinearLayoutManager
1311
import com.woocommerce.android.R
1412
import com.woocommerce.android.databinding.FragmentShippingLabelCreateServicePackageBinding
1513
import com.woocommerce.android.extensions.takeIfNotEqualTo
1614
import com.woocommerce.android.ui.base.BaseFragment
1715
import com.woocommerce.android.ui.base.UIMessageResolver
16+
import com.woocommerce.android.ui.orders.shippinglabels.creation.ShippingLabelCreatePackageViewModel.PackageType.SERVICE
1817
import com.woocommerce.android.ui.orders.shippinglabels.creation.ShippingLabelCreateServicePackageViewModel.PackageSuccessfullyMadeEvent
1918
import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.ShowSnackbar
2019
import com.woocommerce.android.widgets.CustomProgressDialog
2120
import com.woocommerce.android.widgets.SkeletonView
2221
import com.woocommerce.android.widgets.WCEmptyView
2322
import dagger.hilt.android.AndroidEntryPoint
23+
import kotlinx.coroutines.flow.filter
24+
import kotlinx.coroutines.flow.launchIn
25+
import kotlinx.coroutines.flow.onEach
2426
import org.wordpress.android.util.ActivityUtils
2527
import javax.inject.Inject
2628

2729
@AndroidEntryPoint
2830
class ShippingLabelCreateServicePackageFragment :
29-
BaseFragment(R.layout.fragment_shipping_label_create_service_package),
30-
MenuProvider {
31+
BaseFragment(R.layout.fragment_shipping_label_create_service_package) {
3132
@Inject lateinit var uiMessageResolver: UIMessageResolver
3233
private val skeletonView: SkeletonView = SkeletonView()
3334
private var progressDialog: CustomProgressDialog? = null
@@ -36,18 +37,9 @@ class ShippingLabelCreateServicePackageFragment :
3637
private val parentViewModel: ShippingLabelCreatePackageViewModel by viewModels({ requireParentFragment() })
3738
val viewModel: ShippingLabelCreateServicePackageViewModel by viewModels()
3839

39-
override fun onCreateMenu(menu: Menu, inflater: MenuInflater) {
40-
menu.clear()
41-
inflater.inflate(R.menu.menu_done, menu)
42-
doneMenuItem = menu.findItem(R.id.menu_done)
43-
doneMenuItem.isVisible = viewModel.viewStateData.liveData.value?.canSave ?: false
44-
}
45-
4640
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
4741
super.onViewCreated(view, savedInstanceState)
4842

49-
requireActivity().addMenuProvider(this, viewLifecycleOwner)
50-
5143
val binding = FragmentShippingLabelCreateServicePackageBinding.bind(view)
5244
val packagesAdapter = ShippingLabelServicePackageAdapter(
5345
viewModel::onPackageSelected,
@@ -113,17 +105,13 @@ class ShippingLabelCreateServicePackageFragment :
113105
else -> event.isHandled = false
114106
}
115107
}
116-
}
117108

118-
override fun onMenuItemSelected(item: MenuItem): Boolean {
119-
return when (item.itemId) {
120-
R.id.menu_done -> {
109+
parentViewModel.creationDoneFlow
110+
.filter { it.selectedTab == SERVICE }
111+
.onEach {
121112
ActivityUtils.hideKeyboard(activity)
122113
viewModel.onCustomFormDoneMenuClicked()
123-
true
124-
}
125-
else -> false
126-
}
114+
}.launchIn(viewLifecycleOwner.lifecycleScope)
127115
}
128116

129117
private fun showSkeleton(show: Boolean, binding: FragmentShippingLabelCreateServicePackageBinding) {

WooCommerce/src/main/res/layout/fragment_shipping_label_create_custom_package.xml

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,10 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
33
xmlns:app="http://schemas.android.com/apk/res-auto"
4-
xmlns:tools="http://schemas.android.com/tools"
54
android:layout_width="match_parent"
65
android:layout_height="match_parent"
76
android:orientation="vertical">
87

9-
<com.google.android.material.appbar.MaterialToolbar
10-
android:id="@+id/toolbar"
11-
style="@style/Widget.Woo.Toolbar"
12-
android:layout_width="match_parent"
13-
android:layout_height="@dimen/toolbar_height"
14-
android:elevation="@dimen/appbar_elevation"
15-
app:layout_collapseMode="pin"
16-
app:layout_constraintEnd_toEndOf="parent"
17-
app:layout_constraintStart_toStartOf="parent"
18-
app:layout_constraintTop_toTopOf="parent"
19-
tools:title="@string/app_name" />
20-
218
<View
229
android:id="@+id/app_bar_divider"
2310
style="@style/Woo.Divider"

WooCommerce/src/test/kotlin/com/woocommerce/android/ui/orders/shippinglabels/creation/ShippingLabelCreatePackageViewModelTest.kt

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,16 @@ package com.woocommerce.android.ui.orders.shippinglabels.creation
33
import com.woocommerce.android.R
44
import com.woocommerce.android.model.PackageDimensions
55
import com.woocommerce.android.model.ShippingPackage
6+
import com.woocommerce.android.ui.orders.shippinglabels.creation.ShippingLabelCreatePackageViewModel.OnDoneButtonClicked
7+
import com.woocommerce.android.ui.orders.shippinglabels.creation.ShippingLabelCreatePackageViewModel.PackageType
68
import com.woocommerce.android.viewmodel.BaseUnitTest
79
import com.woocommerce.android.viewmodel.MultiLiveEvent.Event
810
import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.ShowSnackbar
911
import kotlinx.coroutines.ExperimentalCoroutinesApi
12+
import kotlinx.coroutines.flow.launchIn
13+
import kotlinx.coroutines.flow.onEach
1014
import org.assertj.core.api.Assertions.assertThat
15+
import org.junit.Before
1116
import org.junit.Test
1217

1318
@ExperimentalCoroutinesApi
@@ -23,14 +28,14 @@ class ShippingLabelCreatePackageViewModelTest : BaseUnitTest() {
2328
1f
2429
)
2530

26-
fun setup() {
31+
@Before
32+
fun setUp() {
2733
val savedState = ShippingLabelCreatePackageFragmentArgs(0).toSavedStateHandle()
2834
viewModel = ShippingLabelCreatePackageViewModel(savedState)
2935
}
3036

3137
@Test
3238
fun `when handling package creation success, then show Snackbar and navigate to edit label package screen`() {
33-
setup()
3439
val events = mutableListOf<Event>()
3540
viewModel.event.observeForever {
3641
events.add(it)
@@ -53,4 +58,33 @@ class ShippingLabelCreatePackageViewModelTest : BaseUnitTest() {
5358
)
5459
)
5560
}
61+
62+
@Test
63+
fun `when onDoneButtonClicked is called, then creationDoneFlow emits the correct event`() = testBlocking {
64+
// Given
65+
val expectedEvent = OnDoneButtonClicked(PackageType.CUSTOM)
66+
var receivedEvent: OnDoneButtonClicked? = null
67+
viewModel.creationDoneFlow.onEach { receivedEvent = it }.launchIn(this.backgroundScope)
68+
69+
// When
70+
viewModel.onDoneButtonClicked()
71+
72+
// Then
73+
assertThat(receivedEvent).isEqualTo(expectedEvent)
74+
}
75+
76+
@Test
77+
fun `when onSelectedPageChanged is called, then selectedTabType is updated`() = testBlocking {
78+
// Given
79+
val expectedEvent = OnDoneButtonClicked(PackageType.SERVICE)
80+
var receivedEvent: OnDoneButtonClicked? = null
81+
viewModel.creationDoneFlow.onEach { receivedEvent = it }.launchIn(this.backgroundScope)
82+
83+
// When
84+
viewModel.onSelectedPageChanged(PackageType.SERVICE)
85+
viewModel.onDoneButtonClicked()
86+
87+
// Then
88+
assertThat(receivedEvent).isEqualTo(expectedEvent)
89+
}
5690
}

0 commit comments

Comments
 (0)