diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsModule.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsModule.kt index 7b0b5eb00363..8cd5655c4c2b 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsModule.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsModule.kt @@ -205,7 +205,7 @@ class StatsModule { mainDispatcher, statsSiteProvider, useCases, - { statsStore.getInsights() }, + { statsStore.getInsights(statsSiteProvider.siteModel) }, uiModelMapper::mapInsights ) } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/BaseListUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/BaseListUseCase.kt index 1aa8953f6862..4cddd61baf0f 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/BaseListUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/BaseListUseCase.kt @@ -34,8 +34,8 @@ class BaseListUseCase( useCases.associateBy { it.type }.mapValues { entry -> entry.value.liveData } ) private val statsTypes = DistinctMutableLiveData>(listOf()) - val data: MediatorLiveData = mergeNotNull(statsTypes, blockListData) { insights, map -> - insights.mapNotNull { + val data: MediatorLiveData = mergeNotNull(statsTypes, blockListData) { types, map -> + types.mapNotNull { if (map.containsKey(it)) { map[it] } else { @@ -69,16 +69,26 @@ class BaseListUseCase( loadData(true, forced) } + suspend fun refreshTypes(): List { + val items = getStatsTypes() + withContext(mainDispatcher) { + statsTypes.value = items + } + return items + } + private suspend fun loadData(refresh: Boolean, forced: Boolean) { if (statsSiteProvider.hasLoadedSite()) { withContext(bgDispatcher) { if (PackageUtils.isDebugBuild() && useCases.distinctBy { it.type }.size < useCases.size) { throw RuntimeException("Duplicate stats type in a use case") } - useCases.forEach { block -> launch { block.fetch(refresh, forced) } } - val items = getStatsTypes() - withContext(mainDispatcher) { - statsTypes.value = items + val visibleTypes = refreshTypes() + visibleTypes.forEach { type -> + useCases.find { it.type == type } + ?.let { block -> launch(bgDispatcher) { + block.fetch(refresh, forced) } + } } } } else { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/StatsBlockDiffCallback.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/StatsBlockDiffCallback.kt index 16c949f8c8fc..af10b9d3118d 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/StatsBlockDiffCallback.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/StatsBlockDiffCallback.kt @@ -1,7 +1,6 @@ package org.wordpress.android.ui.stats.refresh.lists import android.support.v7.util.DiffUtil.Callback -import org.wordpress.android.ui.stats.refresh.lists.StatsBlock.Success class StatsBlockDiffCallback( private val oldList: List, @@ -30,7 +29,7 @@ class StatsBlockDiffCallback( override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Any? { val newItem = newList[newItemPosition] val oldItem = oldList[oldItemPosition] - if (oldItem is Success && newItem is Success) { + if (oldItem.type == newItem.type) { return Payload } return null diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/StatsListFragment.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/StatsListFragment.kt index 98d834da8f17..cb7cf3c9579d 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/StatsListFragment.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/StatsListFragment.kt @@ -183,6 +183,11 @@ class StatsListFragment : DaggerFragment() { viewModel.listSelected.observe(this, Observer { viewModel.onListSelected() }) + + viewModel.typeMoved?.observeEvent(this) { + viewModel.onTypeMoved() + true + } } private fun updateInsights(statsState: List) { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/StatsListViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/StatsListViewModel.kt index 7d60649c1685..2da9d58940d5 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/StatsListViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/StatsListViewModel.kt @@ -22,6 +22,7 @@ import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.StatsSect import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.StatsSection.MONTHS import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.StatsSection.WEEKS import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.StatsSection.YEARS +import org.wordpress.android.ui.stats.refresh.utils.ItemPopupMenuHandler import org.wordpress.android.ui.stats.refresh.utils.StatsDateSelector import org.wordpress.android.util.analytics.AnalyticsTrackerWrapper import org.wordpress.android.util.mapNullable @@ -36,7 +37,8 @@ abstract class StatsListViewModel( defaultDispatcher: CoroutineDispatcher, private val statsUseCase: BaseListUseCase, private val analyticsTracker: AnalyticsTrackerWrapper, - private val dateSelector: StatsDateSelector + private val dateSelector: StatsDateSelector, + private val popupMenuHandler: ItemPopupMenuHandler? = null ) : ScopedViewModel(defaultDispatcher) { private var trackJob: Job? = null private var isInitialized = false @@ -62,6 +64,8 @@ abstract class StatsListViewModel( it ?: DateSelectorUiModel(false) } + val typeMoved = popupMenuHandler?.typeMoved + override fun onCleared() { statsUseCase.onCleared() super.onCleared() @@ -119,6 +123,12 @@ abstract class StatsListViewModel( data class Success(val data: List) : UiModel() class Error(val message: Int = R.string.stats_loading_error) : UiModel() } + + fun onTypeMoved() { + launch { + statsUseCase.refreshTypes() + } + } } class InsightsListViewModel @@ -126,8 +136,15 @@ class InsightsListViewModel @Named(UI_THREAD) mainDispatcher: CoroutineDispatcher, @Named(INSIGHTS_USE_CASE) private val insightsUseCase: BaseListUseCase, analyticsTracker: AnalyticsTrackerWrapper, - dateSelectorFactory: StatsDateSelector.Factory -) : StatsListViewModel(mainDispatcher, insightsUseCase, analyticsTracker, dateSelectorFactory.build(INSIGHTS)) + dateSelectorFactory: StatsDateSelector.Factory, + popupMenuHandler: ItemPopupMenuHandler +) : StatsListViewModel( + mainDispatcher, + insightsUseCase, + analyticsTracker, + dateSelectorFactory.build(INSIGHTS), + popupMenuHandler +) class YearsListViewModel @Inject constructor( @Named(UI_THREAD) mainDispatcher: CoroutineDispatcher, diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/BlockListAdapter.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/BlockListAdapter.kt index b709a1fa71c6..9f5d045d855d 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/BlockListAdapter.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/BlockListAdapter.kt @@ -20,8 +20,8 @@ import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.ListI import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.ListItemWithIcon import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.LoadingItem import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.MapItem -import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.ReferredItem import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.QuickScanItem +import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.ReferredItem import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.TabsItem import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Text import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Title @@ -39,8 +39,8 @@ import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Type. import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Type.LIST_ITEM_WITH_ICON import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Type.LOADING_ITEM import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Type.MAP -import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Type.REFERRED_ITEM import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Type.QUICK_SCAN_ITEM +import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Type.REFERRED_ITEM import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Type.TABS import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Type.TEXT import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Type.TITLE @@ -62,8 +62,8 @@ import org.wordpress.android.ui.stats.refresh.lists.sections.viewholders.ListIte import org.wordpress.android.ui.stats.refresh.lists.sections.viewholders.ListItemWithIconViewHolder import org.wordpress.android.ui.stats.refresh.lists.sections.viewholders.LoadingItemViewHolder import org.wordpress.android.ui.stats.refresh.lists.sections.viewholders.MapViewHolder -import org.wordpress.android.ui.stats.refresh.lists.sections.viewholders.ReferredItemViewHolder import org.wordpress.android.ui.stats.refresh.lists.sections.viewholders.QuickScanItemViewHolder +import org.wordpress.android.ui.stats.refresh.lists.sections.viewholders.ReferredItemViewHolder import org.wordpress.android.ui.stats.refresh.lists.sections.viewholders.TabsViewHolder import org.wordpress.android.ui.stats.refresh.lists.sections.viewholders.TextViewHolder import org.wordpress.android.ui.stats.refresh.lists.sections.viewholders.TitleViewHolder diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/BlockListItem.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/BlockListItem.kt index 4e2c0cd1ee06..706b5c25088c 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/BlockListItem.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/BlockListItem.kt @@ -2,6 +2,7 @@ package org.wordpress.android.ui.stats.refresh.lists.sections import android.support.annotation.DrawableRes import android.support.annotation.StringRes +import android.view.View import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.ListItemWithIcon.IconStyle.NORMAL import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Type.ACTIVITY_ITEM import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Type.BAR_CHART @@ -54,7 +55,11 @@ sealed class BlockListItem(val type: Type) { QUICK_SCAN_ITEM } - data class Title(@StringRes val textResource: Int? = null, val text: String? = null) : BlockListItem(TITLE) + data class Title( + @StringRes val textResource: Int? = null, + val text: String? = null, + val menuAction: ((View) -> Unit)? = null + ) : BlockListItem(TITLE) data class ReferredItem(@StringRes val label: Int, val itemTitle: String) : BlockListItem(REFERRED_ITEM) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/AllTimeStatsUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/AllTimeStatsUseCase.kt index 32c7efe6e7ea..d865684bf660 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/AllTimeStatsUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/AllTimeStatsUseCase.kt @@ -1,5 +1,6 @@ package org.wordpress.android.ui.stats.refresh.lists.sections.insights.usecases +import android.view.View import kotlinx.coroutines.CoroutineDispatcher import org.wordpress.android.R import org.wordpress.android.R.string @@ -13,6 +14,7 @@ import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Empty import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.QuickScanItem import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.QuickScanItem.Column import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Title +import org.wordpress.android.ui.stats.refresh.utils.ItemPopupMenuHandler import org.wordpress.android.ui.stats.refresh.utils.StatsDateFormatter import org.wordpress.android.ui.stats.refresh.utils.StatsSiteProvider import org.wordpress.android.ui.stats.refresh.utils.toFormattedString @@ -24,10 +26,15 @@ class AllTimeStatsUseCase @Named(UI_THREAD) private val mainDispatcher: CoroutineDispatcher, private val allTimeStore: AllTimeInsightsStore, private val statsSiteProvider: StatsSiteProvider, - private val statsDateFormatter: StatsDateFormatter + private val statsDateFormatter: StatsDateFormatter, + private val popupMenuHandler: ItemPopupMenuHandler ) : StatelessUseCase(ALL_TIME_STATS, mainDispatcher) { override fun buildLoadingItem(): List = listOf(Title(R.string.stats_insights_all_time_stats)) + override fun buildEmptyItem(): List { + return listOf(buildTitle(), Empty()) + } + override suspend fun loadCachedData(): InsightsAllTimeModel? { return allTimeStore.getAllTimeInsights(statsSiteProvider.siteModel) } @@ -51,7 +58,7 @@ class AllTimeStatsUseCase override fun buildUiModel(domainModel: InsightsAllTimeModel): List { val items = mutableListOf() - items.add(Title(R.string.stats_insights_all_time_stats)) + items.add(buildTitle()) val hasPosts = domainModel.posts > 0 val hasViews = domainModel.views > 0 @@ -79,4 +86,10 @@ class AllTimeStatsUseCase } return items } + + private fun buildTitle() = Title(string.stats_insights_all_time_stats, menuAction = this::onMenuClick) + + private fun onMenuClick(view: View) { + popupMenuHandler.onMenuClick(view, type) + } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/CommentsUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/CommentsUseCase.kt index 112d651ab01b..29195d0506b1 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/CommentsUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/CommentsUseCase.kt @@ -1,5 +1,6 @@ package org.wordpress.android.ui.stats.refresh.lists.sections.insights.usecases +import android.view.View import kotlinx.coroutines.CoroutineDispatcher import org.wordpress.android.R import org.wordpress.android.R.string @@ -24,6 +25,7 @@ import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Navig import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.TabsItem import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Title import org.wordpress.android.ui.stats.refresh.lists.sections.insights.InsightUseCaseFactory +import org.wordpress.android.ui.stats.refresh.utils.ItemPopupMenuHandler import org.wordpress.android.ui.stats.refresh.utils.StatsSiteProvider import org.wordpress.android.ui.stats.refresh.utils.toFormattedString import org.wordpress.android.util.analytics.AnalyticsTrackerWrapper @@ -40,6 +42,7 @@ class CommentsUseCase private val commentsStore: CommentsStore, private val statsSiteProvider: StatsSiteProvider, private val analyticsTracker: AnalyticsTrackerWrapper, + private val popupMenuHandler: ItemPopupMenuHandler, private val useCaseMode: UseCaseMode ) : StatefulUseCase(COMMENTS, mainDispatcher, 0) { override suspend fun fetchRemoteData(forced: Boolean): State { @@ -52,7 +55,8 @@ class CommentsUseCase error != null -> State.Error(error.message ?: error.type.name) model != null && (model.authors.isNotEmpty() || model.posts.isNotEmpty()) -> State.Data( model, - cached = response.cached) + cached = response.cached + ) else -> State.Empty() } } @@ -64,11 +68,15 @@ class CommentsUseCase override fun buildLoadingItem(): List = listOf(Title(R.string.stats_view_comments)) + override fun buildEmptyItem(): List { + return listOf(buildTitle(), Empty()) + } + override fun buildStatefulUiModel(model: CommentsModel, uiState: Int): List { val items = mutableListOf() if (useCaseMode == BLOCK) { - items.add(Title(string.stats_view_comments)) + items.add(buildTitle()) } if (model.authors.isNotEmpty() || model.posts.isNotEmpty()) { @@ -99,6 +107,8 @@ class CommentsUseCase return items } + private fun buildTitle() = Title(string.stats_view_comments, menuAction = this::onMenuClick) + private fun buildAuthorsTab(authors: List): List { val mutableItems = mutableListOf() if (authors.isNotEmpty()) { @@ -140,12 +150,17 @@ class CommentsUseCase navigateTo(ViewCommentsStats(selectedTab)) } + private fun onMenuClick(view: View) { + popupMenuHandler.onMenuClick(view, type) + } + class CommentsUseCaseFactory @Inject constructor( @Named(UI_THREAD) private val mainDispatcher: CoroutineDispatcher, private val commentsStore: CommentsStore, private val statsSiteProvider: StatsSiteProvider, - private val analyticsTracker: AnalyticsTrackerWrapper + private val analyticsTracker: AnalyticsTrackerWrapper, + private val popupMenuHandler: ItemPopupMenuHandler ) : InsightUseCaseFactory { override fun build(useCaseMode: UseCaseMode) = CommentsUseCase( @@ -153,6 +168,7 @@ class CommentsUseCase commentsStore, statsSiteProvider, analyticsTracker, + popupMenuHandler, useCaseMode ) } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/FollowersUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/FollowersUseCase.kt index 675ee8974ec6..34d1ecc7897c 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/FollowersUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/FollowersUseCase.kt @@ -1,5 +1,6 @@ package org.wordpress.android.ui.stats.refresh.lists.sections.insights.usecases +import android.view.View import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.async @@ -34,6 +35,7 @@ import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.TabsI import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Title import org.wordpress.android.ui.stats.refresh.lists.sections.insights.InsightUseCaseFactory import org.wordpress.android.ui.stats.refresh.lists.sections.insights.usecases.FollowersUseCase.FollowersUiState +import org.wordpress.android.ui.stats.refresh.utils.ItemPopupMenuHandler import org.wordpress.android.ui.stats.refresh.utils.StatsSiteProvider import org.wordpress.android.util.analytics.AnalyticsTrackerWrapper import org.wordpress.android.viewmodel.ResourceProvider @@ -52,6 +54,7 @@ class FollowersUseCase private val statsUtilsWrapper: StatsUtilsWrapper, private val resourceProvider: ResourceProvider, private val analyticsTracker: AnalyticsTrackerWrapper, + private val popupMenuHandler: ItemPopupMenuHandler, private val useCaseMode: UseCaseMode ) : StatefulUseCase, FollowersUiState>( FOLLOWERS, @@ -119,6 +122,10 @@ class FollowersUseCase override fun buildLoadingItem(): List = listOf(Title(R.string.stats_view_followers)) + override fun buildEmptyItem(): List { + return listOf(buildTitle(), Empty()) + } + override fun buildStatefulUiModel( domainModel: Pair, uiState: FollowersUiState @@ -128,7 +135,7 @@ class FollowersUseCase val items = mutableListOf() if (useCaseMode == BLOCK) { - items.add(Title(string.stats_view_followers)) + items.add(buildTitle()) } if (domainModel.first.followers.isNotEmpty() || domainModel.second.followers.isNotEmpty()) { @@ -169,6 +176,8 @@ class FollowersUseCase return items } + private fun buildTitle() = Title(string.stats_view_followers, menuAction = this::onMenuClick) + private fun loadMore() { GlobalScope.launch(bgDispatcher) { val state = fetchData(true, PagedMode(itemsToLoad, true)) @@ -215,6 +224,10 @@ class FollowersUseCase data class FollowersUiState(val selectedTab: Int = 0, val isLoading: Boolean = false) + private fun onMenuClick(view: View) { + popupMenuHandler.onMenuClick(view, type) + } + class FollowersUseCaseFactory @Inject constructor( @Named(UI_THREAD) private val mainDispatcher: CoroutineDispatcher, @@ -223,6 +236,7 @@ class FollowersUseCase private val statsSiteProvider: StatsSiteProvider, private val statsUtilsWrapper: StatsUtilsWrapper, private val resourceProvider: ResourceProvider, + private val popupMenuHandler: ItemPopupMenuHandler, private val analyticsTracker: AnalyticsTrackerWrapper ) : InsightUseCaseFactory { override fun build(useCaseMode: UseCaseMode) = @@ -234,6 +248,7 @@ class FollowersUseCase statsUtilsWrapper, resourceProvider, analyticsTracker, + popupMenuHandler, useCaseMode ) } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/LatestPostSummaryUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/LatestPostSummaryUseCase.kt index 4abdaf9d1e32..26830b73cf77 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/LatestPostSummaryUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/LatestPostSummaryUseCase.kt @@ -1,5 +1,6 @@ package org.wordpress.android.ui.stats.refresh.lists.sections.insights.usecases +import android.view.View import kotlinx.coroutines.CoroutineDispatcher import org.wordpress.android.R import org.wordpress.android.R.string @@ -23,6 +24,7 @@ import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Navig import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Title import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.ValueItem import org.wordpress.android.ui.stats.refresh.utils.HUNDRED_THOUSAND +import org.wordpress.android.ui.stats.refresh.utils.ItemPopupMenuHandler import org.wordpress.android.ui.stats.refresh.utils.StatsSiteProvider import org.wordpress.android.ui.stats.refresh.utils.toFormattedString import org.wordpress.android.util.analytics.AnalyticsTrackerWrapper @@ -35,7 +37,8 @@ class LatestPostSummaryUseCase private val latestPostStore: LatestPostInsightsStore, private val statsSiteProvider: StatsSiteProvider, private val latestPostSummaryMapper: LatestPostSummaryMapper, - private val analyticsTracker: AnalyticsTrackerWrapper + private val analyticsTracker: AnalyticsTrackerWrapper, + private val popupMenuHandler: ItemPopupMenuHandler ) : StatelessUseCase(LATEST_POST_SUMMARY, mainDispatcher) { override suspend fun loadCachedData(): InsightsLatestPostModel? { return latestPostStore.getLatestPostInsights(statsSiteProvider.siteModel) @@ -67,7 +70,7 @@ class LatestPostSummaryUseCase private fun buildNullableUiModel(domainModel: InsightsLatestPostModel?): MutableList { val items = mutableListOf() - items.add(Title(string.stats_insights_latest_post_summary)) + items.add(buildTitle()) items.add(latestPostSummaryMapper.buildMessageItem(domainModel, this::onLinkClicked)) if (domainModel != null && domainModel.hasData()) { items.add( @@ -100,6 +103,8 @@ class LatestPostSummaryUseCase return items } + private fun buildTitle() = Title(string.stats_insights_latest_post_summary, menuAction = this::onMenuClick) + private fun InsightsLatestPostModel.hasData() = this.postViewsCount > 0 || this.postCommentCount > 0 || this.postLikeCount > 0 @@ -154,6 +159,10 @@ class LatestPostSummaryUseCase navigateTo(ViewPost(params.postId, params.postUrl)) } + private fun onMenuClick(view: View) { + popupMenuHandler.onMenuClick(view, type) + } + data class LinkClickParams(val postId: Long, val postUrl: String) data class SharePostParams(val postUrl: String, val postTitle: String) data class ViewMoreParams(val postId: Long, val postTitle: String, val postUrl: String) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/MostPopularInsightsUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/MostPopularInsightsUseCase.kt index 4a68269d37b4..7e2c9039d756 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/MostPopularInsightsUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/MostPopularInsightsUseCase.kt @@ -1,7 +1,9 @@ package org.wordpress.android.ui.stats.refresh.lists.sections.insights.usecases +import android.view.View import kotlinx.coroutines.CoroutineDispatcher import org.wordpress.android.R +import org.wordpress.android.R.string import org.wordpress.android.fluxc.model.stats.InsightsMostPopularModel import org.wordpress.android.fluxc.store.StatsStore.InsightsTypes.MOST_POPULAR_DAY_AND_HOUR import org.wordpress.android.fluxc.store.stats.insights.MostPopularInsightsStore @@ -10,8 +12,10 @@ import org.wordpress.android.ui.stats.refresh.lists.sections.BaseStatsUseCase.St import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.QuickScanItem import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.QuickScanItem.Column +import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Empty import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Title import org.wordpress.android.ui.stats.refresh.utils.DateUtils +import org.wordpress.android.ui.stats.refresh.utils.ItemPopupMenuHandler import org.wordpress.android.ui.stats.refresh.utils.StatsSiteProvider import org.wordpress.android.viewmodel.ResourceProvider import javax.inject.Inject @@ -24,7 +28,8 @@ class MostPopularInsightsUseCase private val mostPopularStore: MostPopularInsightsStore, private val statsSiteProvider: StatsSiteProvider, private val dateUtils: DateUtils, - private val resourceProvider: ResourceProvider + private val resourceProvider: ResourceProvider, + private val popupMenuHandler: ItemPopupMenuHandler ) : StatelessUseCase(MOST_POPULAR_DAY_AND_HOUR, mainDispatcher) { override suspend fun loadCachedData(): InsightsMostPopularModel? { return mostPopularStore.getMostPopularInsights(statsSiteProvider.siteModel) @@ -44,9 +49,13 @@ class MostPopularInsightsUseCase override fun buildLoadingItem(): List = listOf(Title(R.string.stats_insights_popular)) + override fun buildEmptyItem(): List { + return listOf(buildTitle(), Empty()) + } + override fun buildUiModel(domainModel: InsightsMostPopularModel): List { val items = mutableListOf() - items.add(Title(R.string.stats_insights_popular)) + items.add(buildTitle()) items.add( QuickScanItem( Column( @@ -69,4 +78,10 @@ class MostPopularInsightsUseCase ) return items } + + private fun buildTitle() = Title(string.stats_insights_popular, menuAction = this::onMenuClick) + + private fun onMenuClick(view: View) { + popupMenuHandler.onMenuClick(view, type) + } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/PostingActivityUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/PostingActivityUseCase.kt index e74598851d3b..32ca6117dce7 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/PostingActivityUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/PostingActivityUseCase.kt @@ -1,5 +1,6 @@ package org.wordpress.android.ui.stats.refresh.lists.sections.insights.usecases +import android.view.View import kotlinx.coroutines.CoroutineDispatcher import org.wordpress.android.R import org.wordpress.android.fluxc.model.stats.insights.PostingActivityModel @@ -9,7 +10,9 @@ import org.wordpress.android.fluxc.store.stats.insights.PostingActivityStore import org.wordpress.android.modules.UI_THREAD import org.wordpress.android.ui.stats.refresh.lists.sections.BaseStatsUseCase.StatelessUseCase import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem +import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Empty import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Title +import org.wordpress.android.ui.stats.refresh.utils.ItemPopupMenuHandler import org.wordpress.android.ui.stats.refresh.utils.StatsSiteProvider import java.util.Calendar import javax.inject.Inject @@ -20,9 +23,14 @@ class PostingActivityUseCase @Named(UI_THREAD) private val mainDispatcher: CoroutineDispatcher, private val store: PostingActivityStore, private val statsSiteProvider: StatsSiteProvider, - private val postingActivityMapper: PostingActivityMapper + private val postingActivityMapper: PostingActivityMapper, + private val popupMenuHandler: ItemPopupMenuHandler ) : StatelessUseCase(POSTING_ACTIVITY, mainDispatcher) { - override fun buildLoadingItem(): List = listOf(Title(R.string.stats_insights_all_time_stats)) + override fun buildLoadingItem(): List = listOf(Title(R.string.stats_insights_posting_activity)) + + override fun buildEmptyItem(): List { + return listOf(buildTitle(), Empty()) + } override suspend fun loadCachedData(): PostingActivityModel? { return store.getPostingActivity(statsSiteProvider.siteModel, getStartDate(), getEndDate()) @@ -44,12 +52,14 @@ class PostingActivityUseCase override fun buildUiModel(domainModel: PostingActivityModel): List { val items = mutableListOf() - items.add(Title(R.string.stats_insights_posting_activity)) + items.add(buildTitle()) val activityItem = postingActivityMapper.buildActivityItem(domainModel.months, domainModel.max) items.add(activityItem) return items } + private fun buildTitle() = Title(R.string.stats_insights_posting_activity, menuAction = this::onMenuClick) + private fun getEndDate(): Day { val endDate = Calendar.getInstance() return Day( @@ -68,4 +78,8 @@ class PostingActivityUseCase startDate.getActualMinimum(Calendar.DAY_OF_MONTH) ) } + + private fun onMenuClick(view: View) { + popupMenuHandler.onMenuClick(view, type) + } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/PublicizeUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/PublicizeUseCase.kt index 7c4d957f91dd..5abaef887398 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/PublicizeUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/PublicizeUseCase.kt @@ -1,5 +1,6 @@ package org.wordpress.android.ui.stats.refresh.lists.sections.insights.usecases +import android.view.View import kotlinx.coroutines.CoroutineDispatcher import org.wordpress.android.R import org.wordpress.android.R.string @@ -20,6 +21,7 @@ import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Link import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.NavigationAction import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Title import org.wordpress.android.ui.stats.refresh.lists.sections.insights.InsightUseCaseFactory +import org.wordpress.android.ui.stats.refresh.utils.ItemPopupMenuHandler import org.wordpress.android.ui.stats.refresh.utils.ServiceMapper import org.wordpress.android.ui.stats.refresh.utils.StatsSiteProvider import org.wordpress.android.util.analytics.AnalyticsTrackerWrapper @@ -36,6 +38,7 @@ class PublicizeUseCase private val statsSiteProvider: StatsSiteProvider, private val mapper: ServiceMapper, private val analyticsTracker: AnalyticsTrackerWrapper, + private val popupMenuHandler: ItemPopupMenuHandler, private val useCaseMode: UseCaseMode ) : StatelessUseCase(PUBLICIZE, mainDispatcher) { private val itemsToLoad = if (useCaseMode == VIEW_ALL) VIEW_ALL_ITEM_COUNT else BLOCK_ITEM_COUNT @@ -67,11 +70,15 @@ class PublicizeUseCase override fun buildLoadingItem(): List = listOf(Title(R.string.stats_view_publicize)) + override fun buildEmptyItem(): List { + return listOf(buildTitle(), Empty()) + } + override fun buildUiModel(domainModel: PublicizeModel): List { val items = mutableListOf() if (useCaseMode == BLOCK) { - items.add(Title(string.stats_view_publicize)) + items.add(buildTitle()) } if (domainModel.services.isEmpty()) { @@ -91,18 +98,25 @@ class PublicizeUseCase return items } + private fun buildTitle() = Title(R.string.stats_view_publicize, menuAction = this::onMenuClick) + private fun onLinkClick() { analyticsTracker.track(AnalyticsTracker.Stat.STATS_PUBLICIZE_VIEW_MORE_TAPPED) return navigateTo(ViewPublicizeStats()) } + private fun onMenuClick(view: View) { + popupMenuHandler.onMenuClick(view, type) + } + class PublicizeUseCaseFactory @Inject constructor( @Named(UI_THREAD) private val mainDispatcher: CoroutineDispatcher, private val publicizeStore: PublicizeStore, private val statsSiteProvider: StatsSiteProvider, private val mapper: ServiceMapper, - private val analyticsTracker: AnalyticsTrackerWrapper + private val analyticsTracker: AnalyticsTrackerWrapper, + private val popupMenuHandler: ItemPopupMenuHandler ) : InsightUseCaseFactory { override fun build(useCaseMode: UseCaseMode) = PublicizeUseCase( @@ -111,6 +125,7 @@ class PublicizeUseCase statsSiteProvider, mapper, analyticsTracker, + popupMenuHandler, useCaseMode ) } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/TagsAndCategoriesUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/TagsAndCategoriesUseCase.kt index 8926483158c1..c59eed254dcf 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/TagsAndCategoriesUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/TagsAndCategoriesUseCase.kt @@ -1,5 +1,6 @@ package org.wordpress.android.ui.stats.refresh.lists.sections.insights.usecases +import android.view.View import kotlinx.coroutines.CoroutineDispatcher import org.wordpress.android.R import org.wordpress.android.R.drawable @@ -28,6 +29,7 @@ import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Navig import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Title import org.wordpress.android.ui.stats.refresh.lists.sections.insights.InsightUseCaseFactory import org.wordpress.android.ui.stats.refresh.lists.sections.insights.usecases.TagsAndCategoriesUseCase.TagsAndCategoriesUiState +import org.wordpress.android.ui.stats.refresh.utils.ItemPopupMenuHandler import org.wordpress.android.ui.stats.refresh.utils.StatsSiteProvider import org.wordpress.android.ui.stats.refresh.utils.toFormattedString import org.wordpress.android.util.analytics.AnalyticsTrackerWrapper @@ -45,6 +47,7 @@ class TagsAndCategoriesUseCase private val statsSiteProvider: StatsSiteProvider, private val resourceProvider: ResourceProvider, private val analyticsTracker: AnalyticsTrackerWrapper, + private val popupMenuHandler: ItemPopupMenuHandler, private val useCaseMode: UseCaseMode ) : StatefulUseCase( TAGS_AND_CATEGORIES, @@ -71,11 +74,15 @@ class TagsAndCategoriesUseCase override fun buildLoadingItem(): List = listOf(Title(R.string.stats_insights_tags_and_categories)) + override fun buildEmptyItem(): List { + return listOf(buildTitle(), Empty()) + } + override fun buildStatefulUiModel(domainModel: TagsModel, uiState: TagsAndCategoriesUiState): List { val items = mutableListOf() if (useCaseMode == BLOCK) { - items.add(Title(R.string.stats_insights_tags_and_categories)) + items.add(buildTitle()) } if (domainModel.tags.isEmpty()) { @@ -122,6 +129,8 @@ class TagsAndCategoriesUseCase return items } + private fun buildTitle() = Title(string.stats_insights_tags_and_categories, menuAction = this::onMenuClick) + private fun areTagsEqual(tagA: TagModel, tagB: TagModel?): Boolean { return tagA.items == tagB?.items && tagA.views == tagB.views } @@ -175,6 +184,10 @@ class TagsAndCategoriesUseCase navigateTo(ViewTag(link)) } + private fun onMenuClick(view: View) { + popupMenuHandler.onMenuClick(view, type) + } + data class TagsAndCategoriesUiState(val expandedTag: TagModel? = null) class TagsAndCategoriesUseCaseFactory @@ -183,7 +196,8 @@ class TagsAndCategoriesUseCase private val tagsStore: TagsStore, private val statsSiteProvider: StatsSiteProvider, private val resourceProvider: ResourceProvider, - private val analyticsTracker: AnalyticsTrackerWrapper + private val analyticsTracker: AnalyticsTrackerWrapper, + private val popupMenuHandler: ItemPopupMenuHandler ) : InsightUseCaseFactory { override fun build(useCaseMode: UseCaseMode) = TagsAndCategoriesUseCase( @@ -192,6 +206,7 @@ class TagsAndCategoriesUseCase statsSiteProvider, resourceProvider, analyticsTracker, + popupMenuHandler, useCaseMode ) } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/TodayStatsUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/TodayStatsUseCase.kt index 7b16ed794661..b88df4445499 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/TodayStatsUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/TodayStatsUseCase.kt @@ -1,7 +1,9 @@ package org.wordpress.android.ui.stats.refresh.lists.sections.insights.usecases +import android.view.View import kotlinx.coroutines.CoroutineDispatcher import org.wordpress.android.R +import org.wordpress.android.R.string import org.wordpress.android.fluxc.model.stats.VisitsModel import org.wordpress.android.fluxc.store.StatsStore.InsightsTypes.TODAY_STATS import org.wordpress.android.fluxc.store.stats.insights.TodayInsightsStore @@ -12,6 +14,7 @@ import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Empty import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.QuickScanItem import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.QuickScanItem.Column import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Title +import org.wordpress.android.ui.stats.refresh.utils.ItemPopupMenuHandler import org.wordpress.android.ui.stats.refresh.utils.StatsSiteProvider import org.wordpress.android.ui.stats.refresh.utils.toFormattedString import javax.inject.Inject @@ -21,7 +24,8 @@ class TodayStatsUseCase @Inject constructor( @Named(UI_THREAD) private val mainDispatcher: CoroutineDispatcher, private val todayStore: TodayInsightsStore, - private val statsSiteProvider: StatsSiteProvider + private val statsSiteProvider: StatsSiteProvider, + private val popupMenuHandler: ItemPopupMenuHandler ) : StatelessUseCase(TODAY_STATS, mainDispatcher) { override suspend fun loadCachedData(): VisitsModel? { return todayStore.getTodayInsights(statsSiteProvider.siteModel) @@ -44,9 +48,13 @@ class TodayStatsUseCase override fun buildLoadingItem(): List = listOf(Title(R.string.stats_insights_today_stats)) + override fun buildEmptyItem(): List { + return listOf(buildTitle(), Empty()) + } + override fun buildUiModel(domainModel: VisitsModel): List { val items = mutableListOf() - items.add(Title(R.string.stats_insights_today_stats)) + items.add(buildTitle()) val hasViews = domainModel.views > 0 val hasVisitors = domainModel.visitors > 0 @@ -70,4 +78,10 @@ class TodayStatsUseCase } return items } + + private fun onMenuClick(view: View) { + popupMenuHandler.onMenuClick(view, type) + } + + private fun buildTitle() = Title(string.stats_insights_today_stats, menuAction = this::onMenuClick) } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/viewholders/TitleViewHolder.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/viewholders/TitleViewHolder.kt index 04f708903444..ad1d2b783138 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/viewholders/TitleViewHolder.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/viewholders/TitleViewHolder.kt @@ -1,7 +1,10 @@ package org.wordpress.android.ui.stats.refresh.lists.sections.viewholders +import android.view.View import android.view.ViewGroup +import android.widget.ImageButton import android.widget.TextView +import org.wordpress.android.R import org.wordpress.android.R.id import org.wordpress.android.R.layout import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Title @@ -11,7 +14,15 @@ class TitleViewHolder(parent: ViewGroup) : BlockListItemViewHolder( layout.stats_block_title_item ) { private val text = itemView.findViewById(id.text) + private val menu = itemView.findViewById(R.id.menu) fun bind(item: Title) { text.setTextOrHide(item.textResource, item.text) + if (item.menuAction != null) { + menu.visibility = View.VISIBLE + menu.setOnClickListener { item.menuAction.invoke(menu) } + } else { + menu.visibility = View.GONE + menu.setOnClickListener(null) + } } } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/utils/ItemPopupMenuHandler.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/utils/ItemPopupMenuHandler.kt new file mode 100644 index 000000000000..4cd6a7a70920 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/utils/ItemPopupMenuHandler.kt @@ -0,0 +1,78 @@ +package org.wordpress.android.ui.stats.refresh.utils + +import android.arch.lifecycle.LiveData +import android.arch.lifecycle.MutableLiveData +import android.support.v7.widget.PopupMenu +import android.view.View +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import org.wordpress.android.R +import org.wordpress.android.fluxc.store.StatsStore +import org.wordpress.android.fluxc.store.StatsStore.InsightsTypes +import org.wordpress.android.fluxc.store.StatsStore.StatsTypes +import org.wordpress.android.modules.BG_THREAD +import org.wordpress.android.modules.UI_THREAD +import org.wordpress.android.util.Event +import javax.inject.Inject +import javax.inject.Named +import javax.inject.Singleton + +@Singleton +class ItemPopupMenuHandler +@Inject constructor( + @Named(BG_THREAD) private val bgDispatcher: CoroutineDispatcher, + @Named(UI_THREAD) private val mainDispatcher: CoroutineDispatcher, + private val statsStore: StatsStore, + private val statsSiteProvider: StatsSiteProvider +) { + private val mutableTypeMoved = MutableLiveData() + val typeMoved: LiveData = mutableTypeMoved + fun onMenuClick(view: View, statsType: StatsTypes) { + GlobalScope.launch(bgDispatcher) { + val type = statsType as InsightsTypes + val insights = statsStore.getInsights(statsSiteProvider.siteModel) + + val indexOfBlock = insights.indexOfFirst { it == type } + val showUpAction = indexOfBlock > 0 + val showDownAction = indexOfBlock < insights.size - 1 + withContext(mainDispatcher) { + val popup = PopupMenu(view.context, view) + val popupMenu = popup.menu + popup.inflate(R.menu.menu_stats_item) + popupMenu.findItem(R.id.action_move_up).isVisible = showUpAction + popupMenu.findItem(R.id.action_move_down).isVisible = showDownAction + popup.show() + popup.setOnMenuItemClickListener { menuItem -> + when (menuItem.itemId) { + R.id.action_move_up -> { + GlobalScope.launch(bgDispatcher) { + statsStore.moveTypeUp(statsSiteProvider.siteModel, type) + mutableTypeMoved.postValue(TypeChangeEvent(type)) + } + true + } + R.id.action_move_down -> { + GlobalScope.launch(bgDispatcher) { + statsStore.moveTypeDown(statsSiteProvider.siteModel, type) + mutableTypeMoved.postValue(TypeChangeEvent(type)) + } + true + } + R.id.action_remove -> { + GlobalScope.launch(bgDispatcher) { + statsStore.removeType(statsSiteProvider.siteModel, type) + mutableTypeMoved.postValue(TypeChangeEvent(type)) + } + true + } + else -> false + } + } + } + } + } + + data class TypeChangeEvent(val types: StatsTypes) : Event() +} diff --git a/WordPress/src/main/java/org/wordpress/android/util/LiveDataUtils.kt b/WordPress/src/main/java/org/wordpress/android/util/LiveDataUtils.kt index 3d55e5581c57..07a8073159be 100644 --- a/WordPress/src/main/java/org/wordpress/android/util/LiveDataUtils.kt +++ b/WordPress/src/main/java/org/wordpress/android/util/LiveDataUtils.kt @@ -205,8 +205,8 @@ fun LiveData.throttle( ): ThrottleLiveData { val mediatorLiveData: ThrottleLiveData = ThrottleLiveData(coroutineScope = coroutineScope, offset = offset) mediatorLiveData.addSource(this) { - if (it != mediatorLiveData.value || !distinct) { - mediatorLiveData.value = it + if ((it != mediatorLiveData.value || !distinct) && it != null) { + mediatorLiveData.postValue(it) } } return mediatorLiveData diff --git a/WordPress/src/main/res/drawable/ic_arrow_down_grey_dark_24dp.xml b/WordPress/src/main/res/drawable/ic_arrow_down_grey_dark_24dp.xml new file mode 100644 index 000000000000..11365e9c23b0 --- /dev/null +++ b/WordPress/src/main/res/drawable/ic_arrow_down_grey_dark_24dp.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/WordPress/src/main/res/drawable/ic_arrow_up_grey_dark_24dp.xml b/WordPress/src/main/res/drawable/ic_arrow_up_grey_dark_24dp.xml new file mode 100644 index 000000000000..c1cdf781c6a7 --- /dev/null +++ b/WordPress/src/main/res/drawable/ic_arrow_up_grey_dark_24dp.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/WordPress/src/main/res/drawable/ic_ellipsis_vertical_grey_darken_48dp.xml b/WordPress/src/main/res/drawable/ic_ellipsis_vertical_grey_darken_48dp.xml new file mode 100644 index 000000000000..047b3c616cc3 --- /dev/null +++ b/WordPress/src/main/res/drawable/ic_ellipsis_vertical_grey_darken_48dp.xml @@ -0,0 +1,15 @@ + + + + + + + + diff --git a/WordPress/src/main/res/drawable/ic_trash_grey_dark_24dp.xml b/WordPress/src/main/res/drawable/ic_trash_grey_dark_24dp.xml new file mode 100644 index 000000000000..a7d5e8e7e921 --- /dev/null +++ b/WordPress/src/main/res/drawable/ic_trash_grey_dark_24dp.xml @@ -0,0 +1,15 @@ + + + + + + diff --git a/WordPress/src/main/res/layout/stats_block_title_item.xml b/WordPress/src/main/res/layout/stats_block_title_item.xml index 072e9b7a9aec..4ff1df73b702 100644 --- a/WordPress/src/main/res/layout/stats_block_title_item.xml +++ b/WordPress/src/main/res/layout/stats_block_title_item.xml @@ -1,15 +1,28 @@ + + + + diff --git a/WordPress/src/main/res/layout/stats_list_fragment.xml b/WordPress/src/main/res/layout/stats_list_fragment.xml index a6c6c4e3db2c..5b30ad47a550 100644 --- a/WordPress/src/main/res/layout/stats_list_fragment.xml +++ b/WordPress/src/main/res/layout/stats_list_fragment.xml @@ -32,6 +32,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:clipToPadding="false" + android:descendantFocusability="beforeDescendants" android:scrollbars="vertical"/> @@ -39,6 +40,7 @@ android:id="@+id/recyclerView" style="@style/StatsList" android:clipToPadding="false" + android:descendantFocusability="beforeDescendants" android:scrollbars="vertical"/> diff --git a/WordPress/src/main/res/menu/menu_stats_item.xml b/WordPress/src/main/res/menu/menu_stats_item.xml new file mode 100644 index 000000000000..4c707d8be788 --- /dev/null +++ b/WordPress/src/main/res/menu/menu_stats_item.xml @@ -0,0 +1,15 @@ + + + + + + diff --git a/WordPress/src/main/res/values/strings.xml b/WordPress/src/main/res/values/strings.xml index b9beb4ff8ece..b302741f1c9f 100644 --- a/WordPress/src/main/res/values/strings.xml +++ b/WordPress/src/main/res/values/strings.xml @@ -809,6 +809,7 @@ Service Followers %1$s - %2$s + Stats item settings We cannot open the statistics at the moment. Please try again later +%1$s (%2$s%%) %1$s (%2$s%%) @@ -816,6 +817,9 @@ More posts Site not loaded yet Showing stats for: + Move up + Move down + Remove from insights Jetpack diff --git a/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/AllTimeStatsUseCaseTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/AllTimeStatsUseCaseTest.kt index 7b9848e1c084..09da2b29da9a 100644 --- a/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/AllTimeStatsUseCaseTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/AllTimeStatsUseCaseTest.kt @@ -23,6 +23,7 @@ import org.wordpress.android.ui.stats.refresh.lists.sections.BaseStatsUseCase.Us import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Empty import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.QuickScanItem import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Title +import org.wordpress.android.ui.stats.refresh.utils.ItemPopupMenuHandler import org.wordpress.android.ui.stats.refresh.utils.StatsDateFormatter import org.wordpress.android.ui.stats.refresh.utils.StatsSiteProvider @@ -30,6 +31,7 @@ class AllTimeStatsUseCaseTest : BaseUnitTest() { @Mock lateinit var insightsStore: AllTimeInsightsStore @Mock lateinit var statsDateFormatter: StatsDateFormatter @Mock lateinit var statsSiteProvider: StatsSiteProvider + @Mock lateinit var popupMenuHandler: ItemPopupMenuHandler @Mock lateinit var site: SiteModel private lateinit var useCase: AllTimeStatsUseCase private val bestDay = "2018-11-25" @@ -40,7 +42,8 @@ class AllTimeStatsUseCaseTest : BaseUnitTest() { Dispatchers.Unconfined, insightsStore, statsSiteProvider, - statsDateFormatter + statsDateFormatter, + popupMenuHandler ) whenever(statsSiteProvider.siteModel).thenReturn(site) whenever(statsDateFormatter.printDate(bestDay)).thenReturn(bestDayTransformed) diff --git a/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/CommentsUseCaseTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/CommentsUseCaseTest.kt index 50626e03b46d..38a1cfbfa2b6 100644 --- a/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/CommentsUseCaseTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/CommentsUseCaseTest.kt @@ -36,6 +36,7 @@ import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Type. import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Type.LIST_ITEM import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Type.LIST_ITEM_WITH_ICON import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Type.TITLE +import org.wordpress.android.ui.stats.refresh.utils.ItemPopupMenuHandler import org.wordpress.android.ui.stats.refresh.utils.StatsSiteProvider import org.wordpress.android.util.analytics.AnalyticsTrackerWrapper @@ -44,6 +45,7 @@ class CommentsUseCaseTest : BaseUnitTest() { @Mock lateinit var statsSiteProvider: StatsSiteProvider @Mock lateinit var site: SiteModel @Mock lateinit var tracker: AnalyticsTrackerWrapper + @Mock lateinit var popupMenuHandler: ItemPopupMenuHandler private lateinit var useCase: CommentsUseCase private val postId: Long = 10 private val postTitle = "Post" @@ -59,6 +61,7 @@ class CommentsUseCaseTest : BaseUnitTest() { insightsStore, statsSiteProvider, tracker, + popupMenuHandler, BLOCK ) whenever(statsSiteProvider.siteModel).thenReturn(site) diff --git a/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/FollowersUseCaseTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/FollowersUseCaseTest.kt index 00782dc473a4..e37af56fb042 100644 --- a/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/FollowersUseCaseTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/FollowersUseCaseTest.kt @@ -36,6 +36,8 @@ import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Loadi import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.TabsItem import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Title import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Type.TITLE +import org.wordpress.android.ui.stats.refresh.lists.sections.insights.usecases.FollowersUseCase.FollowersUseCaseFactory +import org.wordpress.android.ui.stats.refresh.utils.ItemPopupMenuHandler import org.wordpress.android.ui.stats.refresh.utils.StatsSiteProvider import org.wordpress.android.util.analytics.AnalyticsTrackerWrapper import org.wordpress.android.viewmodel.ResourceProvider @@ -48,6 +50,8 @@ class FollowersUseCaseTest : BaseUnitTest() { @Mock lateinit var statsSiteProvider: StatsSiteProvider @Mock lateinit var site: SiteModel @Mock lateinit var tracker: AnalyticsTrackerWrapper + @Mock lateinit var popupMenuHandler: ItemPopupMenuHandler + private lateinit var useCaseFactory: FollowersUseCaseFactory private lateinit var useCase: FollowersUseCase private val avatar = "avatar.jpg" private val user = "John Smith" @@ -64,16 +68,17 @@ class FollowersUseCaseTest : BaseUnitTest() { val message = "Total followers count is 50" @Before fun setUp() { - useCase = FollowersUseCase( + useCaseFactory = FollowersUseCaseFactory( Dispatchers.Unconfined, Dispatchers.Unconfined, insightsStore, statsSiteProvider, statsUtilsWrapper, resourceProvider, - tracker, - BLOCK + popupMenuHandler, + tracker ) + useCase = useCaseFactory.build(BLOCK) whenever(statsUtilsWrapper.getSinceLabelLowerCase(dateSubscribed)).thenReturn(sinceLabel) whenever(resourceProvider.getString(any())).thenReturn(wordPressLabel) whenever(resourceProvider.getString(eq(R.string.stats_followers_count_message), any(), any())).thenReturn( @@ -234,16 +239,7 @@ class FollowersUseCaseTest : BaseUnitTest() { @Test fun `maps email followers to UI model in the view all mode`() = test { - useCase = FollowersUseCase( - Dispatchers.Unconfined, - Dispatchers.Unconfined, - insightsStore, - statsSiteProvider, - statsUtilsWrapper, - resourceProvider, - tracker, - VIEW_ALL - ) + useCase = useCaseFactory.build(VIEW_ALL) val refresh = true val wpComModel = FollowersModel( diff --git a/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/LatestPostSummaryUseCaseTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/LatestPostSummaryUseCaseTest.kt index dcaa41d05a5d..17957a275024 100644 --- a/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/LatestPostSummaryUseCaseTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/LatestPostSummaryUseCaseTest.kt @@ -29,6 +29,7 @@ import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.ListI import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Text import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Title import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.ValueItem +import org.wordpress.android.ui.stats.refresh.utils.ItemPopupMenuHandler import org.wordpress.android.ui.stats.refresh.utils.StatsSiteProvider import org.wordpress.android.util.analytics.AnalyticsTrackerWrapper import java.util.Date @@ -39,6 +40,7 @@ class LatestPostSummaryUseCaseTest : BaseUnitTest() { @Mock lateinit var statsSiteProvider: StatsSiteProvider @Mock lateinit var site: SiteModel @Mock lateinit var tracker: AnalyticsTrackerWrapper + @Mock lateinit var popupMenuHandler: ItemPopupMenuHandler private lateinit var useCase: LatestPostSummaryUseCase @Before fun setUp() = test { @@ -47,7 +49,8 @@ class LatestPostSummaryUseCaseTest : BaseUnitTest() { insightsStore, statsSiteProvider, latestPostSummaryMapper, - tracker + tracker, + popupMenuHandler ) whenever(statsSiteProvider.siteModel).thenReturn(site) useCase.navigationTarget.observeForever {} diff --git a/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/MostPopularInsightsUseCaseTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/MostPopularInsightsUseCaseTest.kt index 14a9c1d8e81e..266e5f41db1b 100644 --- a/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/MostPopularInsightsUseCaseTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/MostPopularInsightsUseCaseTest.kt @@ -23,6 +23,7 @@ import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Title import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Type.QUICK_SCAN_ITEM import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Type.TITLE import org.wordpress.android.ui.stats.refresh.utils.DateUtils +import org.wordpress.android.ui.stats.refresh.utils.ItemPopupMenuHandler import org.wordpress.android.ui.stats.refresh.utils.StatsSiteProvider import org.wordpress.android.viewmodel.ResourceProvider import kotlin.math.roundToInt @@ -33,6 +34,7 @@ class MostPopularInsightsUseCaseTest : BaseUnitTest() { @Mock lateinit var site: SiteModel @Mock lateinit var dateUtils: DateUtils @Mock lateinit var resourceProvider: ResourceProvider + @Mock lateinit var popupMenuHandler: ItemPopupMenuHandler private lateinit var useCase: MostPopularInsightsUseCase private val day = 2 private val highestDayPercent = 15.0 @@ -47,7 +49,8 @@ class MostPopularInsightsUseCaseTest : BaseUnitTest() { insightsStore, statsSiteProvider, dateUtils, - resourceProvider + resourceProvider, + popupMenuHandler ) whenever(statsSiteProvider.siteModel).thenReturn(site) whenever(dateUtils.getWeekDay(day)).thenReturn(dayString) diff --git a/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/PublicizeUseCaseTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/PublicizeUseCaseTest.kt index 7fcebe831534..52520000cb52 100644 --- a/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/PublicizeUseCaseTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/PublicizeUseCaseTest.kt @@ -29,6 +29,7 @@ import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.ListI import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Title import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Type.LINK import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Type.TITLE +import org.wordpress.android.ui.stats.refresh.utils.ItemPopupMenuHandler import org.wordpress.android.ui.stats.refresh.utils.ServiceMapper import org.wordpress.android.ui.stats.refresh.utils.StatsSiteProvider import org.wordpress.android.util.analytics.AnalyticsTrackerWrapper @@ -39,6 +40,7 @@ class PublicizeUseCaseTest : BaseUnitTest() { @Mock lateinit var site: SiteModel @Mock lateinit var serviceMapper: ServiceMapper @Mock lateinit var tracker: AnalyticsTrackerWrapper + @Mock lateinit var popupMenuHandler: ItemPopupMenuHandler private lateinit var useCase: PublicizeUseCase private val itemsToLoad = 6 private val limitMode = LimitMode.Top(itemsToLoad) @@ -50,6 +52,7 @@ class PublicizeUseCaseTest : BaseUnitTest() { statsSiteProvider, serviceMapper, tracker, + popupMenuHandler, BLOCK ) whenever(statsSiteProvider.siteModel).thenReturn(site) diff --git a/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/TagsAndCategoriesUseCaseTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/TagsAndCategoriesUseCaseTest.kt index dbfdb7f017af..b3df05e2bde4 100644 --- a/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/TagsAndCategoriesUseCaseTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/TagsAndCategoriesUseCaseTest.kt @@ -34,6 +34,7 @@ import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Type. import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Type.LINK import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Type.LIST_ITEM_WITH_ICON import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Type.TITLE +import org.wordpress.android.ui.stats.refresh.utils.ItemPopupMenuHandler import org.wordpress.android.ui.stats.refresh.utils.StatsSiteProvider import org.wordpress.android.util.analytics.AnalyticsTrackerWrapper import org.wordpress.android.viewmodel.ResourceProvider @@ -44,6 +45,7 @@ class TagsAndCategoriesUseCaseTest : BaseUnitTest() { @Mock lateinit var site: SiteModel @Mock lateinit var resourceProvider: ResourceProvider @Mock lateinit var tracker: AnalyticsTrackerWrapper + @Mock lateinit var popupMenuHandler: ItemPopupMenuHandler private lateinit var useCase: TagsAndCategoriesUseCase private val blockItemCount = 6 private val singleTagViews: Long = 10 @@ -59,6 +61,7 @@ class TagsAndCategoriesUseCaseTest : BaseUnitTest() { statsSiteProvider, resourceProvider, tracker, + popupMenuHandler, BLOCK ) whenever(statsSiteProvider.siteModel).thenReturn(site) diff --git a/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/TodayStatsUseCaseTest.kt b/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/TodayStatsUseCaseTest.kt index 6364a2cc89d8..8135785a0ba5 100644 --- a/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/TodayStatsUseCaseTest.kt +++ b/WordPress/src/test/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/TodayStatsUseCaseTest.kt @@ -23,11 +23,13 @@ import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Quick import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Title import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Type.QUICK_SCAN_ITEM import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Type.TITLE +import org.wordpress.android.ui.stats.refresh.utils.ItemPopupMenuHandler import org.wordpress.android.ui.stats.refresh.utils.StatsSiteProvider class TodayStatsUseCaseTest : BaseUnitTest() { @Mock lateinit var insightsStore: TodayInsightsStore @Mock lateinit var statsSiteProvider: StatsSiteProvider + @Mock lateinit var popupMenuHandler: ItemPopupMenuHandler @Mock lateinit var site: SiteModel private lateinit var useCase: TodayStatsUseCase private val views = 10 @@ -39,7 +41,8 @@ class TodayStatsUseCaseTest : BaseUnitTest() { useCase = TodayStatsUseCase( Dispatchers.Unconfined, insightsStore, - statsSiteProvider + statsSiteProvider, + popupMenuHandler ) whenever(statsSiteProvider.siteModel).thenReturn(site) } diff --git a/build.gradle b/build.gradle index c79187da8f28..ed8af793e12e 100644 --- a/build.gradle +++ b/build.gradle @@ -104,5 +104,5 @@ buildScan { } ext { - fluxCVersion = '8c78b7cb3c3960525a34d8af9125ca261cf50977' + fluxCVersion = 'e049ac97ac54f371aef152de5e02b1dc2eb32f82' }