Skip to content

[Dynamic Dashboard] Orders card #11486

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 22 commits into from
May 16, 2024
Merged

[Dynamic Dashboard] Orders card #11486

merged 22 commits into from
May 16, 2024

Conversation

0nko
Copy link
Contributor

@0nko 0nko commented May 10, 2024

Implements #11461 and displays the Most recent orders dashboard card (without the filtering header).

To test:

  1. Start the app
  2. Notice a new Most recent orders card is shown
  3. Verify the 3 most recent orders are displayed
  4. Tap on View all orders
  5. Verify the Orders tab is shown
  6. Go back
  7. Pull to refresh
  8. Notice the card data is reloaded

@0nko 0nko added the feature: dashboard Related to home screen project label May 10, 2024
@0nko 0nko added this to the 18.7 milestone May 10, 2024
@0nko 0nko requested a review from hichamboushaba May 10, 2024 12:08
@dangermattic
Copy link
Collaborator

dangermattic commented May 10, 2024

5 Warnings
⚠️ This PR is larger than 300 lines of changes. Please consider splitting it into smaller PRs for easier and faster reviews.
⚠️ Class DashboardOrdersViewModel is missing tests, but unit-tests-exemption label was set to ignore this.
⚠️ Class ViewState is missing tests, but unit-tests-exemption label was set to ignore this.
⚠️ Class Error is missing tests, but unit-tests-exemption label was set to ignore this.
⚠️ This PR is assigned to the milestone 18.7. This milestone is due in less than 2 days.
Please make sure to get it merged by then or assign it to a milestone with a later deadline.

Generated by 🚫 Danger

@wpmobilebot
Copy link
Collaborator

Found 1 violations:

The PR caused the following dependency changes:

expand

