-
Notifications
You must be signed in to change notification settings - Fork 136
[POS Historical Orders] Pull to Refresh #14578
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
Changes from 19 commits
a890474
cc911fc
73ba760
313a64c
b0f9a13
3cabebf
0df5676
ba95772
9713b6d
b70396c
020078a
48dc8e5
e4d24ab
3292780
4340083
6c9aa20
a5378e8
73bec2d
2894ae4
7cf9f93
ac705b3
6f414cc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,7 @@ import androidx.activity.compose.BackHandler | |
| import androidx.compose.foundation.background | ||
| import androidx.compose.foundation.clickable | ||
| import androidx.compose.foundation.layout.Arrangement | ||
| import androidx.compose.foundation.layout.Box | ||
| import androidx.compose.foundation.layout.Column | ||
| import androidx.compose.foundation.layout.PaddingValues | ||
| import androidx.compose.foundation.layout.Row | ||
|
|
@@ -14,6 +15,10 @@ import androidx.compose.foundation.layout.fillMaxWidth | |
| import androidx.compose.foundation.layout.padding | ||
| import androidx.compose.foundation.lazy.LazyColumn | ||
| import androidx.compose.foundation.lazy.items | ||
| import androidx.compose.material.ExperimentalMaterialApi | ||
| import androidx.compose.material.pullrefresh.PullRefreshIndicator | ||
| import androidx.compose.material.pullrefresh.pullRefresh | ||
| import androidx.compose.material.pullrefresh.rememberPullRefreshState | ||
| import androidx.compose.material3.MaterialTheme | ||
| import androidx.compose.runtime.Composable | ||
| import androidx.compose.runtime.collectAsState | ||
|
|
@@ -27,16 +32,16 @@ import androidx.compose.ui.semantics.semantics | |
| import androidx.compose.ui.text.font.FontWeight | ||
| import androidx.hilt.navigation.compose.hiltViewModel | ||
| import com.woocommerce.android.R | ||
| import com.woocommerce.android.extensions.formatToDDMMMYYYY | ||
| import com.woocommerce.android.model.Order | ||
| import com.woocommerce.android.ui.woopos.common.composeui.WooPosPreview | ||
| import com.woocommerce.android.ui.woopos.common.composeui.component.WooPosText | ||
| import com.woocommerce.android.ui.woopos.common.composeui.component.WooPosToolbar | ||
| import com.woocommerce.android.ui.woopos.common.composeui.designsystem.WooPosSpacing | ||
| import com.woocommerce.android.ui.woopos.common.composeui.designsystem.WooPosTheme | ||
| import com.woocommerce.android.ui.woopos.common.composeui.designsystem.WooPosTypography | ||
| import com.woocommerce.android.ui.woopos.home.items.WooPosPullToRefreshState | ||
| import com.woocommerce.android.ui.woopos.root.navigation.WooPosNavigationEvent | ||
|
|
||
| @OptIn(ExperimentalMaterialApi::class) | ||
| @Composable | ||
| fun WooPosOrdersScreen( | ||
| onNavigationEvent: (WooPosNavigationEvent) -> Unit, | ||
|
|
@@ -48,19 +53,59 @@ fun WooPosOrdersScreen( | |
| BackHandler { onNavigationEvent(WooPosNavigationEvent.GoBack) } | ||
|
|
||
| Row(modifier = Modifier.fillMaxSize()) { | ||
| Column( | ||
| WooPosOrdersLeftPane( | ||
| state = state, | ||
| onBackClicked = onBackClicked, | ||
| onRefresh = viewModel::refresh, | ||
| isRefreshing = viewModel.isRefreshing(), | ||
|
||
| onOrderSelected = viewModel::onOrderSelected, | ||
| modifier = Modifier | ||
| .weight(0.3f) | ||
| .fillMaxHeight() | ||
| .background(MaterialTheme.colorScheme.surface) | ||
| ) { | ||
| WooPosToolbar( | ||
| titleText = stringResource(R.string.woopos_orders_title), | ||
| onBackClicked = onBackClicked, | ||
| ) | ||
| ) | ||
|
|
||
| WooPosOrdersRightPane( | ||
| state = state, | ||
| modifier = Modifier | ||
| .weight(0.7f) | ||
| .fillMaxHeight() | ||
| .background(MaterialTheme.colorScheme.surfaceContainerLow) | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| @OptIn(ExperimentalMaterialApi::class) | ||
| @Composable | ||
| private fun WooPosOrdersLeftPane( | ||
| state: WooPosOrdersState, | ||
| onBackClicked: () -> Unit, | ||
| onRefresh: () -> Unit, | ||
| isRefreshing: Boolean, | ||
| onOrderSelected: (Long) -> Unit, | ||
| modifier: Modifier = Modifier | ||
| ) { | ||
| Column(modifier = modifier) { | ||
| WooPosToolbar( | ||
| titleText = stringResource(R.string.woopos_orders_title), | ||
| onBackClicked = onBackClicked, | ||
| ) | ||
|
|
||
| val pullRefreshState = rememberPullRefreshState( | ||
| refreshing = isRefreshing, | ||
| onRefresh = onRefresh | ||
| ) | ||
|
|
||
| when { | ||
| state.isLoading -> { | ||
| Box( | ||
| modifier = Modifier | ||
| .fillMaxSize() | ||
| .pullRefresh( | ||
| pullRefreshState, | ||
| enabled = state.pullToRefreshState != WooPosPullToRefreshState.Disabled | ||
| ) | ||
| ) { | ||
| when (state) { | ||
| is WooPosOrdersState.Loading -> { | ||
| Column( | ||
| modifier = Modifier.fillMaxSize(), | ||
| horizontalAlignment = Alignment.CenterHorizontally | ||
|
|
@@ -73,56 +118,77 @@ fun WooPosOrdersScreen( | |
| ) | ||
| } | ||
| } | ||
| state.error != null -> { | ||
|
|
||
| is WooPosOrdersState.Error -> { | ||
| Column( | ||
| modifier = Modifier.fillMaxSize(), | ||
| horizontalAlignment = Alignment.CenterHorizontally | ||
| ) { | ||
| WooPosText( | ||
| text = state.error ?: stringResource(R.string.error_generic), | ||
| text = state.message, | ||
| style = WooPosTypography.BodyMedium, | ||
| color = MaterialTheme.colorScheme.error, | ||
| modifier = Modifier.padding(WooPosSpacing.Large.value) | ||
| ) | ||
| } | ||
| } | ||
| state.orders.isEmpty() -> { | ||
|
|
||
| is WooPosOrdersState.Empty -> { | ||
| Column( | ||
| modifier = Modifier.fillMaxSize(), | ||
| horizontalAlignment = Alignment.CenterHorizontally | ||
| ) { | ||
| WooPosText( | ||
| text = "No Orders Found", | ||
| text = "No orders found", | ||
| style = WooPosTypography.BodyMedium, | ||
| color = MaterialTheme.colorScheme.onSurfaceVariant, | ||
| modifier = Modifier.padding(WooPosSpacing.Large.value) | ||
| ) | ||
| } | ||
| } | ||
| else -> { | ||
|
|
||
| is WooPosOrdersState.Content -> { | ||
| WooPosOrdersListPaneScreen( | ||
| orders = state.orders, | ||
| items = state.items, | ||
| selectedOrderId = state.selectedOrderId, | ||
| onOrderSelected = viewModel::onOrderSelected, | ||
| onOrderSelected = onOrderSelected, | ||
| modifier = Modifier.fillMaxSize() | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| PullRefreshIndicator( | ||
| refreshing = isRefreshing, | ||
| state = pullRefreshState, | ||
| modifier = Modifier | ||
| .align(Alignment.TopCenter) | ||
| .padding(top = WooPosSpacing.XSmall.value), | ||
| backgroundColor = MaterialTheme.colorScheme.surface, | ||
| contentColor = MaterialTheme.colorScheme.primary | ||
| ) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| WooPosOrdersDetailPaneScreen( | ||
| order = state.selectedOrder, | ||
| modifier = Modifier | ||
| .weight(0.7f) | ||
| .fillMaxHeight() | ||
| .background(MaterialTheme.colorScheme.surfaceContainerLow) | ||
| ) | ||
| @Composable | ||
| private fun WooPosOrdersRightPane( | ||
| state: WooPosOrdersState, | ||
| modifier: Modifier = Modifier | ||
| ) { | ||
| val selectedItem: OrderItemViewState? = when (state) { | ||
| is WooPosOrdersState.Content -> state.items.firstOrNull { it.id == state.selectedOrderId } | ||
| else -> null | ||
| } | ||
|
|
||
| WooPosOrdersDetailPaneScreen( | ||
| selected = selectedItem, | ||
| modifier = modifier.fillMaxSize() | ||
| ) | ||
| } | ||
|
|
||
| @Composable | ||
| fun WooPosOrdersListPaneScreen( | ||
| orders: List<Order>, | ||
| items: List<OrderItemViewState>, | ||
| selectedOrderId: Long?, | ||
| onOrderSelected: (Long) -> Unit, | ||
| modifier: Modifier = Modifier | ||
|
|
@@ -131,14 +197,13 @@ fun WooPosOrdersListPaneScreen( | |
| modifier = modifier, | ||
| contentPadding = PaddingValues(vertical = WooPosSpacing.XSmall.value) | ||
| ) { | ||
| items(orders, key = { it.id }) { order -> | ||
| val isSelected = order.id == selectedOrderId | ||
| items(items, key = { it.id }) { item -> | ||
| val isSelected = item.id == selectedOrderId | ||
| val background = if (isSelected) { | ||
| MaterialTheme.colorScheme.primaryContainer | ||
| } else { | ||
| MaterialTheme.colorScheme.surface | ||
| } | ||
|
|
||
| val foreground = if (isSelected) { | ||
| MaterialTheme.colorScheme.onPrimaryContainer | ||
| } else { | ||
|
|
@@ -150,32 +215,21 @@ fun WooPosOrdersListPaneScreen( | |
| .fillMaxWidth() | ||
| .clip(MaterialTheme.shapes.medium) | ||
| .background(background) | ||
| .clickable { onOrderSelected(order.id) } | ||
| .clickable { onOrderSelected(item.id) } | ||
| .semantics { selected = isSelected } | ||
| .padding( | ||
| horizontal = WooPosSpacing.Medium.value, | ||
| vertical = WooPosSpacing.Medium.value | ||
| ), | ||
| verticalAlignment = Alignment.Top | ||
| ) { | ||
| Column( | ||
| verticalArrangement = Arrangement.spacedBy(WooPosSpacing.XSmall.value) | ||
| ) { | ||
| WooPosText( | ||
| "Order #${order.number}", | ||
| style = WooPosTypography.BodyMedium | ||
| ) | ||
| WooPosText( | ||
| text = order.dateCreated.formatToDDMMMYYYY(), | ||
| style = WooPosTypography.BodySmall, | ||
| color = foreground | ||
| ) | ||
| Column(verticalArrangement = Arrangement.spacedBy(WooPosSpacing.XSmall.value)) { | ||
| WooPosText(item.title, style = WooPosTypography.BodyMedium, color = foreground) | ||
| WooPosText(item.date, style = WooPosTypography.BodySmall, color = foreground) | ||
| } | ||
|
|
||
| Spacer(Modifier.weight(1f)) | ||
|
|
||
| WooPosText( | ||
| text = "${order.total} ${order.currency}", | ||
| text = item.total, | ||
| style = WooPosTypography.BodyMedium, | ||
| modifier = Modifier.alignByBaseline() | ||
| ) | ||
|
|
@@ -186,29 +240,22 @@ fun WooPosOrdersListPaneScreen( | |
|
|
||
| @Composable | ||
| fun WooPosOrdersDetailPaneScreen( | ||
| order: Order?, | ||
| selected: OrderItemViewState?, | ||
| modifier: Modifier = Modifier | ||
| ) { | ||
| Column( | ||
| modifier = modifier.fillMaxSize() | ||
| ) { | ||
| Column(modifier = modifier.fillMaxSize()) { | ||
| WooPosToolbar( | ||
| modifier = Modifier | ||
| .fillMaxWidth(), | ||
| titleText = "Order #${order?.number ?: "--"}", | ||
| modifier = Modifier.fillMaxWidth(), | ||
| titleText = selected?.title ?: "--", | ||
| titleFontWeight = FontWeight.Bold | ||
| ) | ||
|
|
||
| Column( | ||
| modifier = Modifier | ||
| .fillMaxSize() | ||
| .padding( | ||
| start = WooPosSpacing.Large.value, | ||
| end = WooPosSpacing.Large.value, | ||
| ) | ||
| .padding(start = WooPosSpacing.Large.value, end = WooPosSpacing.Large.value) | ||
| ) { | ||
| WooPosText( | ||
| text = "Orders details will be displayed here", | ||
| text = "Order details goes here", | ||
| style = WooPosTypography.BodyMedium, | ||
| color = MaterialTheme.colorScheme.onSurfaceVariant | ||
| ) | ||
|
|
@@ -219,9 +266,5 @@ fun WooPosOrdersDetailPaneScreen( | |
| @WooPosPreview | ||
| @Composable | ||
| fun WooPosOrdersScreenPreview() { | ||
| WooPosTheme { | ||
| WooPosOrdersScreen( | ||
| onNavigationEvent = {} | ||
| ) | ||
| } | ||
| WooPosTheme { WooPosOrdersScreen(onNavigationEvent = {}) } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,13 +1,44 @@ | ||
| package com.woocommerce.android.ui.woopos.orders | ||
|
|
||
| import com.woocommerce.android.model.Order | ||
|
|
||
| data class WooPosOrdersState( | ||
| val orders: List<Order> = emptyList(), | ||
| val selectedOrderId: Long? = null, | ||
| val isLoading: Boolean = false, | ||
| val error: String? = null | ||
| ) { | ||
| val selectedOrder: Order? | ||
| get() = selectedOrderId?.let { id -> orders.firstOrNull { it.id == id } } | ||
| import androidx.compose.runtime.Immutable | ||
| import com.woocommerce.android.ui.woopos.home.items.WooPosPaginationState | ||
| import com.woocommerce.android.ui.woopos.home.items.WooPosPullToRefreshState | ||
|
|
||
| @Immutable | ||
| data class OrderItemViewState( | ||
| val id: Long, | ||
| val title: String, | ||
| val date: String, | ||
| val total: String, | ||
| val isSelected: Boolean | ||
| ) | ||
|
|
||
| @Immutable | ||
| sealed class WooPosOrdersState { | ||
| abstract val pullToRefreshState: WooPosPullToRefreshState | ||
|
|
||
| @Immutable | ||
| data class Content( | ||
| val items: List<OrderItemViewState>, | ||
| override val pullToRefreshState: WooPosPullToRefreshState, | ||
| val paginationState: WooPosPaginationState, | ||
| val selectedOrderId: Long? | ||
| ) : WooPosOrdersState() | ||
|
|
||
| @Immutable | ||
| data class Error( | ||
| val message: String, | ||
| override val pullToRefreshState: WooPosPullToRefreshState = WooPosPullToRefreshState.Disabled, | ||
| ) : WooPosOrdersState() | ||
|
|
||
| @Immutable | ||
| data object Loading : WooPosOrdersState() { | ||
| override val pullToRefreshState: WooPosPullToRefreshState = WooPosPullToRefreshState.Disabled | ||
| } | ||
|
|
||
| @Immutable | ||
| data class Empty( | ||
| override val pullToRefreshState: WooPosPullToRefreshState = | ||
| WooPosPullToRefreshState.Enabled | ||
| ) : WooPosOrdersState() | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not related to the PR: on line 55 there is still

?:applied to a non-nullable typeThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True, removed in 3292780