diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/DashboardContainer.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/DashboardContainer.kt index b0f33562a94..73219c6e2e5 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/DashboardContainer.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/DashboardContainer.kt @@ -5,6 +5,7 @@ import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -18,8 +19,12 @@ import androidx.compose.foundation.lazy.staggeredgrid.LazyVerticalStaggeredGrid import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells import androidx.compose.foundation.lazy.staggeredgrid.items import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.MaterialTheme import androidx.compose.material.Text +import androidx.compose.material.pullrefresh.PullRefreshIndicator +import androidx.compose.material.pullrefresh.pullRefresh +import androidx.compose.material.pullrefresh.rememberPullRefreshState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.livedata.observeAsState @@ -58,25 +63,37 @@ import com.woocommerce.android.ui.dashboard.stock.DashboardProductStockCard import com.woocommerce.android.ui.dashboard.topperformers.DashboardTopPerformersWidgetCard import com.woocommerce.android.ui.main.MainActivityViewModel +@OptIn(ExperimentalMaterialApi::class) @Composable fun DashboardContainer( mainActivityViewModel: MainActivityViewModel, dashboardViewModel: DashboardViewModel, blazeCampaignCreationDispatcher: BlazeCampaignCreationDispatcher, - windowSizeClass: WindowSizeClass + windowSizeClass: WindowSizeClass, ) { - dashboardViewModel.dashboardWidgets.observeAsState().value?.let { widgets -> - DashboardWidgets( - widgetUiModels = widgets, - mainActivityViewModel = mainActivityViewModel, - dashboardViewModel = dashboardViewModel, - blazeCampaignCreationDispatcher = blazeCampaignCreationDispatcher, - modifier = Modifier - .fillMaxSize() - .background(MaterialTheme.colors.surface) - .padding(vertical = dimensionResource(id = R.dimen.major_100)), - numberOfColumns = if (windowSizeClass != WindowSizeClass.Compact) 2 else 1 - ) + dashboardViewModel.dashboardCardsState.observeAsState().value?.let { state -> + + val pullRefreshState = rememberPullRefreshState(state.isRefreshing, dashboardViewModel::onPullToRefresh) + Box(Modifier.pullRefresh(pullRefreshState)) { + DashboardWidgets( + widgetUiModels = state.widgets, + mainActivityViewModel = mainActivityViewModel, + dashboardViewModel = dashboardViewModel, + blazeCampaignCreationDispatcher = blazeCampaignCreationDispatcher, + modifier = Modifier + .fillMaxSize() + .background(MaterialTheme.colors.surface) + .padding(vertical = dimensionResource(id = R.dimen.major_100)), + numberOfColumns = if (windowSizeClass != WindowSizeClass.Compact) 2 else 1 + ) + + PullRefreshIndicator( + refreshing = state.isRefreshing, + state = pullRefreshState, + modifier = Modifier.align(Alignment.TopCenter), + contentColor = MaterialTheme.colors.primary, + ) + } } } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/DashboardFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/DashboardFragment.kt index 7443c3ec363..a22c3ac0677 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/DashboardFragment.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/DashboardFragment.kt @@ -26,7 +26,6 @@ import com.woocommerce.android.R import com.woocommerce.android.analytics.AnalyticsEvent import com.woocommerce.android.analytics.AnalyticsTracker import com.woocommerce.android.databinding.FragmentDashboardBinding -import com.woocommerce.android.extensions.WindowSizeClass import com.woocommerce.android.extensions.getColorCompat import com.woocommerce.android.extensions.handleNotice import com.woocommerce.android.extensions.handleResult @@ -200,10 +199,10 @@ class DashboardFragment : dashboardViewModel.jetpackBenefitsBannerState.observe(viewLifecycleOwner) { jetpackBenefitsBanner -> onVisitorStatsUnavailable(jetpackBenefitsBanner) } - dashboardViewModel.dashboardWidgets.observe(viewLifecycleOwner) { widgets -> + dashboardViewModel.dashboardCardsState.observe(viewLifecycleOwner) { state -> // Show banners only if onboarding list is NOT displayed if ( - widgets.none { + state.widgets.none { (it as? DashboardWidgetUiModel.ConfigurableWidget)?.widget?.type == DashboardWidget.Type.ONBOARDING } ) { @@ -213,9 +212,6 @@ class DashboardFragment : dashboardViewModel.hasNewWidgets.observe(viewLifecycleOwner) { hasNewWidgets -> editButtonBadge.isVisible = hasNewWidgets } - dashboardViewModel.isRefreshingOnBackground.observe(viewLifecycleOwner) { isRefreshing -> - binding.myStoreRefreshLayout.isRefreshing = isRefreshing - } } private fun setupResultHandlers() { diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/DashboardViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/DashboardViewModel.kt index 0edbf70b694..8ea0eb66dc3 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/DashboardViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/dashboard/DashboardViewModel.kt @@ -105,19 +105,32 @@ class DashboardViewModel @Inject constructor( feedbackPrefs.userFeedbackIsDueObservable ) { configurableWidgets, hasNewWidgets, userFeedbackIsDue -> mapWidgetsToUiModels(configurableWidgets, hasNewWidgets, userFeedbackIsDue) - }.asLiveData() + } val hasNewWidgets = dashboardRepository.hasNewWidgets.asLiveData() private val refreshingOnBackground = MutableStateFlow(-1) val isRefreshingOnBackground = refreshingOnBackground.map { it > -1 }.asLiveData() - fun displayRefreshingIndicator() { refreshingOnBackground.value += 1 } + fun displayRefreshingIndicator() { + refreshingOnBackground.value += 1 + } + fun hideRefreshingIndicator() { val value = (refreshingOnBackground.value - 1).coerceAtLeast(-1) refreshingOnBackground.value = value } + val dashboardCardsState = combine( + dashboardWidgets, + refreshingOnBackground.map { it > -1 } + ) { widgets, isRefreshing -> + DashboardCardsState( + widgets = widgets, + isRefreshing = isRefreshing + ) + }.asLiveData() + init { ConnectionChangeReceiver.getEventBus().register(this) @@ -364,4 +377,9 @@ class DashboardViewModel @Inject constructor( action = action ) } + + data class DashboardCardsState( + val widgets: List, + val isRefreshing: Boolean + ) }