Skip to content

Commit

Permalink
Merge branch 'trunk' into issue/12439-update-address-name
Browse files Browse the repository at this point in the history
  • Loading branch information
atorresveiga authored Jan 9, 2025
2 parents b3c19f0 + c2c1e55 commit 0dc326e
Show file tree
Hide file tree
Showing 18 changed files with 859 additions and 110 deletions.
3 changes: 3 additions & 0 deletions RELEASE-NOTES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
- [***] Orders: Merchants can now bulk update the status of their orders [https://github.com/woocommerce/woocommerce-android/pull/13245]
- [*] Fixed a crash on the order details [https://github.com/woocommerce/woocommerce-android/pull/13191]
- [**] Introduced fallback logic for the barcode scanner to use the front-facing camera when a back-facing camera is unavailable [https://github.com/woocommerce/woocommerce-android/pull/13230]
- [*] It possible to quickly open plugins page from the plugins list in the up to update an outdated plugin [https://github.com/woocommerce/woocommerce-android/pull/13246]
- [**] Fixed a bug when refunded items were displayed on the refund screen [https://github.com/woocommerce/woocommerce-android/pull/13212]
- [**] Enable hiding and disabling push notification for sites from site picker [https://github.com/woocommerce/woocommerce-android/issues/13107]

21.3
-----
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,10 +147,19 @@ fun List<Refund>.getNonRefundedProducts(
fun List<Refund>.getMaxRefundQuantities(
products: List<Order.Item>
): Map<Long, Float> {
val map = mutableMapOf<Long, Float>()
val groupedRefunds = this.flatMap { it.items }.groupBy { it.orderItemId }
products.map { item ->
map[item.itemId] = item.quantity - (groupedRefunds[item.itemId]?.sumOf { it.quantity } ?: 0)
}
return map
val allRefundedItems = this.flatMap { it.items }

return products.mapNotNull { product ->
val refundedQuantity = allRefundedItems
.filter { it.productId == product.productId }
.sumOf { it.quantity }

val quantityLeftToRefund = product.quantity - refundedQuantity

if (quantityLeftToRefund > 0) {
product.itemId to quantityLeftToRefund.toFloat()
} else {
null
}
}.toMap()
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@ class OrderSelectionItemKeyProvider(private val recyclerView: RecyclerView) :
}

override fun getPosition(key: Long): Int =
(recyclerView.adapter as? OrderListAdapter)?.orderIdAndPosition[key] ?: RecyclerView.NO_POSITION
(recyclerView.adapter as? OrderListAdapter)?.orderIdAndPosition?.get(key) ?: RecyclerView.NO_POSITION
}
Original file line number Diff line number Diff line change
Expand Up @@ -415,24 +415,36 @@ class OrderListFragment :
}
}
tracker?.onSaveInstanceState(outState)
viewModel.orderIdAndPositionBackup =
((binding.orderListView.ordersList.adapter as? OrderListAdapter)?.orderIdAndPosition ?: emptyMap())
as MutableMap<Long, Int>

_binding?.let { binding ->
val adapter = binding.orderListView.ordersList.adapter as? OrderListAdapter
adapter?.let {
viewModel.orderIdAndPositionBackup = it.orderIdAndPosition
}
}
}

override fun onViewStateRestored(savedInstanceState: Bundle?) {
tracker?.run {
onRestoreInstanceState(savedInstanceState)
(binding.orderListView.ordersList.adapter as? OrderListAdapter)?.orderIdAndPosition =
viewModel.orderIdAndPositionBackup
if (hasSelection()) {
setItemsSelected(selection.toList(), true)
_binding?.let { binding ->
restoreAdapterBulkSelectionState(binding)
if (hasSelection()) {
setItemsSelected(selection.toList(), true)
}
}
}

super.onViewStateRestored(savedInstanceState)
}

private fun restoreAdapterBulkSelectionState(binding: FragmentOrderListBinding) {
val adapter = binding.orderListView.ordersList.adapter as? OrderListAdapter
if (adapter != null) {
adapter.orderIdAndPosition = viewModel.orderIdAndPositionBackup
}
}

override fun onDestroyView() {
disableSearchListeners()
handler.removeCallbacksAndMessages(null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -284,8 +284,8 @@ class IssueRefundViewModel @Inject constructor(
)
}

val items = order.items.map {
val maxQuantity = maxQuantities[it.itemId] ?: 0f
val items = order.items.mapNotNull {
val maxQuantity = maxQuantities[it.itemId] ?: return@mapNotNull null
val selectedQuantity = min(selectedQuantities[it.itemId] ?: 0, maxQuantity.toInt())
ProductRefundListItem(
orderItem = it,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.woocommerce.android.ui.prefs.plugins

import com.woocommerce.android.viewmodel.MultiLiveEvent

sealed class PluginsEvent : MultiLiveEvent.Event() {
data class NavigateToPluginsWeb(val url: String) : PluginsEvent()
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import android.view.ViewGroup
import androidx.compose.ui.platform.ComposeView
import androidx.fragment.app.viewModels
import androidx.navigation.fragment.findNavController
import com.woocommerce.android.NavGraphSettingsDirections
import com.woocommerce.android.analytics.AnalyticsTracker
import com.woocommerce.android.ui.base.BaseFragment
import com.woocommerce.android.ui.compose.theme.WooThemeWithBackground
Expand Down Expand Up @@ -46,6 +47,10 @@ class PluginsFragment : BaseFragment() {
viewModel.event.observe(viewLifecycleOwner) { event ->
when (event) {
is MultiLiveEvent.Event.Exit -> findNavController().navigateUp()
is PluginsEvent.NavigateToPluginsWeb -> {
findNavController()
.navigate(NavGraphSettingsDirections.actionGlobalWPComWebViewFragment(event.url))
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import androidx.compose.animation.core.infiniteRepeatable
import androidx.compose.animation.core.rememberInfiniteTransition
import androidx.compose.animation.core.tween
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
Expand All @@ -16,16 +17,19 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.Divider
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Scaffold
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.Refresh
import androidx.compose.runtime.Composable
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.ui.Alignment
Expand All @@ -44,12 +48,12 @@ import com.woocommerce.android.extensions.isNotNullOrEmpty
import com.woocommerce.android.ui.compose.component.Toolbar
import com.woocommerce.android.ui.compose.component.WCColoredButton
import com.woocommerce.android.ui.compose.preview.LightDarkThemePreviews
import com.woocommerce.android.ui.prefs.plugins.PluginsViewModel.ViewState
import com.woocommerce.android.ui.prefs.plugins.PluginsViewModel.ViewState.Loaded.Plugin
import com.woocommerce.android.ui.prefs.plugins.PluginsViewModel.ViewState.Loaded.Plugin.PluginStatus.Inactive
import com.woocommerce.android.ui.prefs.plugins.PluginsViewModel.ViewState.Loaded.Plugin.PluginStatus.Unknown
import com.woocommerce.android.ui.prefs.plugins.PluginsViewModel.ViewState.Loaded.Plugin.PluginStatus.UpToDate
import com.woocommerce.android.ui.prefs.plugins.PluginsViewModel.ViewState.Loaded.Plugin.PluginStatus.UpdateAvailable
import com.woocommerce.android.ui.compose.theme.WooThemeWithBackground
import com.woocommerce.android.ui.prefs.plugins.PluginsViewState.Loaded.Plugin
import com.woocommerce.android.ui.prefs.plugins.PluginsViewState.Loaded.Plugin.PluginStatus.Inactive
import com.woocommerce.android.ui.prefs.plugins.PluginsViewState.Loaded.Plugin.PluginStatus.Unknown
import com.woocommerce.android.ui.prefs.plugins.PluginsViewState.Loaded.Plugin.PluginStatus.UpToDate
import com.woocommerce.android.ui.prefs.plugins.PluginsViewState.Loaded.Plugin.PluginStatus.UpdateAvailable

@Composable
fun PluginsScreen(viewModel: PluginsViewModel) {
Expand All @@ -69,34 +73,50 @@ fun PluginsScreen(viewModel: PluginsViewModel) {
.padding(paddingValues)
) {
viewModel.viewState.observeAsState().value?.let { state ->
PluginsScreen(state, viewModel::onRetryClicked)
PluginsScreen(
state = state,
onRetryTapped = viewModel::onRetryClicked,
onPluginClicked = viewModel::onPluginClicked,
)
}
}
}
}

@Composable
private fun PluginsScreen(state: ViewState, onRetryTapped: () -> Unit) {
private fun PluginsScreen(
state: PluginsViewState,
onRetryTapped: () -> Unit,
onPluginClicked: (Plugin) -> Unit,
) {
Crossfade(targetState = state, label = "") {
when (it) {
is ViewState.Loading -> {
is PluginsViewState.Loading -> {
ShimmerPluginsList()
}
is ViewState.Error -> {

is PluginsViewState.Error -> {
Error(onRetryTapped)
}
is ViewState.Loaded -> {
Plugins(it.plugins)

is PluginsViewState.Loaded -> {
Plugins(
it.plugins,
onPluginClicked,
)
}
}
}
}

@Composable
private fun Plugins(plugins: List<Plugin>) {
private fun Plugins(
plugins: List<Plugin>,
onPluginClicked: (Plugin) -> Unit
) {
LazyColumn {
items(plugins) { plugin ->
PluginItem(plugin)
PluginItem(plugin, onPluginClicked)

if (plugins.last() != plugin) {
Divider()
Expand All @@ -106,10 +126,14 @@ private fun Plugins(plugins: List<Plugin>) {
}

@Composable
private fun PluginItem(plugin: Plugin) {
private fun PluginItem(
plugin: Plugin,
onPluginClicked: (Plugin) -> Unit
) {
Row(
modifier = Modifier
.fillMaxWidth()
.clickable(onClick = { onPluginClicked(plugin) })
.padding(dimensionResource(R.dimen.major_100))
) {
Column(
Expand All @@ -133,12 +157,41 @@ private fun PluginItem(plugin: Plugin) {
)
}

if (plugin.status !is Unknown) {
Text(
when (plugin.status) {
is Inactive -> Text(
text = plugin.status.title,
color = colorResource(id = plugin.status.color),
fontWeight = FontWeight.Bold
)

is UpToDate -> Text(
text = plugin.status.title,
color = colorResource(id = plugin.status.color),
fontWeight = FontWeight.Bold
)

is UpdateAvailable -> {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
Icon(
imageVector = Icons.Default.Refresh,
contentDescription = plugin.status.title,
tint = colorResource(id = plugin.status.color),
modifier = Modifier
.size(24.dp)
)

Text(
text = plugin.status.title,
color = colorResource(id = plugin.status.color),
fontWeight = FontWeight.Bold
)
}
}

Unknown -> {}
}
}
}
Expand Down Expand Up @@ -252,33 +305,47 @@ private fun Error(onRetryTapped: () -> Unit) {
@LightDarkThemePreviews
@Composable
private fun PreviewPlugins() {
PluginsScreen(
ViewState.Loaded(
plugins = listOf(
Plugin("Plugin 1", "Automattic", "1.0", UpToDate("Up-to-date")),
Plugin("Plugin 2", null, "2.0", UpdateAvailable("Update available (4.9)")),
Plugin("Plugin 3", "Gutenberg", "3.0", Inactive("Inactive")),
Plugin("Plugin 5", "Blabla", "5.0", Unknown)
)
),
onRetryTapped = {}
)
WooThemeWithBackground {
PluginsScreen(
PluginsViewState.Loaded(
plugins = listOf(
Plugin("Plugin 1", "Automattic", "1.0", UpToDate("Up-to-date", R.color.color_info)),
Plugin(
"Plugin 2",
"Something",
"2.0",
UpdateAvailable("Update available (4.9)", R.color.color_primary)
),
Plugin("Plugin 3", "Gutenberg", "3.0", Inactive("Inactive", R.color.color_on_surface_disabled)),
Plugin("Plugin 5", "Blabla", "5.0", Unknown)
)
),
onRetryTapped = {},
onPluginClicked = {},
)
}
}

@LightDarkThemePreviews
@Composable
private fun PreviewError() {
PluginsScreen(
ViewState.Error,
onRetryTapped = {}
)
WooThemeWithBackground {
PluginsScreen(
PluginsViewState.Error,
onRetryTapped = {},
onPluginClicked = {},
)
}
}

@LightDarkThemePreviews
@Composable
private fun PreviewLoading() {
PluginsScreen(
ViewState.Loading,
onRetryTapped = {}
)
WooThemeWithBackground {
PluginsScreen(
PluginsViewState.Loading,
onRetryTapped = {},
onPluginClicked = {},
)
}
}
Loading

0 comments on commit 0dc326e

Please sign in to comment.