Skip to content

[Watch App] Introduce Order Details UI #11505

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
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
873b542
Introduce OrderDetailsScreen.kt
ThomazFB May 13, 2024
87099d1
Refactor Orders packages
ThomazFB May 13, 2024
a5d1eed
Define Order Details scaffolds
ThomazFB May 13, 2024
8365bd5
Refactor OrdersListViewModel to support order ID control
ThomazFB May 13, 2024
8ccc884
Wire Order item click event with ViewModel
ThomazFB May 13, 2024
0be211f
Introduce Order Details navigation route with parameters
ThomazFB May 13, 2024
14dd210
Fix lint issues
ThomazFB May 13, 2024
0770901
Introduce Order retrieval for OrderDetails
ThomazFB May 13, 2024
abfc82c
Create ParseOrderData use case
ThomazFB May 13, 2024
d053f17
Remove OrderItem usage from ViewModel and use the Use case one
ThomazFB May 13, 2024
c3a4000
Configure OrderDetailsViewModel order parsing and handling
ThomazFB May 13, 2024
3d06d08
Adjust OrderDetailsScreen to listen to Order data changes
ThomazFB May 13, 2024
d9aa510
Fix lint issues
ThomazFB May 13, 2024
9398db5
Fix navigation crashes
ThomazFB May 13, 2024
bd365e9
Improve Navigation args declaration
ThomazFB May 13, 2024
3cb6d94
Add preview and initial style adjustments to OrderDetailsScreen
ThomazFB May 13, 2024
9b1e7dd
Fix and improve Order details data fetching
ThomazFB May 13, 2024
b1a4720
Fix lint issues
ThomazFB May 13, 2024
2730a21
Add initial header structure for OrderDetailsScreen
ThomazFB May 14, 2024
4c5ae03
Add TimeText and text alignments
ThomazFB May 14, 2024
f15efc3
Centralize all format operations to the ParseOrderData
ThomazFB May 14, 2024
67af8ca
Add Text style to OrderDetailsScreen
ThomazFB May 14, 2024
e894a29
Rename ParseOrderData.kt to FormatOrderData
ThomazFB May 14, 2024
f37f78f
Add Order Details string resources
ThomazFB May 14, 2024
0499d55
Fix lint issues
ThomazFB May 14, 2024
0bb1f30
Add Order Refunds fetching support
ThomazFB May 14, 2024
ae7834d
Fix lint issues
ThomazFB May 15, 2024
fe280df
Define WearOrderProduct type
ThomazFB May 16, 2024
42a9989
Introduce GetWearableOrderProducts operation
ThomazFB May 16, 2024
5881da0
Add Order Products support to Wearable Paths
ThomazFB May 16, 2024
b1bd678
Handle Order products request from WearOS
ThomazFB May 16, 2024
b281970
Add the Order ID looping back to the OrderProducts request
ThomazFB May 16, 2024
732f614
Receive and store Order Products data from phone
ThomazFB May 16, 2024
bf30bf8
Refactor Order Products requests from WearableConnectionRepository
ThomazFB May 16, 2024
86b3730
Introduce FlowUtils.kt
ThomazFB May 16, 2024
58e4a5b
Refactor flow with timeout operations
ThomazFB May 16, 2024
5fd1247
Rename FlowUtils to FlowExt.kt
ThomazFB May 16, 2024
cee2912
Fix lint issues
ThomazFB May 16, 2024
30e39f8
Introduce FetchOrderProducts with data store observation from OrdersR…
ThomazFB May 16, 2024
f66a3e6
Improve FetchOrderProducts with dedicated Request class
ThomazFB May 16, 2024
98fff08
Refactor FetchOrders to follow FetchOrderProducts strategy
ThomazFB May 16, 2024
cb07d29
Overhaul Store stats request structure
ThomazFB May 16, 2024
8b4414c
Improve Stats fetching even further
ThomazFB May 16, 2024
d80c0c5
Enforce site cohesion inside StatsRepository
ThomazFB May 16, 2024
1345b1a
Add missing fixes for the Order Products request
ThomazFB May 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions WooCommerce-Wear/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,14 @@
android:pathPrefix="/orders-data"
android:scheme="wear" />
</intent-filter>

