Skip to content

[Watch App] Introduce Orders fetching strategy #11471

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
29 commits
Select commit Hold shift + click to select a range
e561426
Introduce OrdersRepository type
ThomazFB May 8, 2024
17ce05c
Introduce Orders path between Phone and Watch
ThomazFB May 8, 2024
ea83051
Intercept Orders message on the Phone
ThomazFB May 8, 2024
3598ed1
Implement OrdersRepository initial fetching
ThomazFB May 9, 2024
c157079
Update OrdersListViewModel with actual order request
ThomazFB May 9, 2024
de24898
Introduce loading handling to OrdersListScreen
ThomazFB May 9, 2024
736a06e
Introduce DateUtils
ThomazFB May 9, 2024
de2bd99
Adjust TodayRangeData to use the DateUtils
ThomazFB May 9, 2024
9052e39
Remove repeated code from StatsTimeRangeData
ThomazFB May 9, 2024
9213c09
Fix TodayRangeData incorrect construction
ThomazFB May 9, 2024
400fa27
Introduce full FetchOrdersFromStore implementation
ThomazFB May 10, 2024
19ff4d4
Refactor OrdersListViewModel and OrdersRepository with new Use Case
ThomazFB May 10, 2024
53b5900
Update OrdersListScreen strings
ThomazFB May 10, 2024
800d22a
Configure Phone app to send Orders data to watch
ThomazFB May 10, 2024
80797f6
Refactor Orders fetching use case
ThomazFB May 10, 2024
f393b0b
Adjust FetchOrdersForUI to account for network availability
ThomazFB May 10, 2024
4013278
Add DataStore support for OrdersRepository
ThomazFB May 10, 2024
75621c3
Update OrdersListViewModel with flow usage from FetchOrders
ThomazFB May 10, 2024
93c559c
Introduce Order list handling from Phone
ThomazFB May 10, 2024
a3b5fe0
Refactor OrdersListViewModel to parse the Orders list data
ThomazFB May 10, 2024
7f2cfbd
Fix lint issues
ThomazFB May 10, 2024
998d618
Intercept Orders DataMap and wire it to the OrdersRepository
ThomazFB May 10, 2024
51342d3
Refactor PhoneConnectionRepository
ThomazFB May 10, 2024
bebb41a
Fix lint issues
ThomazFB May 10, 2024
2dbee19
Add missing Orders DataStore provider
ThomazFB May 10, 2024
e844a87
Adjust timeout semantics
ThomazFB May 10, 2024
cb1d237
Improve null mapping during data store observations
ThomazFB May 10, 2024
e892aac
Fix lint issues
ThomazFB May 10, 2024
d8704d2
Fix incorrect billing name validation and parsing
ThomazFB May 10, 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 @@ -47,6 +47,14 @@
android:pathPrefix="/stats-data"
android:scheme="wear" />
</intent-filter>

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

<service
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.preferencesDataStoreFile
import com.woocommerce.android.datastore.DataStoreQualifier
import com.woocommerce.android.datastore.DataStoreType.LOGIN
import com.woocommerce.android.datastore.DataStoreType.ORDERS
import com.woocommerce.android.datastore.DataStoreType.STATS
import dagger.Module
import dagger.Provides
Expand All @@ -26,9 +27,7 @@ class DataStoreModule {
appContext: Context,
@AppCoroutineScope appCoroutineScope: CoroutineScope
): DataStore<Preferences> = PreferenceDataStoreFactory.create(
produceFile = {
appContext.preferencesDataStoreFile(LOGIN.typeName)
},
produceFile = { appContext.preferencesDataStoreFile(LOGIN.typeName) },
scope = CoroutineScope(appCoroutineScope.coroutineContext + Dispatchers.IO)
)

