Skip to content

Commit

Permalink
Merge pull request #13151 from woocommerce/task/13130-order-multi-sel…
Browse files Browse the repository at this point in the history
…ect-ii

[Bulk Update Orders] Enable multiple selection in Order List: Part II
  • Loading branch information
hafizrahman authored Dec 27, 2024
2 parents b34b06d + 96c8072 commit 16cc17a
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ class OrderFiltersCard @JvmOverloads constructor(
}
}

fun isEnabled(value: Boolean) {
binding.btnOrderFilter.isEnabled = value
}

private fun setFiltersTitle(@StringRes stringId: Int) {
binding.filtersTitle.text = context.getString(stringId)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ class OrderListAdapter(
viewBinding.divider.visibility = if (orderItemUI.isLastItemInSection) View.GONE else View.VISIBLE

when {
orderItemUI.isSelected || isActivated -> {
orderItemUI.isSelected -> {
viewBinding.orderItemLayout.setBackgroundColor(
viewBinding.root.context.getColor(R.color.color_item_selected)
)
Expand All @@ -188,6 +188,8 @@ class OrderListAdapter(
viewBinding.orderItemLayout.setBackgroundColor(Color.TRANSPARENT)
}
}
viewBinding.orderImageSelected.visibility = if (isActivated) View.VISIBLE else View.GONE

// clear existing tags and add new ones
viewBinding.orderTags.removeAllViews()
processTagView(orderItemUI.status, this)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,14 +130,6 @@ class OrderListFragment :
super.onStop()
}

// Alias for interacting with [viewModel.isSearching] so the value is always identical
// to the real value on the UI side.
private var isSearching: Boolean
private set(value) {
viewModel.isSearching = value
}
get() = viewModel.isSearching

private var orderListMenu: Menu? = null
private var searchMenuItem: MenuItem? = null
private var searchView: SearchView? = null
Expand Down Expand Up @@ -166,7 +158,7 @@ class OrderListFragment :
lifecycle.addObserver(viewModel.performanceObserver)
super.onCreate(savedInstanceState)
savedInstanceState?.let { bundle ->
isSearching = bundle.getBoolean(STATE_KEY_IS_SEARCHING)
viewModel.isSearching = bundle.getBoolean(STATE_KEY_IS_SEARCHING)
searchQuery = bundle.getString(STATE_KEY_SEARCH_QUERY, "")
}
requireActivity().onBackPressedDispatcher.addCallback(
Expand All @@ -184,7 +176,7 @@ class OrderListFragment :
if (!binding.detailPaneContainer.findNavController().popBackStack()) {
findNavController().popBackStack()
}
} else if (isSearching) {
} else if (viewModel.isSearching) {
handleSearchViewCollapse()
} else {
val result =
Expand Down Expand Up @@ -312,7 +304,7 @@ class OrderListFragment :
}

private fun handleSearchViewExpand(): Boolean {
isSearching = true
viewModel.isSearching = true
refreshOptionsMenu()
checkOrientation()
onSearchViewActiveChanged(isActive = true)
Expand Down Expand Up @@ -397,7 +389,7 @@ class OrderListFragment :
}

override fun onSaveInstanceState(outState: Bundle) {
outState.putBoolean(STATE_KEY_IS_SEARCHING, isSearching)
outState.putBoolean(STATE_KEY_IS_SEARCHING, viewModel.isSearching)
outState.putString(STATE_KEY_SEARCH_QUERY, searchQuery)
if (findNavController().currentDestination?.id == R.id.orders) {
// We want to check if [OrderListFragment] is the current destination (at the top of the backstack),
Expand All @@ -424,7 +416,7 @@ class OrderListFragment :
* search menu item to collapse
*/
private fun refreshOptionsMenu() {
if (!isChildFragmentShowing() && isSearching) {
if (!isChildFragmentShowing() && viewModel.isSearching) {
val savedSearchQuery = searchQuery
enableSearchListeners()
handler.postDelayed({
Expand Down Expand Up @@ -480,7 +472,7 @@ class OrderListFragment :
}
}

override fun getFragmentTitle() = if (isSearching) "" else getString(R.string.orders)
override fun getFragmentTitle() = if (viewModel.isSearching) "" else getString(R.string.orders)

override fun scrollToTop() {
binding.orderListView.scrollToTop()
Expand Down Expand Up @@ -699,6 +691,12 @@ class OrderListFragment :
one = R.string.orderlist_selection_count_single
)
}
new.isBottomNavBarVisible.takeIfNotEqualTo(old?.isBottomNavBarVisible) { isBottomNavBarVisible ->
showBottomNavBar(isVisible = isBottomNavBarVisible)
}
new.isAddOrderButtonVisible.takeIfNotEqualTo(old?.isAddOrderButtonVisible) { isVisible ->
showAddOrderButton(show = isVisible)
}
}
viewModel.lastUpdateOrdersList.observe(viewLifecycleOwner) { lastUpdate ->
binding.orderFiltersCard.updateLastUpdate(lastUpdate)
Expand All @@ -711,10 +709,14 @@ class OrderListFragment :
actionMode = (requireActivity() as AppCompatActivity)
.startSupportActionMode(this@OrderListFragment)
delayMultiSelection()
enableFiltersCard(false)
enableOrdersRefresh(false)
}

OrderListViewModel.ViewState.OrderListState.Browsing -> {
actionMode?.finish()
enableFiltersCard(true)
enableOrdersRefresh(true)
}
}
}
Expand All @@ -726,6 +728,32 @@ class OrderListFragment :
}
}

private fun enableFiltersCard(enable: Boolean) {
binding.orderFiltersCard.isEnabled(enable)
}

private fun enableOrdersRefresh(enable: Boolean) {
binding.listPaneContainer.isEnabled = enable
}

private fun showBottomNavBar(isVisible: Boolean) {
if (!isVisible) {
(activity as? MainActivity)?.hideBottomNav()
} else {
(activity as? MainActivity)?.showBottomNav()
}
}

private fun showAddOrderButton(show: Boolean) {
if (show) {
uiMessageResolver.anchorViewId = binding.createOrderButton.id
binding.createOrderButton.show()
} else {
uiMessageResolver.anchorViewId = null
binding.createOrderButton.hide()
}
}

private fun openFirstOrder() {
binding.orderListView.openFirstOrder()
}
Expand Down Expand Up @@ -826,13 +854,32 @@ class OrderListFragment :
binding.orderListView.submitPagedList(pagedListData)
}

// Some edge cases in order selection mode, like tapping the screen with 4 fingers or using TalkBack,
// cause the order's onClick listener to gain focus over the selection tracker.
// This quick fix will prevent the app from entering an unexpected status when the app is in selection mode.
private fun shouldPreventDetailNavigation(orderId: Long): Boolean {
if (viewModel.isSelecting()) {
tracker?.let { selectionTracker ->
if (selectionTracker.isSelected(orderId)) {
selectionTracker.deselect(orderId)
} else {
selectionTracker.select(orderId)
}
}
return true
}
return false
}

override fun openOrderDetail(
orderId: Long,
allOrderIds: List<Long>,
orderStatus: String,
sharedView: View?,
startPaymentsFlow: Boolean,
) {
if (shouldPreventDetailNavigation(orderId)) return

viewModel.trackOrderClickEvent(
orderId,
orderStatus,
Expand All @@ -847,12 +894,12 @@ class OrderListFragment :

// if a search is active, we need to collapse the search view so order detail can show it's title and then
// remember the user was searching (since both searchQuery and isSearching will be reset)
if (isSearching) {
if (viewModel.isSearching) {
val savedSearch = searchQuery
clearSearchResults()
updateActivityTitle()
searchQuery = savedSearch
isSearching = true
viewModel.isSearching = true
}
(activity as? MainNavigationRouter)?.run {
val navHostFragment = if (requireContext().windowSizeClass != WindowSizeClass.Compact) {
Expand Down Expand Up @@ -906,7 +953,7 @@ class OrderListFragment :
}

override fun onMenuItemActionExpand(item: MenuItem): Boolean {
isSearching = true
viewModel.isSearching = true
checkOrientation()
onSearchViewActiveChanged(isActive = true)
binding.orderFiltersCard.isVisible = false
Expand All @@ -924,16 +971,16 @@ class OrderListFragment :
}

private fun clearSearchResults() {
if (isSearching) {
if (viewModel.isSearching) {
if (requireContext().windowSizeClass == WindowSizeClass.Compact) {
searchQuery = ""
isSearching = false
viewModel.isSearching = false
disableSearchListeners()
updateActivityTitle()
searchMenuItem?.collapseActionView()
(activity as? MainActivity)?.showBottomNav()
}
isSearching = false
viewModel.isSearching = false
}
}

Expand Down Expand Up @@ -1007,14 +1054,14 @@ class OrderListFragment :

private fun checkOrientation() {
val isLandscape = DisplayUtils.isLandscape(context)
if (isLandscape && isSearching) {
if (isLandscape && viewModel.isSearching) {
searchView?.post { searchView?.clearFocus() }
}
}
// endregion

override fun shouldExpandToolbar(): Boolean {
return binding.orderListView.ordersList.computeVerticalScrollOffset() == 0 && !isSearching
return binding.orderListView.ordersList.computeVerticalScrollOffset() == 0 && !viewModel.isSearching
}

private fun displaySimplePaymentsWIPCard(show: Boolean) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,12 @@ class OrderListViewModel @Inject constructor(

private var activeWCOrderListDescriptor: WCOrderListDescriptor? = null

var isSearching = false
var isSearching: Boolean
get() = viewState.isSearching
set(value) {
viewState = viewState.copy(isSearching = value)
}

private var dismissListErrors = false
var searchQuery = ""

Expand Down Expand Up @@ -900,14 +905,16 @@ class OrderListViewModel @Inject constructor(
private fun enterSelectionMode(count: Int) {
viewState = viewState.copy(
orderListState = ViewState.OrderListState.Selecting,
selectionCount = count
selectionCount = count,
isAddOrderButtonVisible = false
)
}

private fun exitSelectionMode() {
viewState = viewState.copy(
orderListState = ViewState.OrderListState.Browsing,
selectionCount = null
selectionCount = null,
isAddOrderButtonVisible = true
)
}

Expand Down Expand Up @@ -957,8 +964,13 @@ class OrderListViewModel @Inject constructor(
val isErrorFetchingDataBannerVisible: Boolean = false,
val shouldDisplayTroubleshootingBanner: Boolean = false,
val orderListState: OrderListState? = null,
val selectionCount: Int? = null
val isSearching: Boolean = false,
val selectionCount: Int? = null,
val isAddOrderButtonVisible: Boolean = true
) : Parcelable {
@IgnoredOnParcel
val isBottomNavBarVisible = !isSearching && orderListState != OrderListState.Selecting

@IgnoredOnParcel
val isFilteringActive = filterCount > 0

Expand Down
42 changes: 32 additions & 10 deletions WooCommerce/src/main/res/layout/order_list_item.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,33 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">

<ImageView
android:id="@+id/orderImageSelected"
android:layout_width="@dimen/major_250"
android:layout_height="@dimen/major_250"
android:layout_marginStart="@dimen/major_75"
android:layout_marginEnd="@dimen/major_75"
android:importantForAccessibility="no"
android:src="@drawable/product_selected_overlay"
android:visibility="gone"
tools:visibility="visible"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"/>

<androidx.constraintlayout.widget.Barrier
android:id="@+id/start_barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="end"
app:constraint_referenced_ids="orderImageSelected"/>

<com.google.android.material.textview.MaterialTextView
android:id="@+id/orderDate"
style="@style/Woo.ListItem.Caption"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintStart_toEndOf="@id/start_barrier"
app:layout_constraintTop_toTopOf="parent"
tools:text="May 11, 1967 11:30"/>

Expand All @@ -28,21 +49,20 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/minor_50"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintStart_toEndOf="@id/start_barrier"
app:layout_constraintTop_toBottomOf="@+id/orderDate"
tools:text="#51"/>

<com.google.android.material.textview.MaterialTextView
android:id="@+id/orderName"
style="@style/Woo.ListItem.Title"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/minor_100"
android:layout_marginTop="@dimen/minor_50"
app:layout_constrainedWidth="true"
app:layout_constraintEnd_toStartOf="@+id/orderTotal"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@+id/orderNum"
app:layout_constraintEnd_toStartOf="@+id/orderTotal"
app:layout_constraintTop_toBottomOf="@+id/orderDate"
tools:text="John Doe"/>

Expand All @@ -52,27 +72,29 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/minor_50"
android:layout_marginEnd="@dimen/major_100"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/orderDate"
tools:text="$100"/>

<com.woocommerce.android.widgets.FlowLayout
android:id="@+id/orderTags"
style="@style/Woo.FlowLayout"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="@+id/divider"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/orderName"/>
app:layout_constraintStart_toEndOf="@id/start_barrier"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/orderName" />

<View
style="@style/Woo.Divider"
android:id="@+id/divider"
android:layout_width="0dp"
android:layout_marginStart="@dimen/major_100"
app:layout_constraintTop_toBottomOf="@+id/orderTags"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="@+id/orderNum"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>
Expand Down

0 comments on commit 16cc17a

Please sign in to comment.