-+--- org.wordpress:fluxc:trunk-cfe34975d20df76a2b7b4522dccd2faf53ef0f7c
-|    +--- org.wordpress:wellsql:2.0.0
-|    |    +--- androidx.annotation:annotation:1.2.0 -> 1.7.0 (*)
-|    |    \--- org.wordpress.wellsql:wellsql-annotations:2.0.0
-|    +--- org.wordpress.fluxc:fluxc-annotations:trunk-cfe34975d20df76a2b7b4522dccd2faf53ef0f7c
-|    +--- org.greenrobot:eventbus:3.3.1 (*)
-|    +--- com.squareup.okhttp3:okhttp:4.9.0 -> 4.12.0 (*)
-|    +--- com.android.volley:volley:1.1.1 -> 1.2.0
-|    +--- androidx.paging:paging-runtime:2.1.2
-|    |    +--- androidx.paging:paging-common:2.1.2
-|    |    |    +--- androidx.annotation:annotation:1.0.0 -> 1.7.0 (*)
-|    |    |    \--- androidx.arch.core:core-common:2.0.0 -> 2.2.0 (*)
-|    |    +--- androidx.arch.core:core-runtime:2.0.0 -> 2.2.0 (*)
-|    |    +--- androidx.lifecycle:lifecycle-runtime:2.0.0 -> 2.6.2 (*)
-|    |    +--- androidx.lifecycle:lifecycle-livedata:2.0.0 -> 2.6.2 (*)
-|    |    \--- androidx.recyclerview:recyclerview:1.0.0 -> 1.3.2 (*)
-|    +--- com.goterl:lazysodium-android:5.0.2
-|    +--- net.java.dev.jna:jna:5.5.0
-|    +--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.20 -> 1.9.10 (*)
-|    +--- org.jetbrains.kotlin:kotlin-android-extensions-runtime:1.6.20 -> 1.9.22
-|    |    \--- org.jetbrains.kotlin:kotlin-stdlib:1.9.22 (*)
-|    +--- androidx.appcompat:appcompat:1.0.2 -> 1.6.1 (*)
-|    +--- androidx.recyclerview:recyclerview:1.0.0 -> 1.3.2 (*)
-|    +--- androidx.exifinterface:exifinterface:1.0.0 -> 1.3.6
-|    |    \--- androidx.annotation:annotation:1.2.0 -> 1.7.0 (*)
-|    +--- androidx.security:security-crypto:1.0.0 -> 1.1.0-alpha03
-|    |    +--- androidx.annotation:annotation:1.1.0 -> 1.7.0 (*)
-|    |    +--- com.google.crypto.tink:tink-android:1.5.0
-|    |    \--- androidx.collection:collection:1.1.0 -> 1.4.0 (*)
-|    +--- com.squareup.okhttp3:okhttp-urlconnection:4.9.0
-|    |    +--- com.squareup.okhttp3:okhttp:4.9.0 -> 4.12.0 (*)
-|    |    \--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.10 -> 1.9.10 (*)
-|    +--- com.google.code.gson:gson:2.8.5 -> 2.10.1
-|    +--- org.apache.commons:commons-text:1.10.0 (*)
-|    +--- androidx.room:room-runtime:2.4.2 -> 2.5.2 (*)
-|    +--- androidx.room:room-ktx:2.4.2 -> 2.5.2
-|    |    +--- androidx.room:room-common:2.5.2 (*)
-|    |    +--- androidx.room:room-runtime:2.5.2 (*)
-|    |    +--- org.jetbrains.kotlin:kotlin-stdlib:1.7.20 -> 1.9.22 (*)
-|    |    \--- org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4 -> 1.7.3 (*)
-|    +--- com.google.dagger:dagger:2.42 -> 2.50
-|    |    \--- javax.inject:javax.inject:1
-|    +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9 -> 1.7.3 (*)
-|    \--- org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9 -> 1.7.3 (*)
++--- org.wordpress:fluxc:3008-f3ff9689a0a998537a624ce9a77b930a341bee48
+|    +--- org.wordpress:wellsql:2.0.0
+|    |    +--- androidx.annotation:annotation:1.2.0 -> 1.7.0 (*)
+|    |    \--- org.wordpress.wellsql:wellsql-annotations:2.0.0
+|    +--- org.wordpress.fluxc:fluxc-annotations:3008-f3ff9689a0a998537a624ce9a77b930a341bee48
+|    +--- org.greenrobot:eventbus:3.3.1 (*)
+|    +--- com.squareup.okhttp3:okhttp:4.9.0 -> 4.12.0 (*)
+|    +--- com.android.volley:volley:1.1.1 -> 1.2.0
+|    +--- androidx.paging:paging-runtime:2.1.2
+|    |    +--- androidx.paging:paging-common:2.1.2
+|    |    |    +--- androidx.annotation:annotation:1.0.0 -> 1.7.0 (*)
+|    |    |    \--- androidx.arch.core:core-common:2.0.0 -> 2.2.0 (*)
+|    |    +--- androidx.arch.core:core-runtime:2.0.0 -> 2.2.0 (*)
+|    |    +--- androidx.lifecycle:lifecycle-runtime:2.0.0 -> 2.6.2 (*)
+|    |    +--- androidx.lifecycle:lifecycle-livedata:2.0.0 -> 2.6.2 (*)
+|    |    \--- androidx.recyclerview:recyclerview:1.0.0 -> 1.3.2 (*)
+|    +--- com.goterl:lazysodium-android:5.0.2
+|    +--- net.java.dev.jna:jna:5.5.0
+|    +--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.20 -> 1.9.10 (*)
+|    +--- org.jetbrains.kotlin:kotlin-android-extensions-runtime:1.6.20 -> 1.9.22
+|    |    \--- org.jetbrains.kotlin:kotlin-stdlib:1.9.22 (*)
+|    +--- androidx.appcompat:appcompat:1.0.2 -> 1.6.1 (*)
+|    +--- androidx.recyclerview:recyclerview:1.0.0 -> 1.3.2 (*)
+|    +--- androidx.exifinterface:exifinterface:1.0.0 -> 1.3.6
+|    |    \--- androidx.annotation:annotation:1.2.0 -> 1.7.0 (*)
+|    +--- androidx.security:security-crypto:1.0.0 -> 1.1.0-alpha03
+|    |    +--- androidx.annotation:annotation:1.1.0 -> 1.7.0 (*)
+|    |    +--- com.google.crypto.tink:tink-android:1.5.0
+|    |    \--- androidx.collection:collection:1.1.0 -> 1.4.0 (*)
+|    +--- com.squareup.okhttp3:okhttp-urlconnection:4.9.0
+|    |    +--- com.squareup.okhttp3:okhttp:4.9.0 -> 4.12.0 (*)
+|    |    \--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.4.10 -> 1.9.10 (*)
+|    +--- com.google.code.gson:gson:2.8.5 -> 2.10.1
+|    +--- org.apache.commons:commons-text:1.10.0 (*)
+|    +--- androidx.room:room-runtime:2.4.2 -> 2.5.2 (*)
+|    +--- androidx.room:room-ktx:2.4.2 -> 2.5.2
+|    |    +--- androidx.room:room-common:2.5.2 (*)
+|    |    +--- androidx.room:room-runtime:2.5.2 (*)
+|    |    +--- org.jetbrains.kotlin:kotlin-stdlib:1.7.20 -> 1.9.22 (*)
+|    |    \--- org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4 -> 1.7.3 (*)
+|    +--- com.google.dagger:dagger:2.42 -> 2.50
+|    |    \--- javax.inject:javax.inject:1
+|    +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9 -> 1.7.3 (*)
+|    \--- org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9 -> 1.7.3 (*)
-\--- org.wordpress.fluxc.plugins:woocommerce:trunk-cfe34975d20df76a2b7b4522dccd2faf53ef0f7c
-     +--- org.wordpress:wellsql:2.0.0 (*)
-     +--- org.wordpress.fluxc:fluxc-annotations:trunk-cfe34975d20df76a2b7b4522dccd2faf53ef0f7c
-     +--- androidx.room:room-ktx:2.4.2 -> 2.5.2 (*)
-     +--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.20 -> 1.9.10 (*)
-     +--- org.wordpress:fluxc:trunk-cfe34975d20df76a2b7b4522dccd2faf53ef0f7c (*)
-     +--- com.google.code.gson:gson:2.8.5 -> 2.10.1
-     +--- com.google.dagger:dagger:2.42 -> 2.50 (*)
-     +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9 -> 1.7.3 (*)
-     +--- org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9 -> 1.7.3 (*)
-     \--- androidx.room:room-runtime:2.4.2 -> 2.5.2 (*)
+\--- org.wordpress.fluxc.plugins:woocommerce:3008-f3ff9689a0a998537a624ce9a77b930a341bee48
+     +--- org.wordpress:wellsql:2.0.0 (*)
+     +--- org.wordpress.fluxc:fluxc-annotations:3008-f3ff9689a0a998537a624ce9a77b930a341bee48
+     +--- androidx.room:room-ktx:2.4.2 -> 2.5.2 (*)
+     +--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.6.20 -> 1.9.10 (*)
+     +--- org.wordpress:fluxc:3008-f3ff9689a0a998537a624ce9a77b930a341bee48 (*)
+     +--- com.google.code.gson:gson:2.8.5 -> 2.10.1
+     +--- com.google.dagger:dagger:2.42 -> 2.50 (*)
+     +--- org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9 -> 1.7.3 (*)
+     +--- org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9 -> 1.7.3 (*)
+     \--- androidx.room:room-runtime:2.4.2 -> 2.5.2 (*)