Expand All @@ -39,9 +38,18 @@ class DataStoreModule {
appContext: Context,
@AppCoroutineScope appCoroutineScope: CoroutineScope
): DataStore<Preferences> = PreferenceDataStoreFactory.create(
produceFile = {
appContext.preferencesDataStoreFile(STATS.typeName)
},
produceFile = { appContext.preferencesDataStoreFile(STATS.typeName) },
scope = CoroutineScope(appCoroutineScope.coroutineContext + Dispatchers.IO)
)

@Provides
@Singleton
@DataStoreQualifier(ORDERS)
fun provideOrdersDataStore(
appContext: Context,
@AppCoroutineScope appCoroutineScope: CoroutineScope
): DataStore<Preferences> = PreferenceDataStoreFactory.create(
produceFile = { appContext.preferencesDataStoreFile(ORDERS.typeName) },
scope = CoroutineScope(appCoroutineScope.coroutineContext + Dispatchers.IO)
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@ import com.google.android.gms.wearable.DataItem
import com.google.android.gms.wearable.DataMapItem
import com.google.android.gms.wearable.MessageClient
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
import com.woocommerce.commons.wear.DataPath.ORDERS_DATA
import com.woocommerce.commons.wear.DataPath.SITE_DATA
import com.woocommerce.commons.wear.DataPath.STATS_DATA
import com.woocommerce.commons.wear.MessagePath
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.async
Expand All @@ -19,17 +22,24 @@ import javax.inject.Inject
class PhoneConnectionRepository @Inject constructor(
private val loginRepository: LoginRepository,
private val statsRepository: StatsRepository,
private val ordersRepository: OrdersRepository,
private val capabilityClient: CapabilityClient,
private val messageClient: MessageClient,
private val coroutineScope: CoroutineScope
) {
suspend fun isPhoneConnectionAvailable() = fetchReachableNodes().isNotEmpty()

fun handleReceivedData(dataItem: DataItem) {
when (dataItem.uri.path) {
DataPath.SITE_DATA.value -> handleAuthenticationData(dataItem)
DataPath.STATS_DATA.value -> handleStoreStatsData(dataItem)
else -> Log.d(TAG, "Unknown path data received")
val dataPath = dataItem.uri.path
val dataMap = DataMapItem.fromDataItem(dataItem).dataMap

coroutineScope.launch {
when (dataPath) {
SITE_DATA.value -> loginRepository.receiveStoreDataFromPhone(dataMap)
STATS_DATA.value -> statsRepository.receiveStatsDataFromPhone(dataMap)
ORDERS_DATA.value -> ordersRepository.receiveOrdersDataFromPhone(dataMap)
else -> Log.d(TAG, "Unknown path data received")
}
}
}

Expand All @@ -52,16 +62,6 @@ class PhoneConnectionRepository @Inject constructor(
WOO_MOBILE_CAPABILITY in capabilityInfo
}.map { (node, _) -> node }

private fun handleAuthenticationData(dataItem: DataItem) {
val dataMap = DataMapItem.fromDataItem(dataItem).dataMap
coroutineScope.launch { loginRepository.receiveStoreDataFromPhone(dataMap) }
}

private fun handleStoreStatsData(dataItem: DataItem) {
val dataMap = DataMapItem.fromDataItem(dataItem).dataMap
coroutineScope.launch { statsRepository.receiveStatsDataFromPhone(dataMap) }
}

companion object {
const val TAG = "PhoneConnectionRepository"
const val WOO_MOBILE_CAPABILITY = "woo_mobile"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.woocommerce.android.ui.orders

import com.woocommerce.android.phone.PhoneConnectionRepository
import com.woocommerce.android.system.NetworkStatus
import com.woocommerce.android.ui.login.ObserveLoginRequest.Companion.TIMEOUT_MILLIS
import com.woocommerce.commons.wear.MessagePath.REQUEST_ORDERS
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flow
import org.wordpress.android.fluxc.model.OrderEntity
import org.wordpress.android.fluxc.model.SiteModel
import org.wordpress.android.fluxc.store.WCOrderStore.OrdersForWearablesResult.Success
import javax.inject.Inject

class FetchOrders @Inject constructor(
private val phoneRepository: PhoneConnectionRepository,
private val ordersRepository: OrdersRepository,
private val networkStatus: NetworkStatus,
) {
suspend operator fun invoke(
selectedSite: SiteModel
): Flow<List<OrderEntity>> = combine(
selectOrdersDataSource(selectedSite),
timeoutFlow
) { orders, isTimeout ->
when {
orders.isNotEmpty() -> orders
isTimeout -> emptyList()
else -> null
}
}.filterNotNull()

private suspend fun selectOrdersDataSource(
selectedSite: SiteModel
): Flow<List<OrderEntity>> {
return if (networkStatus.isConnected()) {
flow {
when (val result = ordersRepository.fetchOrders(selectedSite)) {
is Success -> result.orders
else -> emptyList()
}.let { emit(it) }
}
} else {
phoneRepository.sendMessage(REQUEST_ORDERS)
return ordersRepository.observeOrdersDataChanges()
}
}

private val timeoutFlow: Flow<Boolean>
get() = flow {
emit(false)
delay(TIMEOUT_MILLIS)
emit(true)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import androidx.wear.compose.foundation.lazy.items
import androidx.wear.compose.foundation.lazy.rememberScalingLazyListState
import androidx.wear.tooling.preview.devices.WearDevices
import com.woocommerce.android.R
import com.woocommerce.android.presentation.component.LoadingScreen
import com.woocommerce.android.presentation.theme.WooColors
import com.woocommerce.android.presentation.theme.WooTheme
import com.woocommerce.android.presentation.theme.WooTypography
Expand All @@ -37,12 +38,14 @@ import com.woocommerce.android.ui.orders.OrdersListViewModel.OrderItem
fun OrdersListScreen(viewModel: OrdersListViewModel) {
val viewState by viewModel.viewState.observeAsState()
OrdersListScreen(
isLoading = viewState?.isLoading ?: false,
orders = viewState?.orders.orEmpty()
)
}

@Composable
fun OrdersListScreen(
isLoading: Boolean,
orders: List<OrderItem>,
modifier: Modifier = Modifier
) {
Expand All @@ -64,25 +67,37 @@ fun OrdersListScreen(
.fillMaxWidth()
.padding(top = 6.dp)
)
ScalingLazyColumn(
modifier = Modifier.fillMaxSize(),
autoCentering = AutoCenteringParams(itemIndex = 0),
state = rememberScalingLazyListState(
initialCenterItemIndex = 0
)
) {
items(orders) {
OrderListItem(
modifier = modifier,
order = it
)
}
if (isLoading) {
LoadingScreen()
} else {
OrdersLazyColumn(orders, modifier)
}
}
}
}
}

@Composable
private fun OrdersLazyColumn(
orders: List<OrderItem>,
modifier: Modifier
) {
ScalingLazyColumn(
modifier = Modifier.fillMaxSize(),
autoCentering = AutoCenteringParams(itemIndex = 0),
state = rememberScalingLazyListState(
initialCenterItemIndex = 0
)
) {
items(orders) {
OrderListItem(
modifier = modifier,
order = it
)
}
}
}

@Composable
fun OrderListItem(
modifier: Modifier,
Expand All @@ -105,12 +120,14 @@ fun OrderListItem(
color = WooColors.woo_purple_20
)
Text(
text = order.number,
text = "#${order.number}",
color = WooColors.woo_gray_alpha
)
}
Text(
text = order.customerName,
text = order.customerName
?.takeIf { it.isNotEmpty() }
?: stringResource(id = R.string.orders_list_guest_customer),
style = WooTypography.body1,
color = Color.White,
textAlign = TextAlign.Start,
Expand Down Expand Up @@ -142,6 +159,7 @@ fun OrderListItem(
@Composable
fun Preview() {
OrdersListScreen(
isLoading = false,
orders = listOf(
OrderItem(
date = "25 Feb",
Expand Down
Loading
Loading