<intent-filter>
<action android:name="com.google.android.gms.wearable.DATA_CHANGED" />
<data
android:host="*"
android:pathPrefix="/order-products-data"
android:scheme="wear" />
</intent-filter>
</service>

<service
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.woocommerce.android.extensions

import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flow

const val TIMEOUT_MILLIS = 20000L
val timeoutFlow: Flow<Boolean>
get() = flow {
emit(false)
delay(TIMEOUT_MILLIS)
emit(true)
}

fun <T, R> Flow<T>.combineWithTimeout(
transform: (data: T, isTimeout: Boolean) -> R
) = combine(this, timeoutFlow, transform)
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import com.woocommerce.android.ui.login.LoginRepository
import com.woocommerce.android.ui.orders.OrdersRepository
import com.woocommerce.android.ui.stats.datasource.StatsRepository
import com.woocommerce.commons.wear.DataPath.ORDERS_DATA
import com.woocommerce.commons.wear.DataPath.ORDER_PRODUCTS_DATA
import com.woocommerce.commons.wear.DataPath.SITE_DATA
import com.woocommerce.commons.wear.DataPath.STATS_DATA
import com.woocommerce.commons.wear.MessagePath
Expand Down Expand Up @@ -38,6 +39,7 @@ class PhoneConnectionRepository @Inject constructor(
SITE_DATA.value -> loginRepository.receiveStoreDataFromPhone(dataMap)
STATS_DATA.value -> statsRepository.receiveStatsDataFromPhone(dataMap)
ORDERS_DATA.value -> ordersRepository.receiveOrdersDataFromPhone(dataMap)
ORDER_PRODUCTS_DATA.value -> ordersRepository.receiveOrderProductsDataFromPhone(dataMap)
else -> Log.d(TAG, "Unknown path data received")
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,21 @@ package com.woocommerce.android.ui
import androidx.compose.runtime.Composable
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavHostController
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import com.woocommerce.android.ui.NavArgs.ORDER_ID
import com.woocommerce.android.ui.NavRoutes.LOGIN
import com.woocommerce.android.ui.NavRoutes.MY_STORE
import com.woocommerce.android.ui.NavRoutes.ORDER_DETAILS
import com.woocommerce.android.ui.login.LoginScreen
import com.woocommerce.android.ui.login.LoginViewModel
import com.woocommerce.android.ui.mystore.MyStoreScreen
import com.woocommerce.android.ui.orders.OrdersListViewModel
import com.woocommerce.android.ui.orders.details.OrderDetailsScreen
import com.woocommerce.android.ui.orders.details.OrderDetailsViewModel
import com.woocommerce.android.ui.orders.list.OrdersListViewModel
import com.woocommerce.android.ui.stats.StoreStatsViewModel

@Composable
Expand Down Expand Up @@ -39,10 +45,30 @@ fun WooWearNavHost(
ordersListViewModel = ordersListViewModel
)
}
composable(
route = ORDER_DETAILS.withArgs(ORDER_ID),
arguments = listOf(navArgument(ORDER_ID.key) { type = NavType.LongType })
) {
val viewModel: OrderDetailsViewModel = hiltViewModel()
OrderDetailsScreen(viewModel)
}
}
}

enum class NavRoutes(val route: String) {
LOGIN("login"),
MY_STORE("myStore")
MY_STORE("myStore"),
ORDER_DETAILS("orderDetails");

fun withArgs(args: Any): String {
return "$route/$args"
}

fun withArgs(navArgs: NavArgs): String {
return "$route/{${navArgs.key}}"
}
}

enum class NavArgs(val key: String) {
ORDER_ID("orderId")
}
Original file line number Diff line number Diff line change
@@ -1,42 +1,26 @@
package com.woocommerce.android.ui.login