Please review and act accordingly

@wpmobilebot
Copy link
Collaborator

wpmobilebot commented May 10, 2024

📲 You can test the changes from this Pull Request in WooCommerce Android by scanning the QR code below to install the corresponding build.

App Name WooCommerce Android
FlavorJalapeno
Build TypeDebug
Commitb9d2611
Direct Downloadwoocommerce-prototype-build-pr11486-b9d2611.apk

@hichamboushaba
Copy link
Member

@0nko before reviewing this, can you please rebase the PR on top of the branch of #11475, and use a logic similar to this commit (3594055) to make sure the card is now shown until M2 is ready?
If rebasing is not straightforward, then you can merge trunk into your branch after merging #11475.

@hichamboushaba hichamboushaba added the status: do not merge Dependent on another PR, ready for review but not ready for merge. label May 10, 2024
@hichamboushaba hichamboushaba self-assigned this May 10, 2024
@0nko 0nko force-pushed the issue/11461-orders-card branch from ddffdd6 to 146964a Compare May 10, 2024 14:35
@0nko 0nko changed the base branch from trunk to dynamic-dashboard/feature-flag-m2 May 10, 2024 14:36
@0nko
Copy link
Contributor Author

0nko commented May 10, 2024

@hichamboushaba done! Ready for a review.

Copy link
Member

@hichamboushaba hichamboushaba left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work @0nko 👏, I left some comments, mostly nit picks, but the first one is important IMO.

@@ -52,6 +52,7 @@ data class Order(
val selectedGiftCard: String?,
val giftCardDiscountedAmount: BigDecimal?,
val shippingTax: BigDecimal,
val billingName: String = ""
Copy link
Member

@hichamboushaba hichamboushaba May 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see some issues with this change to introduce billingName as a read-only property:

  • Adds to the size of the Order Parcelable, which is a minor thing, but something we should still avoid unless necessary.
  • Could lead to inconsistencies:
    1. we can update the billingAddress using copy function, which I believe we do in the Order edit screen, so when this happen, the billingName will still point to an outdated value.
    2. For someone unfamiliar with the code, they could think that updating the billingName is enough to update the customer's name, which is not the case.

As a solution, I suggest two solutions:

  1. Either use the existing function Order#getBillingName
  2. Introduce a computed property billingName that returns an empty string instead of the default value we use now, and then supply the default value in the UI code, this would be something like:
    @IgnoredOnParcel
    val billingName
        get() = getBillingName("").trim()

WDYT?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, thanks.

@@ -165,6 +166,8 @@ class DashboardFragment :

is FeedbackNegativeAction -> mainNavigationRouter?.showFeedbackSurvey()

is NavigateToOrders -> mainNavigationRouter?.showOrders()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

np, seeing you introduce the function showOrders here got me thinking on why we need it, and it turns out, we don't, the Navigation Component and the integration with the Bottom Navigation can handle this for us with a simple call to findNavController.navigate(R.id.orders), this means that if you want, you can handle this navigation event directly from the card in a HandleEvent function, and decouple it from the parent ViewModel, but it's a minor thing, feel free to ignore it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought there might be an easier way but I didn't think of this, thanks!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I followed your advice but now the bottom navigation button don't work right. I pushed the code so you could take a look while I'm trying to figure out if I can make it work, or if I need to revert the change.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I followed your advice but now the bottom navigation button don't work right. I pushed the code so you could take a look while I'm trying to figure out if I can make it work, or if I need to revert the change.

Sorry I missed this case when I suggested this solution, I found a fix after checking the code of NavigationUI.onNavDestinationSelected:

navController.navigateSafely(
                        resId = R.id.orders,
                        navOptions = navOptions {
                            popUpTo(navController.graph.findStartDestination().id) {
                                saveState = true
                            }
                        }
                    )

But I'm not sure if this is simpler than what you had before or not, I'll leave it to you to decide which solution is better.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, thank you!

@@ -122,6 +126,38 @@ class OrderListRepository @Inject constructor(
}
}