import com.woocommerce.android.extensions.combineWithTimeout
import com.woocommerce.android.ui.login.ObserveLoginRequest.LoginRequestState.Logged
import com.woocommerce.android.ui.login.ObserveLoginRequest.LoginRequestState.Timeout
import com.woocommerce.android.ui.login.ObserveLoginRequest.LoginRequestState.Waiting
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flow
import javax.inject.Inject

class ObserveLoginRequest @Inject constructor(
private val loginRepository: LoginRepository
) {
operator fun invoke() = combine(
loginRepository.isSiteAvailable,
timeoutFlow
) { isSiteAvailable, isTimeout ->
when {
isSiteAvailable -> Logged
isTimeout.not() -> Waiting
else -> Timeout
}
}

private val timeoutFlow: Flow<Boolean>
get() = flow {
emit(false)
delay(TIMEOUT_MILLIS)
emit(true)
operator fun invoke() = loginRepository.isSiteAvailable
.combineWithTimeout { isSiteAvailable, isTimeout ->
when {
isSiteAvailable -> Logged
isTimeout.not() -> Waiting
else -> Timeout
}
}

enum class LoginRequestState {
Logged,
Waiting,
Timeout
}

companion object {
const val TIMEOUT_MILLIS = 20000L
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.google.android.horologist.compose.pager.PagerScreen
import com.woocommerce.android.ui.orders.OrdersListScreen
import com.woocommerce.android.ui.orders.OrdersListViewModel
import com.woocommerce.android.ui.orders.list.OrdersListScreen
import com.woocommerce.android.ui.orders.list.OrdersListViewModel
import com.woocommerce.android.ui.stats.StoreStatsScreen
import com.woocommerce.android.ui.stats.StoreStatsViewModel

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package com.woocommerce.android.ui.orders

import android.content.Context
import android.os.Parcelable
import com.woocommerce.android.R
import com.woocommerce.android.util.DateUtils
import kotlinx.parcelize.Parcelize
import org.wordpress.android.fluxc.model.OrderEntity
import org.wordpress.android.fluxc.model.SiteModel
import org.wordpress.android.fluxc.store.WooCommerceStore
import java.util.Locale
import javax.inject.Inject

class FormatOrderData @Inject constructor(
private val context: Context,
private val wooCommerceStore: WooCommerceStore,
private val dateUtils: DateUtils,
private val locale: Locale,
) {
operator fun invoke(
selectedSite: SiteModel,
order: OrderEntity
) = order.toOrderItem(selectedSite)

operator fun invoke(
selectedSite: SiteModel,
orders: List<OrderEntity>
) = orders.map { it.toOrderItem(selectedSite) }

private fun OrderEntity.toOrderItem(
selectedSite: SiteModel
): OrderItem {
val formattedOrderTotals = wooCommerceStore.formatCurrencyForDisplay(
amount = total.toDoubleOrNull() ?: 0.0,
site = selectedSite,
currencyCode = null,
applyDecimalFormatting = true
)

val formattedCreationDate = dateUtils.getFormattedDateWithSiteTimeZone(
dateCreated
) ?: dateCreated

val formattedBillingName = takeUnless {
billingFirstName.isEmpty() && billingLastName.isEmpty()
}?.let { "$billingFirstName $billingLastName" } ?: context.getString(R.string.orders_list_guest_customer)

val formattedStatus = status.replaceFirstChar {
if (it.isLowerCase()) it.titlecase(locale) else it.toString()
}

val formattedNumber = "#$number"

return OrderItem(
id = orderId,
date = formattedCreationDate,
number = formattedNumber,
customerName = formattedBillingName,
total = formattedOrderTotals,
status = formattedStatus
)
}

@Parcelize
data class OrderItem(
val id: Long,
val date: String,
val number: String,
val customerName: String,
val total: String,
val status: String
) : Parcelable
}

This file was deleted.

Loading
Loading