fun observeTopOrders(count: Int, statusFilter: Order.Status? = null) = flow {
emit(Result.success(emptyList()))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

np, do we need to start with an empty list? I think we can avoid the double emission by just removing the takeIf { it.isNotEmpty() } below, WDYT?

@@ -88,10 +88,9 @@ class DashboardRepository @Inject constructor(
isSelected = widget.isAdded,
status = when (type) {
DashboardWidget.Type.STATS,
DashboardWidget.Type.ORDERS,
DashboardWidget.Type.POPULAR_PRODUCTS -> statsWidgetsStatus
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice that you used the hasOrders logic to hide the card 👏, just one np point, I think it would be more clearer to rename the class and property now, saying statsWidgetStatus is not very clear.

And also we should probably let the iOS team know about this decision (unless this was done and I missed it).

Comment on lines 80 to 81
if (orders.isEmpty()) {
ViewState.Loading
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

np, coupling the empty state to loading could break easily, Do you think we can avoid it with using transformLatest? It's something I did in the reviews card and it worked well.

orders
.toList()
.takeIf { it.isNotEmpty() }
?.let { emit(Result.success(it)) }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this logic, we'll emit the cached value first even when the user pulls-to-refresh, WDYT about passing an argument isForced and skip the cache when it's true?

Base automatically changed from dynamic-dashboard/feature-flag-m2 to trunk May 10, 2024 15:56
@hichamboushaba
Copy link
Member

@0nko regarding the UI tests failure, it impacted my PR too, and it's because of a bug with the scrolling logic that I added on #11454, I attempted to improve the logic in this commit 8591359, feel free to cherry-pick it to your branch to fix the tests.

@0nko 0nko requested a review from a team as a code owner May 14, 2024 15:08
@0nko
Copy link
Contributor Author

0nko commented May 14, 2024

Thank you for the great feedback, @hichamboushaba!

@0nko
Copy link
Contributor Author

0nko commented May 14, 2024

@hichamboushaba I think I've addressed all feedback, ready for another round!

Copy link
Member

@hichamboushaba hichamboushaba left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work @0nko 🎖️, thanks for taking the time to implement my suggestions 🙏

As discussed, the UI tests are still failing, I managed to fix them on my PR #11491, so if it's OK for you, we can merge that PR first, then proceed to merging your after merging trunk, WDYT?

0nko added 3 commits May 15, 2024 15:34
# Conflicts:
#	WooCommerce/src/main/kotlin/com/woocommerce/android/model/DashboardWidget.kt
#	WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/DashboardContainer.kt
#	WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/data/DashboardDataStore.kt
#	WooCommerce/src/main/res/values/strings.xml
@0nko 0nko enabled auto-merge May 15, 2024 20:50
@codecov-commenter
Copy link

Codecov Report

Attention: Patch coverage is 0.80321% with 247 lines in your changes are missing coverage. Please review.

Project coverage is 40.49%. Comparing base (c230ce5) to head (b9d2611).
Report is 5 commits behind head on trunk.

Files Patch % Lines
...android/ui/dashboard/orders/DashboardOrdersCard.kt 0.00% 128 Missing ⚠️
...id/ui/dashboard/orders/DashboardOrdersViewModel.kt 0.00% 75 Missing ⚠️
...erce/android/ui/orders/list/OrderListRepository.kt 0.00% 26 Missing ⚠️
...om/woocommerce/android/ui/compose/component/Tag.kt 0.00% 6 Missing ⚠️
...e/android/ui/dashboard/data/DashboardRepository.kt 0.00% 6 Missing ⚠️
...ommerce/android/ui/dashboard/DashboardContainer.kt 0.00% 3 Missing ⚠️
...main/kotlin/com/woocommerce/android/model/Order.kt 0.00% 1 Missing ⚠️
...ce/android/ui/dashboard/data/DashboardDataStore.kt 0.00% 1 Missing ⚠️
...ndroid/ui/dashboard/data/ObserveSiteOrdersState.kt 0.00% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##              trunk   #11486      +/-   ##
============================================
- Coverage     40.64%   40.49%   -0.16%     
  Complexity     5181     5181              
============================================
  Files          1073     1075       +2     
  Lines         62609    62844     +235     
  Branches       8565     8610      +45     
============================================
+ Hits          25446    25447       +1     
- Misses        34875    35109     +234     
  Partials       2288     2288              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@hichamboushaba hichamboushaba removed the status: do not merge Dependent on another PR, ready for review but not ready for merge. label May 16, 2024
@0nko 0nko merged commit e4cc452 into trunk May 16, 2024
14 of 15 checks passed
@0nko 0nko deleted the issue/11461-orders-card branch May 16, 2024 08:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature: dashboard Related to home screen project unit-tests-exemption
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants