diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index a8cc5d4f9ad6..6041b3d14229 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -5,6 +5,7 @@ * Added ability to toggle to compact view type for blog posts list * Fixed several bugs on the blog posts screen * Fixed bug that prevented audio playing in the reader +* New Insights Management for selecting the most relevant stats 12.3 ----- diff --git a/WordPress/src/main/AndroidManifest.xml b/WordPress/src/main/AndroidManifest.xml index 6f4cb1c22155..eca0e09b391e 100644 --- a/WordPress/src/main/AndroidManifest.xml +++ b/WordPress/src/main/AndroidManifest.xml @@ -288,9 +288,13 @@ android:theme="@style/Calypso.NoActionBar" /> + oldItem == newItem } } else { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/NavigationTarget.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/NavigationTarget.kt index 56447eee483c..cf771c95271e 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/NavigationTarget.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/NavigationTarget.kt @@ -34,4 +34,5 @@ sealed class NavigationTarget { object ViewDayAverageStats : NavigationTarget() object ViewRecentWeeksStats : NavigationTarget() object ViewAnnualStats : NavigationTarget() + object ViewInsightsManagement : NavigationTarget() } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsFragment.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsFragment.kt index 1eca4cc4bb3a..f900dbd64916 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsFragment.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsFragment.kt @@ -36,6 +36,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.StatsNavigator import org.wordpress.android.ui.stats.refresh.utils.StatsSiteProvider import org.wordpress.android.util.WPSwipeToRefreshHelper import org.wordpress.android.util.helpers.SwipeToRefreshHelper @@ -49,15 +50,12 @@ class StatsFragment : DaggerFragment() { @Inject lateinit var statsSiteProvider: StatsSiteProvider private lateinit var viewModel: StatsViewModel private lateinit var swipeToRefreshHelper: SwipeToRefreshHelper + @Inject lateinit var navigator: StatsNavigator private var restorePreviousSearch = false - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setHasOptionsMenu(true) - } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + setHasOptionsMenu(true) return inflater.inflate(R.layout.stats_fragment, container, false) } 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 ab25cedfe4dc..56279380fab8 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 @@ -203,7 +203,7 @@ class StatsModule { mainDispatcher, statsSiteProvider, useCases, - { statsStore.getInsights() }, + { statsStore.getAddedInsights(statsSiteProvider.siteModel) }, uiModelMapper::mapInsights ) } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsViewAllFragment.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsViewAllFragment.kt index dbcd5eebd2bc..afaf84208604 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsViewAllFragment.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsViewAllFragment.kt @@ -83,7 +83,7 @@ class StatsViewAllFragment : DaggerFragment() { super.onSaveInstanceState(outState) } - private fun initializeViews(savedInstanceState: Bundle?) { + private fun initializeViews(savedInstanceState: Bundle?) { val layoutManager = LinearLayoutManager(activity, LinearLayoutManager.VERTICAL, false) savedInstanceState?.getParcelable(listStateKey)?.let { @@ -97,10 +97,10 @@ class StatsViewAllFragment : DaggerFragment() { viewModel.onPullToRefresh() } - select_next_date.setOnClickListener { + nextDateButton.setOnClickListener { viewModel.onNextDateSelected() } - select_previous_date.setOnClickListener { + previousDateButton.setOnClickListener { viewModel.onPreviousDateSelected() } } @@ -173,8 +173,8 @@ class StatsViewAllFragment : DaggerFragment() { if (it != null) { recyclerView.visibility = if (it is StatsBlock.Success) View.VISIBLE else View.GONE loadingContainer.visibility = if (it is StatsBlock.Loading) View.VISIBLE else View.GONE - actionable_error_view.visibility = if (it is StatsBlock.Error) View.VISIBLE else View.GONE - actionable_empty_view.visibility = if (it is StatsBlock.EmptyBlock) View.VISIBLE else View.GONE + statsErrorView.visibility = if (it is StatsBlock.Error) View.VISIBLE else View.GONE + statsEmptyView.visibility = if (it is StatsBlock.EmptyBlock) View.VISIBLE else View.GONE when (it) { is StatsBlock.Success -> { loadData(recyclerView, prepareLayout(it.data, it.type)) @@ -183,7 +183,7 @@ class StatsViewAllFragment : DaggerFragment() { loadData(loadingRecyclerView, prepareLayout(it.data, it.type)) } is StatsBlock.Error -> { - actionable_error_view.button.setOnClickListener { + statsErrorView.button.setOnClickListener { viewModel.onRetryClick() } } @@ -201,14 +201,14 @@ class StatsViewAllFragment : DaggerFragment() { if (date_selection_toolbar.visibility != dateSelectorVisibility) { date_selection_toolbar.visibility = dateSelectorVisibility } - selected_date.text = dateSelectorUiModel?.date ?: "" + selectedDateTextView.text = dateSelectorUiModel?.date ?: "" val enablePreviousButton = dateSelectorUiModel?.enableSelectPrevious == true - if (select_previous_date.isEnabled != enablePreviousButton) { - select_previous_date.isEnabled = enablePreviousButton + if (previousDateButton.isEnabled != enablePreviousButton) { + previousDateButton.isEnabled = enablePreviousButton } val enableNextButton = dateSelectorUiModel?.enableSelectNext == true - if (select_next_date.isEnabled != enableNextButton) { - select_next_date.isEnabled = enableNextButton + if (nextDateButton.isEnabled != enableNextButton) { + nextDateButton.isEnabled = enableNextButton } }) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsViewModel.kt index efdeac1efbe1..f639f2ab3149 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsViewModel.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsViewModel.kt @@ -109,6 +109,7 @@ class StatsViewModel listUseCases[statsSection]?.onListSelected() _toolbarHasShadow.value = statsSection == INSIGHTS + when (statsSection) { INSIGHTS -> analyticsTracker.track(STATS_INSIGHTS_ACCESSED) DAYS -> analyticsTracker.track(STATS_PERIOD_DAYS_ACCESSED) 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 e94599a24b24..2b519389caef 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 @@ -7,14 +7,13 @@ import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.wordpress.android.R -import org.wordpress.android.fluxc.store.StatsStore.StatsTypes +import org.wordpress.android.fluxc.store.StatsStore.StatsType import org.wordpress.android.ui.pages.SnackbarMessageHolder import org.wordpress.android.ui.stats.refresh.NavigationTarget import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.UiModel import org.wordpress.android.ui.stats.refresh.lists.sections.BaseStatsUseCase import org.wordpress.android.ui.stats.refresh.lists.sections.BaseStatsUseCase.UseCaseModel import org.wordpress.android.ui.stats.refresh.utils.StatsSiteProvider -import org.wordpress.android.util.DistinctMutableLiveData import org.wordpress.android.util.PackageUtils import org.wordpress.android.util.combineMap import org.wordpress.android.util.distinct @@ -28,15 +27,19 @@ class BaseListUseCase( private val mainDispatcher: CoroutineDispatcher, private val statsSiteProvider: StatsSiteProvider, private val useCases: List>, - private val getStatsTypes: suspend () -> List, - private val mapUiModel: (useCaseModels: List, showError: (Int) -> Unit) -> UiModel + private val getStatsTypes: suspend () -> List, + private val mapUiModel: ( + useCaseModels: List, + MutableLiveData>, + showError: (Int) -> Unit + ) -> UiModel ) { private val blockListData = combineMap( useCases.associateBy { it.type }.mapValues { entry -> entry.value.liveData } ) - private val statsTypes = DistinctMutableLiveData>(listOf()) - val data: MediatorLiveData = mergeNotNull(statsTypes, blockListData) { insights, map -> - insights.mapNotNull { + private val statsTypes = MutableLiveData>() + val data: MediatorLiveData = mergeNotNull(statsTypes, blockListData) { types, map -> + types.mapNotNull { if (map.containsKey(it)) { map[it] } else { @@ -44,13 +47,14 @@ class BaseListUseCase( } } }.map { useCaseModels -> - mapUiModel(useCaseModels) { message -> + mapUiModel(useCaseModels, mutableNavigationTarget) { message -> mutableSnackbarMessage.postValue(message) } }.distinct() + private val mutableNavigationTarget = MutableLiveData>() val navigationTarget: LiveData> = mergeNotNull( - useCases.map { it.navigationTarget }, + useCases.map { it.navigationTarget } + mutableNavigationTarget, distinct = false ) @@ -70,16 +74,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 { @@ -89,7 +103,6 @@ class BaseListUseCase( fun onCleared() { mutableSnackbarMessage.value = null - statsTypes.clear() blockListData.value = null useCases.forEach { it.clear() } data.value = null diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/StatsBlock.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/StatsBlock.kt index 0a71e078f20f..eef7998ba99f 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/StatsBlock.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/StatsBlock.kt @@ -1,37 +1,43 @@ package org.wordpress.android.ui.stats.refresh.lists -import org.wordpress.android.fluxc.store.StatsStore.StatsTypes +import org.wordpress.android.fluxc.store.StatsStore.StatsType +import org.wordpress.android.ui.stats.refresh.lists.StatsBlock.Type.CONTROL import org.wordpress.android.ui.stats.refresh.lists.StatsBlock.Type.EMPTY import org.wordpress.android.ui.stats.refresh.lists.StatsBlock.Type.ERROR import org.wordpress.android.ui.stats.refresh.lists.StatsBlock.Type.LOADING import org.wordpress.android.ui.stats.refresh.lists.StatsBlock.Type.SUCCESS import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem -sealed class StatsBlock(val type: Type, open val statsTypes: StatsTypes, open val data: List) { +sealed class StatsBlock(val type: Type, open val data: List) { enum class Type { SUCCESS, ERROR, EMPTY, - LOADING + LOADING, + CONTROL } data class Success( - override val statsTypes: StatsTypes, + val statsType: StatsType, override val data: List - ) : StatsBlock(SUCCESS, statsTypes, data) + ) : StatsBlock(SUCCESS, data) data class Error( - override val statsTypes: StatsTypes, + val statsType: StatsType, override val data: List = listOf() - ) : StatsBlock(ERROR, statsTypes, data) + ) : StatsBlock(ERROR, data) data class EmptyBlock( - override val statsTypes: StatsTypes, + val statsType: StatsType, override val data: List - ) : StatsBlock(EMPTY, statsTypes, data) + ) : StatsBlock(EMPTY, data) data class Loading( - override val statsTypes: StatsTypes, + val statsType: StatsType, override val data: List - ) : StatsBlock(LOADING, statsTypes, data) + ) : StatsBlock(LOADING, data) + + data class Control( + override val data: List + ) : StatsBlock(CONTROL, data) } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/StatsBlockAdapter.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/StatsBlockAdapter.kt index 260064dedce9..8b6d67c4dcf8 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/StatsBlockAdapter.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/StatsBlockAdapter.kt @@ -3,6 +3,11 @@ package org.wordpress.android.ui.stats.refresh.lists import android.support.v7.util.DiffUtil import android.support.v7.widget.RecyclerView.Adapter import android.view.ViewGroup +import org.wordpress.android.ui.stats.refresh.lists.StatsBlock.EmptyBlock +import org.wordpress.android.ui.stats.refresh.lists.StatsBlock.Loading +import org.wordpress.android.ui.stats.refresh.lists.StatsBlock.Success +import org.wordpress.android.ui.stats.refresh.lists.StatsBlock.Error +import org.wordpress.android.ui.stats.refresh.lists.StatsBlock.Type.CONTROL import org.wordpress.android.ui.stats.refresh.lists.StatsBlock.Type.EMPTY import org.wordpress.android.ui.stats.refresh.lists.StatsBlock.Type.ERROR import org.wordpress.android.ui.stats.refresh.lists.StatsBlock.Type.LOADING @@ -10,11 +15,13 @@ import org.wordpress.android.ui.stats.refresh.lists.StatsBlock.Type.SUCCESS import org.wordpress.android.ui.stats.refresh.lists.StatsBlock.Type.values import org.wordpress.android.ui.stats.refresh.lists.viewholders.BaseStatsViewHolder import org.wordpress.android.ui.stats.refresh.lists.viewholders.BlockListViewHolder +import org.wordpress.android.ui.stats.refresh.lists.viewholders.ControlViewHolder import org.wordpress.android.ui.stats.refresh.lists.viewholders.LoadingViewHolder import org.wordpress.android.util.image.ImageManager class StatsBlockAdapter(val imageManager: ImageManager) : Adapter() { private var items: List = listOf() + fun update(newItems: List) { val diffResult = DiffUtil.calculateDiff( StatsBlockDiffCallback( @@ -23,7 +30,6 @@ class StatsBlockAdapter(val imageManager: ImageManager) : Adapter BlockListViewHolder(parent, imageManager) LOADING -> LoadingViewHolder(parent, imageManager) + CONTROL -> ControlViewHolder(parent, imageManager) } } @@ -43,8 +50,16 @@ class StatsBlockAdapter(val imageManager: ImageManager) : Adapter) { val item = items[position] when (holder) { - is BlockListViewHolder -> holder.bind(item.statsTypes, item.data) - is LoadingViewHolder -> holder.bind(item.statsTypes, item.data) + is ControlViewHolder -> holder.bind(item.data) + is BlockListViewHolder, + is LoadingViewHolder -> { + when (item) { + is Success -> holder.bind(item.statsType, item.data) + is Loading -> holder.bind(item.statsType, item.data) + is EmptyBlock -> holder.bind(item.statsType, item.data) + is Error -> holder.bind(item.statsType, item.data) + } + } } } 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..525467e9587f 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,11 @@ package org.wordpress.android.ui.stats.refresh.lists import android.support.v7.util.DiffUtil.Callback +import org.wordpress.android.ui.stats.refresh.lists.StatsBlock.Control +import org.wordpress.android.ui.stats.refresh.lists.StatsBlock.EmptyBlock +import org.wordpress.android.ui.stats.refresh.lists.StatsBlock.Loading import org.wordpress.android.ui.stats.refresh.lists.StatsBlock.Success +import org.wordpress.android.ui.stats.refresh.lists.StatsBlock.Error class StatsBlockDiffCallback( private val oldList: List, @@ -11,7 +15,13 @@ class StatsBlockDiffCallback( override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { val newItem = newList[newItemPosition] val oldItem = oldList[oldItemPosition] - return oldItem.statsTypes == newItem.statsTypes + return oldItem.type == newItem.type && when (oldItem) { + is Success -> oldItem.statsType == (newItem as Success).statsType + is EmptyBlock -> oldItem.statsType == (newItem as EmptyBlock).statsType + is Error -> oldItem.statsType == (newItem as Error).statsType + is Loading -> oldItem.statsType == (newItem as Loading).statsType + is Control -> true + } } override fun getOldListSize(): Int = oldList.size @@ -30,7 +40,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 b1b47733df4c..e0b1a70ad91b 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 @@ -15,10 +15,10 @@ import android.view.View import android.view.ViewGroup import dagger.android.support.DaggerFragment import kotlinx.android.synthetic.main.stats_date_selector.* +import kotlinx.android.synthetic.main.stats_empty_view.* import kotlinx.android.synthetic.main.stats_error_view.* import kotlinx.android.synthetic.main.stats_list_fragment.* import org.wordpress.android.R -import org.wordpress.android.ui.stats.refresh.StatsListItemDecoration import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.StatsSection import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.UiModel import org.wordpress.android.ui.stats.refresh.lists.detail.DetailListViewModel @@ -88,6 +88,10 @@ class StatsListFragment : DaggerFragment() { ) ) + statsEmptyView.button.setOnClickListener { + viewModel.onEmptyInsightsButtonClicked() + } + recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { if (!recyclerView.canScrollVertically(1) && dy != 0) { @@ -96,13 +100,17 @@ class StatsListFragment : DaggerFragment() { } }) - select_next_date.setOnClickListener { + nextDateButton.setOnClickListener { viewModel.onNextDateSelected() } - select_previous_date.setOnClickListener { + previousDateButton.setOnClickListener { viewModel.onPreviousDateSelected() } + + statsErrorView.button.setOnClickListener { + viewModel.onRetryClick() + } } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -145,10 +153,13 @@ class StatsListFragment : DaggerFragment() { } is UiModel.Error -> { recyclerView.visibility = View.GONE - actionable_error_view.visibility = View.VISIBLE - actionable_error_view.button.setOnClickListener { - viewModel.onRetryClick() - } + statsErrorView.visibility = View.VISIBLE + statsEmptyView.visibility = View.GONE + } + is UiModel.Empty -> { + recyclerView.visibility = View.GONE + statsEmptyView.visibility = View.VISIBLE + statsErrorView.visibility = View.GONE } } } @@ -159,14 +170,14 @@ class StatsListFragment : DaggerFragment() { if (date_selection_toolbar.visibility != dateSelectorVisibility) { date_selection_toolbar.visibility = dateSelectorVisibility } - selected_date.text = dateSelectorUiModel?.date ?: "" + selectedDateTextView.text = dateSelectorUiModel?.date ?: "" val enablePreviousButton = dateSelectorUiModel?.enableSelectPrevious == true - if (select_previous_date.isEnabled != enablePreviousButton) { - select_previous_date.isEnabled = enablePreviousButton + if (previousDateButton.isEnabled != enablePreviousButton) { + previousDateButton.isEnabled = enablePreviousButton } val enableNextButton = dateSelectorUiModel?.enableSelectNext == true - if (select_next_date.isEnabled != enableNextButton) { - select_next_date.isEnabled = enableNextButton + if (nextDateButton.isEnabled != enableNextButton) { + nextDateButton.isEnabled = enableNextButton } }) @@ -185,11 +196,19 @@ class StatsListFragment : DaggerFragment() { viewModel.listSelected.observe(this, Observer { viewModel.onListSelected() }) + + viewModel.typeMoved?.observe(this, Observer { event -> + event?.getContentIfNotHandled()?.let { + viewModel.onTypeMoved() + } + }) } private fun updateInsights(statsState: List) { recyclerView.visibility = View.VISIBLE - actionable_error_view.visibility = View.GONE + statsErrorView.visibility = View.GONE + statsEmptyView.visibility = View.GONE + val adapter: StatsBlockAdapter if (recyclerView.adapter == null) { adapter = StatsBlockAdapter(imageManager) @@ -197,6 +216,7 @@ class StatsListFragment : DaggerFragment() { } else { adapter = recyclerView.adapter as StatsBlockAdapter } + val layoutManager = recyclerView?.layoutManager val recyclerViewState = layoutManager?.onSaveInstanceState() adapter.update(statsState) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsListItemDecoration.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/StatsListItemDecoration.kt similarity index 95% rename from WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsListItemDecoration.kt rename to WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/StatsListItemDecoration.kt index ad26320b52ee..10fdf97ff384 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/StatsListItemDecoration.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/StatsListItemDecoration.kt @@ -1,4 +1,4 @@ -package org.wordpress.android.ui.stats.refresh +package org.wordpress.android.ui.stats.refresh.lists import android.graphics.Rect import android.support.v7.widget.RecyclerView 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 3ddb76930526..c60bcb98e07c 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 @@ -1,6 +1,7 @@ package org.wordpress.android.ui.stats.refresh.lists import android.arch.lifecycle.LiveData +import android.arch.lifecycle.MutableLiveData import android.support.annotation.StringRes import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.Dispatchers @@ -14,6 +15,7 @@ import org.wordpress.android.ui.stats.refresh.DAY_STATS_USE_CASE import org.wordpress.android.ui.stats.refresh.INSIGHTS_USE_CASE import org.wordpress.android.ui.stats.refresh.MONTH_STATS_USE_CASE import org.wordpress.android.ui.stats.refresh.NavigationTarget +import org.wordpress.android.ui.stats.refresh.NavigationTarget.ViewInsightsManagement import org.wordpress.android.ui.stats.refresh.StatsViewModel.DateSelectorUiModel import org.wordpress.android.ui.stats.refresh.WEEK_STATS_USE_CASE import org.wordpress.android.ui.stats.refresh.YEAR_STATS_USE_CASE @@ -22,9 +24,11 @@ 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 +import org.wordpress.android.util.mergeNotNull import org.wordpress.android.util.throttle import org.wordpress.android.viewmodel.Event import org.wordpress.android.viewmodel.ScopedViewModel @@ -37,7 +41,8 @@ abstract class StatsListViewModel( defaultDispatcher: CoroutineDispatcher, private val statsUseCase: BaseListUseCase, private val analyticsTracker: AnalyticsTrackerWrapper, - private val dateSelector: StatsDateSelector + private val dateSelector: StatsDateSelector, + popupMenuHandler: ItemPopupMenuHandler? = null ) : ScopedViewModel(defaultDispatcher) { private var trackJob: Job? = null private var isInitialized = false @@ -54,7 +59,10 @@ abstract class StatsListViewModel( val selectedDate = dateSelector.selectedDate - val navigationTarget: LiveData> = statsUseCase.navigationTarget + private val mutableNavigationTarget = MutableLiveData>() + val navigationTarget: LiveData> = mergeNotNull( + statsUseCase.navigationTarget, mutableNavigationTarget + ) val listSelected = statsUseCase.listSelected @@ -66,6 +74,8 @@ abstract class StatsListViewModel( it ?: DateSelectorUiModel(false) } + val typeMoved = popupMenuHandler?.typeMoved + override fun onCleared() { statsUseCase.onCleared() super.onCleared() @@ -108,6 +118,10 @@ abstract class StatsListViewModel( dateSelector.updateDateSelector() } + fun onEmptyInsightsButtonClicked() { + mutableNavigationTarget.value = Event(ViewInsightsManagement) + } + fun start() { if (!isInitialized) { isInitialized = true @@ -122,6 +136,13 @@ abstract class StatsListViewModel( sealed class UiModel { data class Success(val data: List) : UiModel() class Error(val message: Int = R.string.stats_loading_error) : UiModel() + object Empty : UiModel() + } + + fun onTypeMoved() { + launch { + statsUseCase.refreshTypes() + } } } @@ -130,8 +151,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/UiModelMapper.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/UiModelMapper.kt index 15ad6c2ef7ba..ae02f7a13836 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/UiModelMapper.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/UiModelMapper.kt @@ -1,69 +1,97 @@ package org.wordpress.android.ui.stats.refresh.lists +import android.arch.lifecycle.MutableLiveData import org.wordpress.android.R.string -import org.wordpress.android.fluxc.store.StatsStore.PostDetailTypes -import org.wordpress.android.fluxc.store.StatsStore.StatsTypes -import org.wordpress.android.fluxc.store.StatsStore.TimeStatsTypes -import org.wordpress.android.ui.stats.refresh.lists.StatsBlock.Error +import org.wordpress.android.fluxc.store.StatsStore.PostDetailType +import org.wordpress.android.fluxc.store.StatsStore.StatsType +import org.wordpress.android.fluxc.store.StatsStore.TimeStatsType +import org.wordpress.android.ui.stats.refresh.NavigationTarget +import org.wordpress.android.ui.stats.refresh.NavigationTarget.ViewInsightsManagement import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.UiModel import org.wordpress.android.ui.stats.refresh.lists.sections.BaseStatsUseCase.UseCaseModel import org.wordpress.android.ui.stats.refresh.lists.sections.BaseStatsUseCase.UseCaseModel.UseCaseState.EMPTY import org.wordpress.android.ui.stats.refresh.lists.sections.BaseStatsUseCase.UseCaseModel.UseCaseState.ERROR import org.wordpress.android.ui.stats.refresh.lists.sections.BaseStatsUseCase.UseCaseModel.UseCaseState.LOADING import org.wordpress.android.ui.stats.refresh.lists.sections.BaseStatsUseCase.UseCaseModel.UseCaseState.SUCCESS +import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.LinkButton +import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.NavigationAction import org.wordpress.android.util.NetworkUtilsWrapper +import org.wordpress.android.viewmodel.Event import javax.inject.Inject class UiModelMapper @Inject constructor(private val networkUtilsWrapper: NetworkUtilsWrapper) { - fun mapInsights(useCaseModels: List, showError: (Int) -> Unit): UiModel { - val allFailing = useCaseModels.isNotEmpty() && useCaseModels.fold(true) { acc, useCaseModel -> - acc && useCaseModel.state == ERROR - } - val allFailingWithoutData = useCaseModels.isNotEmpty() && useCaseModels.fold(true) { acc, useCaseModel -> - acc && useCaseModel.state == ERROR && useCaseModel.data == null - } - return if (!allFailing && !allFailingWithoutData) { - UiModel.Success(useCaseModels.map { useCaseModel -> - when (useCaseModel.state) { - SUCCESS -> StatsBlock.Success(useCaseModel.type, useCaseModel.data ?: listOf()) - ERROR -> Error( - useCaseModel.type, - useCaseModel.stateData ?: useCaseModel.data ?: listOf() - ) - LOADING -> StatsBlock.Loading( - useCaseModel.type, - useCaseModel.stateData ?: useCaseModel.data ?: listOf() - ) - EMPTY -> StatsBlock.EmptyBlock( + fun mapInsights( + useCaseModels: List, + navigationTarget: MutableLiveData>, + showError: (Int) -> Unit + ): UiModel { + if (useCaseModels.isNotEmpty()) { + val allFailing = useCaseModels.fold(true) { acc, useCaseModel -> + acc && useCaseModel.state == ERROR + } + val allFailingWithoutData = useCaseModels.isNotEmpty() && useCaseModels.fold(true) { acc, useCaseModel -> + acc && useCaseModel.state == ERROR && useCaseModel.data == null + } + return if (!allFailing && !allFailingWithoutData) { + val data = useCaseModels.map { useCaseModel -> + when (useCaseModel.state) { + SUCCESS -> StatsBlock.Success(useCaseModel.type, useCaseModel.data ?: listOf()) + ERROR -> StatsBlock.Error( + useCaseModel.type, + useCaseModel.stateData ?: useCaseModel.data ?: listOf() + ) + LOADING -> StatsBlock.Loading( + useCaseModel.type, + useCaseModel.stateData ?: useCaseModel.data ?: listOf() + ) + EMPTY -> StatsBlock.EmptyBlock( + useCaseModel.type, + useCaseModel.stateData ?: useCaseModel.data ?: listOf() + ) + } + }.toMutableList() + + data += StatsBlock.Control(listOf(LinkButton(string.edit, + NavigationAction.create { + navigationTarget.value = Event(ViewInsightsManagement) + } + ))) + UiModel.Success(data) + } else if (!allFailingWithoutData) { + showError(getErrorMessage()) + UiModel.Success(useCaseModels.map { useCaseModel -> + StatsBlock.Error( useCaseModel.type, - useCaseModel.stateData ?: useCaseModel.data ?: listOf() + useCaseModel.data ?: useCaseModel.stateData ?: listOf() ) - } - }) - } else if (!allFailingWithoutData) { - showError(getErrorMessage()) - UiModel.Success(useCaseModels.map { useCaseModel -> - Error( - useCaseModel.type, - useCaseModel.data ?: useCaseModel.stateData ?: listOf() - ) - }) + }) + } else { + UiModel.Error(getErrorMessage()) + } } else { - UiModel.Error(getErrorMessage()) + return UiModel.Empty } } - fun mapTimeStats(useCaseModels: List, showError: (Int) -> Unit): UiModel { - return mapStatsWithOverview(TimeStatsTypes.OVERVIEW, useCaseModels, showError) + fun mapTimeStats( + useCaseModels: List, + navigationTarget: MutableLiveData>, + showError: (Int) -> Unit + ): UiModel { + return mapStatsWithOverview(TimeStatsType.OVERVIEW, useCaseModels, showError) } - fun mapDetailStats(useCaseModels: List, showError: (Int) -> Unit): UiModel { - return mapStatsWithOverview(PostDetailTypes.POST_OVERVIEW, useCaseModels, showError) + fun mapDetailStats( + useCaseModels: List, + navigationTarget: MutableLiveData>, + showError: (Int) -> Unit + ): UiModel { + return mapStatsWithOverview(PostDetailType.POST_OVERVIEW, useCaseModels, showError) } private fun mapStatsWithOverview( - overViewType: StatsTypes, + overViewType: StatsType, useCaseModels: List, showError: (Int) -> Unit ): UiModel { @@ -73,33 +101,37 @@ class UiModelMapper } val overviewHasData = useCaseModels.any { it.type == overViewType && it.data != null } return if (!allFailing) { - UiModel.Success(useCaseModels.mapNotNull { useCaseModel -> - if ((useCaseModel.type == overViewType) && useCaseModel.data != null) { - StatsBlock.Success(useCaseModel.type, useCaseModel.data) - } else { - when (useCaseModel.state) { - SUCCESS -> StatsBlock.Success(useCaseModel.type, useCaseModel.data ?: listOf()) - ERROR -> useCaseModel.stateData?.let { - Error( - useCaseModel.type, - useCaseModel.stateData - ) - } - LOADING -> useCaseModel.stateData?.let { - StatsBlock.Loading( - useCaseModel.type, - useCaseModel.stateData - ) - } - EMPTY -> useCaseModel.stateData?.let { - StatsBlock.EmptyBlock( - useCaseModel.type, - useCaseModel.stateData - ) + if (useCaseModels.isNotEmpty()) { + UiModel.Success(useCaseModels.mapNotNull { useCaseModel -> + if ((useCaseModel.type == overViewType) && useCaseModel.data != null) { + StatsBlock.Success(useCaseModel.type, useCaseModel.data) + } else { + when (useCaseModel.state) { + SUCCESS -> StatsBlock.Success(useCaseModel.type, useCaseModel.data ?: listOf()) + ERROR -> useCaseModel.stateData?.let { + StatsBlock.Error( + useCaseModel.type, + useCaseModel.stateData + ) + } + LOADING -> useCaseModel.stateData?.let { + StatsBlock.Loading( + useCaseModel.type, + useCaseModel.stateData + ) + } + EMPTY -> useCaseModel.stateData?.let { + StatsBlock.EmptyBlock( + useCaseModel.type, + useCaseModel.stateData + ) + } } } - } - }) + }) + } else { + UiModel.Empty + } } else if (overviewHasData) { showError(getErrorMessage()) UiModel.Success(useCaseModels.mapNotNull { useCaseModel -> @@ -107,7 +139,7 @@ class UiModelMapper StatsBlock.Success(useCaseModel.type, useCaseModel.data) } else { useCaseModel.stateData?.let { - Error( + StatsBlock.Error( useCaseModel.type, useCaseModel.stateData ) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/detail/PostAverageViewsPerDayUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/detail/PostAverageViewsPerDayUseCase.kt index fceb81db30bf..b06797ac879a 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/detail/PostAverageViewsPerDayUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/detail/PostAverageViewsPerDayUseCase.kt @@ -3,7 +3,7 @@ package org.wordpress.android.ui.stats.refresh.lists.detail import kotlinx.coroutines.CoroutineDispatcher import org.wordpress.android.R.string import org.wordpress.android.fluxc.model.stats.PostDetailStatsModel -import org.wordpress.android.fluxc.store.StatsStore.PostDetailTypes +import org.wordpress.android.fluxc.store.StatsStore.PostDetailType import org.wordpress.android.fluxc.store.stats.PostDetailStore import org.wordpress.android.modules.UI_THREAD import org.wordpress.android.ui.stats.refresh.NavigationTarget.ViewDayAverageStats @@ -33,7 +33,7 @@ class PostAverageViewsPerDayUseCase( private val postDetailMapper: PostDetailMapper, private val useCaseMode: UseCaseMode ) : BaseStatsUseCase( - PostDetailTypes.AVERAGE_VIEWS_PER_DAY, + PostDetailType.AVERAGE_VIEWS_PER_DAY, mainDispatcher, ExpandedYearUiState() ) { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/detail/PostDayViewsUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/detail/PostDayViewsUseCase.kt index 778327ca4bd2..bc9c05f87727 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/detail/PostDayViewsUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/detail/PostDayViewsUseCase.kt @@ -4,7 +4,7 @@ import kotlinx.coroutines.CoroutineDispatcher import org.wordpress.android.R.string import org.wordpress.android.fluxc.model.stats.PostDetailStatsModel import org.wordpress.android.fluxc.network.utils.StatsGranularity.DAYS -import org.wordpress.android.fluxc.store.StatsStore.PostDetailTypes +import org.wordpress.android.fluxc.store.StatsStore.PostDetailType import org.wordpress.android.fluxc.store.stats.PostDetailStore import org.wordpress.android.modules.UI_THREAD import org.wordpress.android.ui.stats.refresh.lists.StatsListViewModel.StatsSection.DETAIL @@ -30,7 +30,7 @@ class PostDayViewsUseCase private val statsPostProvider: StatsPostProvider, private val postDetailStore: PostDetailStore ) : BaseStatsUseCase( - PostDetailTypes.POST_OVERVIEW, + PostDetailType.POST_OVERVIEW, mainDispatcher, UiState() ) { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/detail/PostHeaderUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/detail/PostHeaderUseCase.kt index 797fc6726f68..c8a0fd72b94f 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/detail/PostHeaderUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/detail/PostHeaderUseCase.kt @@ -2,7 +2,7 @@ package org.wordpress.android.ui.stats.refresh.lists.detail import kotlinx.coroutines.CoroutineDispatcher import org.wordpress.android.R.string -import org.wordpress.android.fluxc.store.StatsStore.PostDetailTypes +import org.wordpress.android.fluxc.store.StatsStore.PostDetailType 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 @@ -16,7 +16,7 @@ class PostHeaderUseCase @Named(UI_THREAD) private val mainDispatcher: CoroutineDispatcher, private val statsPostProvider: StatsPostProvider ) : StatelessUseCase( - PostDetailTypes.POST_HEADER, + PostDetailType.POST_HEADER, mainDispatcher ) { override suspend fun loadCachedData(): String? { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/detail/PostMonthsAndYearsUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/detail/PostMonthsAndYearsUseCase.kt index be4afc2f9a0d..f0aacf04b05d 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/detail/PostMonthsAndYearsUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/detail/PostMonthsAndYearsUseCase.kt @@ -3,7 +3,7 @@ package org.wordpress.android.ui.stats.refresh.lists.detail import kotlinx.coroutines.CoroutineDispatcher import org.wordpress.android.R.string import org.wordpress.android.fluxc.model.stats.PostDetailStatsModel -import org.wordpress.android.fluxc.store.StatsStore.PostDetailTypes +import org.wordpress.android.fluxc.store.StatsStore.PostDetailType import org.wordpress.android.fluxc.store.stats.PostDetailStore import org.wordpress.android.modules.UI_THREAD import org.wordpress.android.ui.stats.refresh.NavigationTarget.ViewMonthsAndYearsStats @@ -33,7 +33,7 @@ class PostMonthsAndYearsUseCase( private val postDetailMapper: PostDetailMapper, private val useCaseMode: UseCaseMode ) : BaseStatsUseCase( - PostDetailTypes.MONTHS_AND_YEARS, + PostDetailType.MONTHS_AND_YEARS, mainDispatcher, ExpandedYearUiState() ) { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/detail/PostRecentWeeksUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/detail/PostRecentWeeksUseCase.kt index 19910f03b75b..5f9c197edb02 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/detail/PostRecentWeeksUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/detail/PostRecentWeeksUseCase.kt @@ -3,7 +3,7 @@ package org.wordpress.android.ui.stats.refresh.lists.detail import kotlinx.coroutines.CoroutineDispatcher import org.wordpress.android.R.string import org.wordpress.android.fluxc.model.stats.PostDetailStatsModel -import org.wordpress.android.fluxc.store.StatsStore.PostDetailTypes +import org.wordpress.android.fluxc.store.StatsStore.PostDetailType import org.wordpress.android.fluxc.store.stats.PostDetailStore import org.wordpress.android.modules.UI_THREAD import org.wordpress.android.ui.stats.refresh.NavigationTarget.ViewRecentWeeksStats @@ -33,7 +33,7 @@ class PostRecentWeeksUseCase( private val postDetailMapper: PostDetailMapper, private val useCaseMode: UseCaseMode ) : BaseStatsUseCase( - PostDetailTypes.CLICKS_BY_WEEKS, + PostDetailType.CLICKS_BY_WEEKS, mainDispatcher, ExpandedWeekUiState() ) { diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/detail/StatsDetailFragment.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/detail/StatsDetailFragment.kt index 0a39df843538..99eda6c04921 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/detail/StatsDetailFragment.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/detail/StatsDetailFragment.kt @@ -89,14 +89,14 @@ class StatsDetailFragment : DaggerFragment() { if (date_selection_toolbar.visibility != dateSelectorVisibility) { date_selection_toolbar.visibility = dateSelectorVisibility } - selected_date.text = dateSelectorUiModel?.date ?: "" + selectedDateTextView.text = dateSelectorUiModel?.date ?: "" val enablePreviousButton = dateSelectorUiModel?.enableSelectPrevious == true - if (select_previous_date.isEnabled != enablePreviousButton) { - select_previous_date.isEnabled = enablePreviousButton + if (previousDateButton.isEnabled != enablePreviousButton) { + previousDateButton.isEnabled = enablePreviousButton } val enableNextButton = dateSelectorUiModel?.enableSelectNext == true - if (select_next_date.isEnabled != enableNextButton) { - select_next_date.isEnabled = enableNextButton + if (nextDateButton.isEnabled != enableNextButton) { + nextDateButton.isEnabled = enableNextButton } }) } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/BaseStatsUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/BaseStatsUseCase.kt index cbdb02a92bdf..2089ff34dea6 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/BaseStatsUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/BaseStatsUseCase.kt @@ -6,7 +6,7 @@ import android.arch.lifecycle.MutableLiveData import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.withContext import org.wordpress.android.R -import org.wordpress.android.fluxc.store.StatsStore.StatsTypes +import org.wordpress.android.fluxc.store.StatsStore.StatsType import org.wordpress.android.ui.stats.refresh.NavigationTarget import org.wordpress.android.ui.stats.refresh.lists.sections.BaseStatsUseCase.State.Data import org.wordpress.android.ui.stats.refresh.lists.sections.BaseStatsUseCase.State.Empty @@ -27,7 +27,7 @@ import org.wordpress.android.viewmodel.Event * Do not override this class directly. Use StatefulUseCase or StatelessUseCase instead. */ abstract class BaseStatsUseCase( - val type: StatsTypes, + val type: StatsType, private val mainDispatcher: CoroutineDispatcher, private val defaultUiState: UI_STATE ) { @@ -200,7 +200,7 @@ abstract class BaseStatsUseCase( } data class UseCaseModel( - val type: StatsTypes, + val type: StatsType, val data: List? = null, val stateData: List? = null, val state: UseCaseState = SUCCESS @@ -216,7 +216,7 @@ abstract class BaseStatsUseCase( * @param defaultUiState default value the UI state should have when the screen first loads */ abstract class StatefulUseCase( - type: StatsTypes, + type: StatsType, mainDispatcher: CoroutineDispatcher, private val defaultUiState: UI_STATE ) : BaseStatsUseCase(type, mainDispatcher, defaultUiState) { @@ -238,7 +238,7 @@ abstract class BaseStatsUseCase( * These blocks don't have only one UI state and it doesn't change. */ abstract class StatelessUseCase( - type: StatsTypes, + type: StatsType, mainDispatcher: CoroutineDispatcher ) : BaseStatsUseCase(type, mainDispatcher, NotUsedUiState) { /** 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..3c1dbb5addcf 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 @@ -16,12 +16,13 @@ import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Expan import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Header import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Information import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Link +import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.LinkButton import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.ListItem 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 @@ -35,12 +36,13 @@ import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Type. import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Type.HEADER import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Type.INFO import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Type.LINK +import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Type.LINK_BUTTON 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.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 @@ -57,13 +59,14 @@ import org.wordpress.android.ui.stats.refresh.lists.sections.viewholders.Expanda import org.wordpress.android.ui.stats.refresh.lists.sections.viewholders.FourColumnsViewHolder import org.wordpress.android.ui.stats.refresh.lists.sections.viewholders.HeaderViewHolder import org.wordpress.android.ui.stats.refresh.lists.sections.viewholders.InformationViewHolder +import org.wordpress.android.ui.stats.refresh.lists.sections.viewholders.LinkButtonViewHolder import org.wordpress.android.ui.stats.refresh.lists.sections.viewholders.LinkViewHolder import org.wordpress.android.ui.stats.refresh.lists.sections.viewholders.ListItemViewHolder 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 @@ -121,6 +124,7 @@ class BlockListAdapter(val imageManager: ImageManager) : Adapter ActivityViewHolder(parent) REFERRED_ITEM -> ReferredItemViewHolder(parent) QUICK_SCAN_ITEM -> QuickScanItemViewHolder(parent) + LINK_BUTTON -> LinkButtonViewHolder(parent) } } @@ -155,6 +159,7 @@ class BlockListAdapter(val imageManager: ImageManager) : Adapter holder.bind(item as LoadingItem) is ReferredItemViewHolder -> holder.bind(item as ReferredItem) is QuickScanItemViewHolder -> holder.bind(item as QuickScanItem) + is LinkButtonViewHolder -> holder.bind(item as LinkButton) } } 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 9d06dbcaf409..626760889bb8 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 @@ -13,6 +14,7 @@ import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Type. import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Type.HEADER import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Type.INFO import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Type.LINK +import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.Type.LINK_BUTTON 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.LOADING_ITEM @@ -51,10 +53,15 @@ sealed class BlockListItem(val type: Type) { LOADING_ITEM, ACTIVITY_ITEM, REFERRED_ITEM, - QUICK_SCAN_ITEM + QUICK_SCAN_ITEM, + LINK_BUTTON } - 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) @@ -134,8 +141,12 @@ sealed class BlockListItem(val type: Type) { @DrawableRes val icon: Int? = null, @StringRes val text: Int, val navigateAction: NavigationAction - ) : - BlockListItem(LINK) + ) : BlockListItem(LINK) + + data class LinkButton( + @StringRes val text: Int, + val navigateAction: NavigationAction + ) : BlockListItem(LINK_BUTTON) data class BarChartItem( val entries: List, diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/GranularStatefulUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/GranularStatefulUseCase.kt index 60c8b17ce4df..d79c978b6911 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/GranularStatefulUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/GranularStatefulUseCase.kt @@ -4,14 +4,14 @@ import kotlinx.coroutines.CoroutineDispatcher import org.wordpress.android.R import org.wordpress.android.fluxc.model.SiteModel import org.wordpress.android.fluxc.network.utils.StatsGranularity -import org.wordpress.android.fluxc.store.StatsStore.StatsTypes +import org.wordpress.android.fluxc.store.StatsStore.StatsType import org.wordpress.android.ui.stats.refresh.lists.sections.BaseStatsUseCase.StatefulUseCase import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem import org.wordpress.android.ui.stats.refresh.utils.StatsSiteProvider import java.util.Date abstract class GranularStatefulUseCase( - type: StatsTypes, + type: StatsType, mainDispatcher: CoroutineDispatcher, val statsSiteProvider: StatsSiteProvider, val selectedDateProvider: SelectedDateProvider, diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/GranularStatelessUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/GranularStatelessUseCase.kt index d8a4dfd126d2..d63c796520a4 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/GranularStatelessUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/GranularStatelessUseCase.kt @@ -4,14 +4,14 @@ import kotlinx.coroutines.CoroutineDispatcher import org.wordpress.android.R import org.wordpress.android.fluxc.model.SiteModel import org.wordpress.android.fluxc.network.utils.StatsGranularity -import org.wordpress.android.fluxc.store.StatsStore.StatsTypes +import org.wordpress.android.fluxc.store.StatsStore.StatsType 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.utils.StatsSiteProvider import java.util.Date abstract class GranularStatelessUseCase( - type: StatsTypes, + type: StatsType, mainDispatcher: CoroutineDispatcher, val selectedDateProvider: SelectedDateProvider, val statsSiteProvider: StatsSiteProvider, diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/AuthorsUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/AuthorsUseCase.kt index 9421c52d70f7..f9755680c84f 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/AuthorsUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/AuthorsUseCase.kt @@ -8,7 +8,7 @@ import org.wordpress.android.fluxc.model.SiteModel import org.wordpress.android.fluxc.model.stats.LimitMode import org.wordpress.android.fluxc.model.stats.time.AuthorsModel import org.wordpress.android.fluxc.network.utils.StatsGranularity -import org.wordpress.android.fluxc.store.StatsStore.TimeStatsTypes.AUTHORS +import org.wordpress.android.fluxc.store.StatsStore.TimeStatsType.AUTHORS import org.wordpress.android.fluxc.store.stats.time.AuthorsStore import org.wordpress.android.modules.UI_THREAD import org.wordpress.android.ui.stats.StatsConstants diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/ClicksUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/ClicksUseCase.kt index 7ecca14b38c0..3d59e419c1d2 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/ClicksUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/ClicksUseCase.kt @@ -8,7 +8,7 @@ import org.wordpress.android.fluxc.model.SiteModel import org.wordpress.android.fluxc.model.stats.LimitMode import org.wordpress.android.fluxc.model.stats.time.ClicksModel import org.wordpress.android.fluxc.network.utils.StatsGranularity -import org.wordpress.android.fluxc.store.StatsStore.TimeStatsTypes.CLICKS +import org.wordpress.android.fluxc.store.StatsStore.TimeStatsType.CLICKS import org.wordpress.android.fluxc.store.stats.time.ClicksStore import org.wordpress.android.modules.UI_THREAD import org.wordpress.android.ui.stats.refresh.NavigationTarget.ViewClicks diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/CountryViewsUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/CountryViewsUseCase.kt index 51b442505465..f299ff9e9fc2 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/CountryViewsUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/CountryViewsUseCase.kt @@ -8,7 +8,7 @@ import org.wordpress.android.fluxc.model.SiteModel import org.wordpress.android.fluxc.model.stats.LimitMode import org.wordpress.android.fluxc.model.stats.time.CountryViewsModel import org.wordpress.android.fluxc.network.utils.StatsGranularity -import org.wordpress.android.fluxc.store.StatsStore.TimeStatsTypes.COUNTRIES +import org.wordpress.android.fluxc.store.StatsStore.TimeStatsType.COUNTRIES import org.wordpress.android.fluxc.store.stats.time.CountryViewsStore import org.wordpress.android.modules.UI_THREAD import org.wordpress.android.ui.stats.refresh.NavigationTarget.ViewCountries diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/OverviewUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/OverviewUseCase.kt index 8c3f83876f43..eb92514d4fca 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/OverviewUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/OverviewUseCase.kt @@ -6,7 +6,7 @@ import org.wordpress.android.analytics.AnalyticsTracker import org.wordpress.android.fluxc.model.stats.LimitMode import org.wordpress.android.fluxc.model.stats.time.VisitsAndViewsModel import org.wordpress.android.fluxc.network.utils.StatsGranularity -import org.wordpress.android.fluxc.store.StatsStore.TimeStatsTypes.OVERVIEW +import org.wordpress.android.fluxc.store.StatsStore.TimeStatsType.OVERVIEW import org.wordpress.android.fluxc.store.stats.time.VisitsAndViewsStore import org.wordpress.android.modules.UI_THREAD import org.wordpress.android.ui.stats.refresh.lists.sections.BaseStatsUseCase.StatefulUseCase diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/PostsAndPagesUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/PostsAndPagesUseCase.kt index aae4b9af1823..182d7a4df514 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/PostsAndPagesUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/PostsAndPagesUseCase.kt @@ -11,7 +11,7 @@ import org.wordpress.android.fluxc.model.stats.time.PostAndPageViewsModel.ViewsT import org.wordpress.android.fluxc.model.stats.time.PostAndPageViewsModel.ViewsType.PAGE import org.wordpress.android.fluxc.model.stats.time.PostAndPageViewsModel.ViewsType.POST import org.wordpress.android.fluxc.network.utils.StatsGranularity -import org.wordpress.android.fluxc.store.StatsStore.TimeStatsTypes.POSTS_AND_PAGES +import org.wordpress.android.fluxc.store.StatsStore.TimeStatsType.POSTS_AND_PAGES import org.wordpress.android.fluxc.store.stats.time.PostAndPageViewsStore import org.wordpress.android.modules.UI_THREAD import org.wordpress.android.ui.stats.StatsConstants.ITEM_TYPE_HOME_PAGE diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/ReferrersUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/ReferrersUseCase.kt index 0c761ff1a1a4..870f5224c848 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/ReferrersUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/ReferrersUseCase.kt @@ -8,7 +8,7 @@ import org.wordpress.android.fluxc.model.SiteModel import org.wordpress.android.fluxc.model.stats.LimitMode import org.wordpress.android.fluxc.model.stats.time.ReferrersModel import org.wordpress.android.fluxc.network.utils.StatsGranularity -import org.wordpress.android.fluxc.store.StatsStore.TimeStatsTypes.REFERRERS +import org.wordpress.android.fluxc.store.StatsStore.TimeStatsType.REFERRERS import org.wordpress.android.fluxc.store.stats.time.ReferrersStore import org.wordpress.android.modules.UI_THREAD import org.wordpress.android.ui.stats.refresh.NavigationTarget.ViewReferrers diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/SearchTermsUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/SearchTermsUseCase.kt index 3ca0f40534db..558efbe85bdb 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/SearchTermsUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/SearchTermsUseCase.kt @@ -8,7 +8,7 @@ import org.wordpress.android.fluxc.model.SiteModel import org.wordpress.android.fluxc.model.stats.LimitMode import org.wordpress.android.fluxc.model.stats.time.SearchTermsModel import org.wordpress.android.fluxc.network.utils.StatsGranularity -import org.wordpress.android.fluxc.store.StatsStore.TimeStatsTypes.SEARCH_TERMS +import org.wordpress.android.fluxc.store.StatsStore.TimeStatsType.SEARCH_TERMS import org.wordpress.android.fluxc.store.stats.time.SearchTermsStore import org.wordpress.android.modules.UI_THREAD import org.wordpress.android.ui.stats.refresh.NavigationTarget.ViewSearchTerms diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/VideoPlaysUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/VideoPlaysUseCase.kt index f546e695f8f9..5f1c3a8e4d86 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/VideoPlaysUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/granular/usecases/VideoPlaysUseCase.kt @@ -8,7 +8,7 @@ import org.wordpress.android.fluxc.model.SiteModel import org.wordpress.android.fluxc.model.stats.LimitMode import org.wordpress.android.fluxc.model.stats.time.VideoPlaysModel import org.wordpress.android.fluxc.network.utils.StatsGranularity -import org.wordpress.android.fluxc.store.StatsStore.TimeStatsTypes.VIDEOS +import org.wordpress.android.fluxc.store.StatsStore.TimeStatsType.VIDEOS import org.wordpress.android.fluxc.store.stats.time.VideoPlaysStore import org.wordpress.android.modules.UI_THREAD import org.wordpress.android.ui.stats.refresh.NavigationTarget.ViewUrl diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/InsightsMenuAdapter.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/InsightsMenuAdapter.kt new file mode 100644 index 000000000000..b2a4735614d0 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/InsightsMenuAdapter.kt @@ -0,0 +1,73 @@ +package org.wordpress.android.ui.stats.refresh.lists.sections.insights + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.BaseAdapter +import android.widget.ImageView +import android.widget.TextView + +import org.wordpress.android.R +import org.wordpress.android.ui.stats.refresh.lists.sections.insights.InsightsMenuAdapter.InsightsMenuItem.DOWN +import org.wordpress.android.ui.stats.refresh.lists.sections.insights.InsightsMenuAdapter.InsightsMenuItem.REMOVE +import org.wordpress.android.ui.stats.refresh.lists.sections.insights.InsightsMenuAdapter.InsightsMenuItem.UP + +class InsightsMenuAdapter(context: Context, isUpVisible: Boolean, isDownVisible: Boolean) : BaseAdapter() { + private val mInflater: LayoutInflater = LayoutInflater.from(context) + private val items = listOfNotNull(if (isUpVisible) UP else null, if (isDownVisible) DOWN else null, REMOVE) + + override fun getCount(): Int { + return items.size + } + + override fun getItem(position: Int): Any { + return items[position] + } + + override fun getItemId(position: Int): Long { + return items[position].id + } + + override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { + var view = convertView + val holder: MenuHolder + if (view == null) { + view = mInflater.inflate(R.layout.stats_insights_popup_menu_item, parent, false) + holder = MenuHolder(view!!) + view.tag = holder + } else { + holder = view.tag as MenuHolder + } + + val textRes: Int + val iconRes: Int + when (items[position]) { + UP -> { + textRes = R.string.stats_menu_move_up + iconRes = R.drawable.ic_arrow_up_grey_dark_24dp + } + DOWN -> { + textRes = R.string.stats_menu_move_down + iconRes = R.drawable.ic_arrow_down_grey_dark_24dp + } + REMOVE -> { + textRes = R.string.stats_menu_remove + iconRes = R.drawable.ic_trash_grey_dark_24dp + } + } + + holder.text.setText(textRes) + holder.icon.setImageResource(iconRes) + return view + } + + internal inner class MenuHolder(view: View) { + val text: TextView = view.findViewById(R.id.text) + val icon: ImageView = view.findViewById(R.id.image) + } + + enum class InsightsMenuItem(val id: Long) { + UP(0), DOWN(1), REMOVE(2) + } +} diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/management/AddedInsightViewHolder.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/management/AddedInsightViewHolder.kt new file mode 100644 index 000000000000..fecb7c741dbf --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/management/AddedInsightViewHolder.kt @@ -0,0 +1,52 @@ +package org.wordpress.android.ui.stats.refresh.lists.sections.insights.management + +import android.support.v4.view.MotionEventCompat +import android.support.v7.widget.RecyclerView.ViewHolder +import android.view.MotionEvent +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.ui.stats.refresh.lists.sections.insights.management.InsightsManagementViewModel.InsightModel + +class AddedInsightViewHolder( + val parent: ViewGroup, + private val onDragStarted: (viewHolder: ViewHolder) -> Unit, + private val onButtonClicked: (InsightModel) -> Unit +) : InsightsManagementViewHolder(parent) { + private val title: TextView = itemView.findViewById(R.id.itemTitle) + private val managementButton: ImageButton = itemView.findViewById(R.id.insightsManagementItemButton) + private val dragAndDropButton: View = itemView.findViewById(R.id.dragAndDropItemButton) + private val divider: View = itemView.findViewById(R.id.divider) + + override fun bind(insight: InsightModel, isLast: Boolean) { + title.setText(insight.name) + + managementButton.setImageResource(R.drawable.ic_remove_circle) + managementButton.setOnClickListener { + managementButton.setOnClickListener(null) + onButtonClicked(insight) + } + + dragAndDropButton.setOnTouchListener { _, event -> + if (MotionEventCompat.isFromSource(event, MotionEvent.ACTION_DOWN)) { + onDragStarted(this) + itemView.elevation = 10f + } + return@setOnTouchListener true + } + + dragAndDropButton.visibility = View.VISIBLE + + updateDividerVisibility(isLast) + } + + fun updateDividerVisibility(isLast: Boolean) { + divider.visibility = if (isLast) View.GONE else View.VISIBLE + } + + fun onDragFinished() { + itemView.elevation = 0f + } +} diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/management/InsightsManagementActivity.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/management/InsightsManagementActivity.kt new file mode 100644 index 000000000000..823d47df9ba3 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/management/InsightsManagementActivity.kt @@ -0,0 +1,28 @@ +package org.wordpress.android.ui.stats.refresh.lists.sections.insights.management + +import android.os.Bundle +import android.support.v7.app.AppCompatActivity +import android.view.MenuItem +import org.wordpress.android.R + +class InsightsManagementActivity : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setContentView(R.layout.insights_management_activity) + + supportActionBar?.let { + it.setHomeButtonEnabled(true) + it.setDisplayHomeAsUpEnabled(true) + it.setHomeAsUpIndicator(R.drawable.ic_close_white_24dp) + } + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + if (item.itemId == android.R.id.home) { + onBackPressed() + return true + } + return super.onOptionsItemSelected(item) + } +} diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/management/InsightsManagementAdapter.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/management/InsightsManagementAdapter.kt new file mode 100644 index 000000000000..f0861ae66c36 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/management/InsightsManagementAdapter.kt @@ -0,0 +1,89 @@ +package org.wordpress.android.ui.stats.refresh.lists.sections.insights.management + +import android.support.v7.util.DiffUtil +import android.support.v7.util.DiffUtil.Callback +import android.support.v7.widget.RecyclerView.Adapter +import android.support.v7.widget.RecyclerView.ViewHolder +import android.view.ViewGroup +import org.wordpress.android.ui.stats.refresh.lists.sections.insights.management.InsightsManagementViewModel.InsightModel +import org.wordpress.android.ui.stats.refresh.lists.sections.insights.management.InsightsManagementViewModel.InsightModel.Status.ADDED +import org.wordpress.android.ui.stats.refresh.lists.sections.insights.management.InsightsManagementViewModel.InsightModel.Status.REMOVED +import java.util.Collections + +class InsightsManagementAdapter( + private val onItemButtonClicked: (InsightModel) -> Unit, + private val onDragStarted: (viewHolder: ViewHolder) -> Unit, + private val onDragFinished: (List) -> Unit +) : Adapter(), ItemTouchHelperAdapter { + private var items = ArrayList() + + override fun onCreateViewHolder(parent: ViewGroup, itemType: Int): InsightsManagementViewHolder { + val type = InsightModel.Status.values()[itemType] + return when (type) { + ADDED -> AddedInsightViewHolder(parent, onDragStarted, onItemButtonClicked) + REMOVED -> RemovedInsightViewHolder(parent, onItemButtonClicked) + } + } + + override fun onBindViewHolder(holder: InsightsManagementViewHolder, position: Int) { + holder.bind(items[position], position == items.size - 1) + } + + override fun getItemViewType(position: Int): Int { + return items[position].type.ordinal + } + + override fun onItemMoved(originalViewHolder: ViewHolder, newViewHolder: ViewHolder) { + val fromPosition = originalViewHolder.adapterPosition + val toPosition = newViewHolder.adapterPosition + if (fromPosition < toPosition) { + for (i in fromPosition until toPosition) { + Collections.swap(items, i, i + 1) + } + } else { + for (i in fromPosition downTo toPosition + 1) { + Collections.swap(items, i, i - 1) + } + } + notifyItemMoved(fromPosition, toPosition) + + (originalViewHolder as? AddedInsightViewHolder)?.updateDividerVisibility(toPosition == items.size - 1) + (newViewHolder as? AddedInsightViewHolder)?.updateDividerVisibility(fromPosition == items.size - 1) + } + + override fun onDragFinished(viewHolder: ViewHolder) { + onDragFinished.invoke(items) + + (viewHolder as? AddedInsightViewHolder)?.onDragFinished() + } + + override fun getItemCount(): Int = items.size + + fun update(newItems: List) { + val diffResult = DiffUtil.calculateDiff(InsightModelDiffCallback(items, newItems)) + items = ArrayList(newItems) + diffResult.dispatchUpdatesTo(this) + } +} + +interface ItemTouchHelperAdapter { + fun onItemMoved(originalViewHolder: ViewHolder, newViewHolder: ViewHolder) + fun onDragFinished(viewHolder: ViewHolder) +} + +class InsightModelDiffCallback( + private val oldList: List, + private val newList: List +) : Callback() { + override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { + return oldList[oldItemPosition].insightType == newList[newItemPosition].insightType + } + + override fun getOldListSize(): Int = oldList.size + + override fun getNewListSize(): Int = newList.size + + override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { + return oldList[oldItemPosition] == newList[newItemPosition] + } +} diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/management/InsightsManagementFragment.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/management/InsightsManagementFragment.kt new file mode 100644 index 000000000000..bd8c990ba34b --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/management/InsightsManagementFragment.kt @@ -0,0 +1,144 @@ +package org.wordpress.android.ui.stats.refresh.lists.sections.insights.management + +import android.arch.lifecycle.Observer +import android.arch.lifecycle.ViewModelProvider +import android.arch.lifecycle.ViewModelProviders +import android.os.Bundle +import android.support.v4.app.FragmentActivity +import android.support.v7.widget.LinearLayoutManager +import android.support.v7.widget.helper.ItemTouchHelper +import android.view.LayoutInflater +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem +import android.view.View +import android.view.ViewGroup +import dagger.android.support.DaggerFragment +import kotlinx.android.synthetic.main.insights_management_fragment.* +import javax.inject.Inject +import android.animation.LayoutTransition +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import org.wordpress.android.R +import org.wordpress.android.ui.stats.refresh.lists.sections.insights.management.InsightsManagementViewModel.InsightModel + +class InsightsManagementFragment : DaggerFragment() { + @Inject lateinit var viewModelFactory: ViewModelProvider.Factory + private lateinit var viewModel: InsightsManagementViewModel + private lateinit var addedInsightsTouchHelper: ItemTouchHelper + + private var menu: Menu? = null + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + setHasOptionsMenu(true) + return inflater.inflate(R.layout.insights_management_fragment, container, false) + } + + override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { + super.onCreateOptionsMenu(menu, inflater) + + inflater?.inflate(R.menu.menu_insights_management, menu) + this.menu = menu + + initializeViews() + initializeViewModels(requireActivity()) + + enableAnimations() + } + + private fun enableAnimations() { + viewModel.launch { + delay(500) + val transition = LayoutTransition() + transition.disableTransitionType(LayoutTransition.DISAPPEARING) + transition.disableTransitionType(LayoutTransition.APPEARING) + transition.enableTransitionType(LayoutTransition.CHANGING) + insightsManagementContainer.layoutTransition = transition + } + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + if (item.itemId == R.id.save_insights) { + viewModel.onSaveInsights() + } + return true + } + + private fun initializeViews() { + removedInsights.layoutManager = LinearLayoutManager(requireActivity(), LinearLayoutManager.VERTICAL, false) + addedInsights.layoutManager = LinearLayoutManager(requireActivity(), LinearLayoutManager.VERTICAL, false) + } + + private fun initializeViewModels(activity: FragmentActivity) { + viewModel = ViewModelProviders.of(activity, viewModelFactory).get(InsightsManagementViewModel::class.java) + viewModel.start() + + setupObservers() + } + + private fun setupObservers() { + viewModel.removedInsights.observe(this, Observer { + it?.let { items -> + updateRemovedInsights(items) + + if (items.isEmpty()) { + addInsightsHeader.visibility = View.GONE + } else { + addInsightsHeader.visibility = View.VISIBLE + } + } + }) + + viewModel.addedInsights.observe(this, Observer { + it?.let { items -> + updateAddedInsights(items) + + if (items.isEmpty()) { + addedInsightsInfo.visibility = View.GONE + } else { + addedInsightsInfo.visibility = View.VISIBLE + } + } + }) + + viewModel.closeInsightsManagement.observe(this, Observer { + requireActivity().finish() + }) + + viewModel.isMenuVisible.observe(this, Observer { isMenuVisible -> + isMenuVisible?.let { + menu?.findItem(R.id.save_insights)?.isVisible = isMenuVisible + } + }) + } + + private fun updateRemovedInsights(insights: List) { + var adapter = removedInsights.adapter as? InsightsManagementAdapter + if (adapter == null) { + adapter = InsightsManagementAdapter( + { item -> viewModel.onItemButtonClicked(item) }, + { viewHolder -> addedInsightsTouchHelper.startDrag(viewHolder) }, + { list -> viewModel.onAddedInsightsReordered(list) } + ) + removedInsights.adapter = adapter + } + adapter.update(insights) + } + + private fun updateAddedInsights(insights: List) { + var adapter = addedInsights.adapter as? InsightsManagementAdapter + if (adapter == null) { + adapter = InsightsManagementAdapter( + { item -> viewModel.onItemButtonClicked(item) }, + { viewHolder -> addedInsightsTouchHelper.startDrag(viewHolder) }, + { list -> viewModel.onAddedInsightsReordered(list) } + ) + addedInsights.adapter = adapter + + val callback = ItemTouchHelperCallback(adapter) + addedInsightsTouchHelper = ItemTouchHelper(callback) + addedInsightsTouchHelper.attachToRecyclerView(addedInsights) + } + adapter.update(insights) + } +} diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/management/InsightsManagementViewHolder.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/management/InsightsManagementViewHolder.kt new file mode 100644 index 000000000000..e0223078ef83 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/management/InsightsManagementViewHolder.kt @@ -0,0 +1,13 @@ +package org.wordpress.android.ui.stats.refresh.lists.sections.insights.management + +import android.support.v7.widget.RecyclerView.ViewHolder +import android.view.LayoutInflater +import android.view.ViewGroup +import org.wordpress.android.R +import org.wordpress.android.ui.stats.refresh.lists.sections.insights.management.InsightsManagementViewModel.InsightModel + +abstract class InsightsManagementViewHolder( + parent: ViewGroup +) : ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.insights_management_list_item, parent, false)) { + abstract fun bind(insight: InsightModel, isLast: Boolean) +} diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/management/InsightsManagementViewModel.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/management/InsightsManagementViewModel.kt new file mode 100644 index 000000000000..6feb1c9d30c2 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/management/InsightsManagementViewModel.kt @@ -0,0 +1,123 @@ +package org.wordpress.android.ui.stats.refresh.lists.sections.insights.management + +import android.arch.lifecycle.LiveData +import android.arch.lifecycle.MutableLiveData +import androidx.annotation.StringRes +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import org.wordpress.android.R +import org.wordpress.android.fluxc.store.StatsStore +import org.wordpress.android.fluxc.store.StatsStore.InsightType +import org.wordpress.android.fluxc.store.StatsStore.InsightType.ALL_TIME_STATS +import org.wordpress.android.fluxc.store.StatsStore.InsightType.ANNUAL_SITE_STATS +import org.wordpress.android.fluxc.store.StatsStore.InsightType.COMMENTS +import org.wordpress.android.fluxc.store.StatsStore.InsightType.FOLLOWERS +import org.wordpress.android.fluxc.store.StatsStore.InsightType.FOLLOWER_TOTALS +import org.wordpress.android.fluxc.store.StatsStore.InsightType.LATEST_POST_SUMMARY +import org.wordpress.android.fluxc.store.StatsStore.InsightType.MOST_POPULAR_DAY_AND_HOUR +import org.wordpress.android.fluxc.store.StatsStore.InsightType.POSTING_ACTIVITY +import org.wordpress.android.fluxc.store.StatsStore.InsightType.PUBLICIZE +import org.wordpress.android.fluxc.store.StatsStore.InsightType.TAGS_AND_CATEGORIES +import org.wordpress.android.fluxc.store.StatsStore.InsightType.TODAY_STATS +import org.wordpress.android.modules.UI_THREAD +import org.wordpress.android.ui.stats.refresh.INSIGHTS_USE_CASE +import org.wordpress.android.ui.stats.refresh.lists.BaseListUseCase +import org.wordpress.android.ui.stats.refresh.lists.sections.insights.management.InsightsManagementViewModel.InsightModel.Status.ADDED +import org.wordpress.android.ui.stats.refresh.lists.sections.insights.management.InsightsManagementViewModel.InsightModel.Status.REMOVED +import org.wordpress.android.ui.stats.refresh.utils.StatsSiteProvider +import org.wordpress.android.viewmodel.ScopedViewModel +import org.wordpress.android.viewmodel.SingleLiveEvent +import javax.inject.Inject +import javax.inject.Named + +class InsightsManagementViewModel @Inject constructor( + @Named(UI_THREAD) mainDispatcher: CoroutineDispatcher, + @Named(INSIGHTS_USE_CASE) val insightsUseCase: BaseListUseCase, + private val siteProvider: StatsSiteProvider, + private val statsStore: StatsStore +) : ScopedViewModel(mainDispatcher) { + private val _removedInsights = MutableLiveData>() + val removedInsights: LiveData> = _removedInsights + + private val _addedInsights = MutableLiveData>() + val addedInsights: LiveData> = _addedInsights + + private val _closeInsightsManagement = SingleLiveEvent() + val closeInsightsManagement: LiveData = _closeInsightsManagement + + private val _isMenuVisible = MutableLiveData() + val isMenuVisible: LiveData = _isMenuVisible + + private lateinit var insights: List + private var isInitialized = false + + fun start() { + if (!isInitialized) { + isInitialized = true + _isMenuVisible.value = false + loadInsights() + } + } + + private fun loadInsights() { + launch { + val addedInsights = statsStore.getAddedInsights(siteProvider.siteModel) + insights = addedInsights.map { InsightModel(it, ADDED) } + + statsStore.getRemovedInsights(addedInsights).map { InsightModel(it, REMOVED) } + displayInsights() + } + } + + private fun displayInsights() { + _addedInsights.value = insights.filter { it.type == ADDED } + _removedInsights.value = insights.filter { it.type == REMOVED } + } + + fun onSaveInsights() { + // This has to be GlobalScope because otherwise the coroutine gets killed with the ViewModel + GlobalScope.launch { + val addedTypes = insights.filter { it.type == ADDED }.map { it.insightType } + statsStore.updateTypes(siteProvider.siteModel, addedTypes) + + insightsUseCase.loadData() + } + _closeInsightsManagement.call() + } + + fun onAddedInsightsReordered(addedInsights: List) { + insights = addedInsights + insights.filter { it.type == REMOVED } + _isMenuVisible.value = true + } + + fun onItemButtonClicked(insight: InsightModel) { + if (insight.type == ADDED) { + insight.type = REMOVED + } else { + insight.type = ADDED + } + displayInsights() + _isMenuVisible.value = true + } + + data class InsightModel(val insightType: InsightType, var type: Status) { + @StringRes val name: Int = when (insightType) { + LATEST_POST_SUMMARY -> R.string.stats_insights_latest_post_summary + MOST_POPULAR_DAY_AND_HOUR -> R.string.stats_insights_popular + ALL_TIME_STATS -> R.string.stats_insights_all_time_stats + TAGS_AND_CATEGORIES -> R.string.stats_insights_tags_and_categories + COMMENTS -> R.string.stats_comments + FOLLOWERS -> R.string.stats_view_followers + TODAY_STATS -> R.string.stats_insights_today + POSTING_ACTIVITY -> R.string.stats_insights_posting_activity + PUBLICIZE -> R.string.stats_view_publicize + ANNUAL_SITE_STATS -> R.string.stats_insights_this_year_site_stats + FOLLOWER_TOTALS -> R.string.stats_view_follower_totals + } + + enum class Status { + ADDED, + REMOVED + } + } +} diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/management/ItemTouchHelperCallback.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/management/ItemTouchHelperCallback.kt new file mode 100644 index 000000000000..51b9d2944926 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/management/ItemTouchHelperCallback.kt @@ -0,0 +1,34 @@ +package org.wordpress.android.ui.stats.refresh.lists.sections.insights.management + +import android.support.v7.widget.RecyclerView +import android.support.v7.widget.RecyclerView.ViewHolder +import android.support.v7.widget.helper.ItemTouchHelper + +class ItemTouchHelperCallback(private val adapter: ItemTouchHelperAdapter) : ItemTouchHelper.Callback() { + override fun isLongPressDragEnabled(): Boolean { + return false + } + + override fun isItemViewSwipeEnabled(): Boolean { + return false + } + + override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: ViewHolder): Int { + val dragFlags = ItemTouchHelper.UP or ItemTouchHelper.DOWN + return makeMovementFlags(dragFlags, 0) + } + + override fun onMove(recyclerView: RecyclerView, viewHolder: ViewHolder, target: ViewHolder): Boolean { + adapter.onItemMoved(viewHolder, target) + return true + } + + override fun onSwiped(viewHolder: ViewHolder, direction: Int) { + } + + override fun clearView(recyclerView: RecyclerView, viewHolder: ViewHolder) { + super.clearView(recyclerView, viewHolder) + + adapter.onDragFinished(viewHolder) + } +} diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/management/RemovedInsightViewHolder.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/management/RemovedInsightViewHolder.kt new file mode 100644 index 000000000000..d231c6ef0504 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/management/RemovedInsightViewHolder.kt @@ -0,0 +1,31 @@ +package org.wordpress.android.ui.stats.refresh.lists.sections.insights.management + +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.ui.stats.refresh.lists.sections.insights.management.InsightsManagementViewModel.InsightModel + +class RemovedInsightViewHolder( + val parent: ViewGroup, + private val onButtonClicked: (InsightModel) -> Unit +) : InsightsManagementViewHolder(parent) { + private val title: TextView = itemView.findViewById(R.id.itemTitle) + private val managementButton: ImageButton = itemView.findViewById(R.id.insightsManagementItemButton) + private val dragAndDropButton: View = itemView.findViewById(R.id.dragAndDropItemButton) + private val divider: View = itemView.findViewById(R.id.divider) + + override fun bind(insight: InsightModel, isLast: Boolean) { + title.setText(insight.name) + + managementButton.setImageResource(R.drawable.ic_add_circle) + managementButton.setOnClickListener { + managementButton.setOnClickListener(null) + onButtonClicked(insight) + } + + dragAndDropButton.visibility = View.GONE + divider.visibility = if (isLast) View.GONE else View.VISIBLE + } +} 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 9a1c8c57fc10..52a588c2c2e8 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,10 +1,11 @@ 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.InsightsAllTimeModel -import org.wordpress.android.fluxc.store.StatsStore.InsightsTypes.ALL_TIME_STATS +import org.wordpress.android.fluxc.store.StatsStore.InsightType.ALL_TIME_STATS import org.wordpress.android.fluxc.store.stats.insights.AllTimeInsightsStore import org.wordpress.android.modules.UI_THREAD import org.wordpress.android.ui.stats.refresh.lists.sections.BaseStatsUseCase.StatelessUseCase @@ -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 @@ -84,4 +91,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/AnnualSiteStatsUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/AnnualSiteStatsUseCase.kt index 80bff5129b8f..cc59286c9cc6 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/AnnualSiteStatsUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/AnnualSiteStatsUseCase.kt @@ -1,9 +1,11 @@ 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.YearsInsightsModel -import org.wordpress.android.fluxc.store.StatsStore.InsightsTypes.ANNUAL_SITE_STATS +import org.wordpress.android.fluxc.store.StatsStore.InsightType.ANNUAL_SITE_STATS import org.wordpress.android.fluxc.store.stats.insights.MostPopularInsightsStore import org.wordpress.android.modules.UI_THREAD import org.wordpress.android.ui.stats.refresh.NavigationTarget @@ -17,6 +19,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.granular.SelectedDateProvider 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.util.LocaleManagerWrapper import java.util.Calendar @@ -33,6 +36,7 @@ class AnnualSiteStatsUseCase( private val selectedDateProvider: SelectedDateProvider, private val annualStatsMapper: AnnualStatsMapper, private val localeManagerWrapper: LocaleManagerWrapper, + private val popupMenuHandler: ItemPopupMenuHandler, private val useCaseMode: UseCaseMode ) : StatelessUseCase(ANNUAL_SITE_STATS, mainDispatcher) { override suspend fun loadCachedData(): YearsInsightsModel? { @@ -67,7 +71,7 @@ class AnnualSiteStatsUseCase( when (useCaseMode) { BLOCK -> { - items.add(Title(R.string.stats_insights_this_year_site_stats)) + items.add(buildTitle()) items.addAll(annualStatsMapper.mapYearInBlock(domainModel.years.last())) if (domainModel.years.size > VISIBLE_ITEMS) { items.add( @@ -102,6 +106,12 @@ class AnnualSiteStatsUseCase( return calendar.time } + private fun buildTitle() = Title(string.stats_insights_this_year_site_stats, menuAction = this::onMenuClick) + + private fun onMenuClick(view: View) { + popupMenuHandler.onMenuClick(view, type) + } + class AnnualSiteStatsUseCaseFactory @Inject constructor( @Named(UI_THREAD) private val mainDispatcher: CoroutineDispatcher, @@ -109,7 +119,8 @@ class AnnualSiteStatsUseCase( private val statsSiteProvider: StatsSiteProvider, private val annualStatsMapper: AnnualStatsMapper, private val localeManagerWrapper: LocaleManagerWrapper, - private val selectedDateProvider: SelectedDateProvider + private val selectedDateProvider: SelectedDateProvider, + private val popupMenuHandler: ItemPopupMenuHandler ) : InsightUseCaseFactory { override fun build(useCaseMode: UseCaseMode) = AnnualSiteStatsUseCase( @@ -119,6 +130,7 @@ class AnnualSiteStatsUseCase( selectedDateProvider, annualStatsMapper, localeManagerWrapper, + popupMenuHandler, useCaseMode ) } 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..3e25b249eef4 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,12 +1,13 @@ 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.analytics.AnalyticsTracker import org.wordpress.android.fluxc.model.stats.CommentsModel import org.wordpress.android.fluxc.model.stats.LimitMode -import org.wordpress.android.fluxc.store.StatsStore.InsightsTypes.COMMENTS +import org.wordpress.android.fluxc.store.StatsStore.InsightType.COMMENTS import org.wordpress.android.fluxc.store.stats.insights.CommentsStore import org.wordpress.android.modules.UI_THREAD import org.wordpress.android.ui.stats.refresh.NavigationTarget.ViewCommentsStats @@ -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/FollowerTotalsUseCase.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/FollowerTotalsUseCase.kt index f5252f8c164a..de4acfaf7906 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/FollowerTotalsUseCase.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/insights/usecases/FollowerTotalsUseCase.kt @@ -6,7 +6,7 @@ import kotlinx.coroutines.async import org.wordpress.android.R import org.wordpress.android.fluxc.model.stats.LimitMode import org.wordpress.android.fluxc.model.stats.PagedMode -import org.wordpress.android.fluxc.store.StatsStore.InsightsTypes.FOLLOWER_TOTALS +import org.wordpress.android.fluxc.store.StatsStore.InsightType.FOLLOWER_TOTALS import org.wordpress.android.fluxc.store.stats.insights.FollowersStore import org.wordpress.android.fluxc.store.stats.insights.PublicizeStore import org.wordpress.android.modules.BG_THREAD 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 0181bfb81e0d..42939b0cf06d 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 @@ -12,7 +13,7 @@ import org.wordpress.android.fluxc.model.stats.FollowersModel import org.wordpress.android.fluxc.model.stats.FollowersModel.FollowerModel import org.wordpress.android.fluxc.model.stats.LimitMode import org.wordpress.android.fluxc.model.stats.PagedMode -import org.wordpress.android.fluxc.store.StatsStore.InsightsTypes.FOLLOWERS +import org.wordpress.android.fluxc.store.StatsStore.InsightType.FOLLOWERS import org.wordpress.android.fluxc.store.stats.insights.FollowersStore import org.wordpress.android.modules.BG_THREAD import org.wordpress.android.modules.UI_THREAD @@ -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 @@ -51,6 +53,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, @@ -118,6 +121,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 @@ -127,7 +134,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()) { @@ -168,6 +175,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)) @@ -214,6 +223,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, @@ -222,6 +235,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) = @@ -233,6 +247,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 ff9d64669c32..5db6e2e19473 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 @@ -8,7 +9,7 @@ import org.wordpress.android.analytics.AnalyticsTracker.Stat.STATS_LATEST_POST_S import org.wordpress.android.analytics.AnalyticsTracker.Stat.STATS_LATEST_POST_SUMMARY_SHARE_POST_TAPPED import org.wordpress.android.analytics.AnalyticsTracker.Stat.STATS_LATEST_POST_SUMMARY_VIEW_POST_DETAILS_TAPPED import org.wordpress.android.fluxc.model.stats.InsightsLatestPostModel -import org.wordpress.android.fluxc.store.StatsStore.InsightsTypes.LATEST_POST_SUMMARY +import org.wordpress.android.fluxc.store.StatsStore.InsightType.LATEST_POST_SUMMARY import org.wordpress.android.fluxc.store.stats.insights.LatestPostInsightsStore import org.wordpress.android.modules.UI_THREAD import org.wordpress.android.ui.stats.refresh.NavigationTarget.AddNewPost @@ -22,6 +23,7 @@ import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.ListI 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.BlockListItem.ValueItem +import org.wordpress.android.ui.stats.refresh.utils.ItemPopupMenuHandler import org.wordpress.android.ui.stats.refresh.utils.MILLION import org.wordpress.android.ui.stats.refresh.utils.StatsSiteProvider import org.wordpress.android.ui.stats.refresh.utils.toFormattedString @@ -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..cf4341de95c0 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,17 +1,21 @@ 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.StatsStore.InsightType.MOST_POPULAR_DAY_AND_HOUR import org.wordpress.android.fluxc.store.stats.insights.MostPopularInsightsStore 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.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..725602c45969 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,15 +1,18 @@ 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 import org.wordpress.android.fluxc.model.stats.insights.PostingActivityModel.Day -import org.wordpress.android.fluxc.store.StatsStore.InsightsTypes.POSTING_ACTIVITY +import org.wordpress.android.fluxc.store.StatsStore.InsightType.POSTING_ACTIVITY 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 6c7296a35b44..8e0aec9de598 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,12 +1,13 @@ 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.analytics.AnalyticsTracker import org.wordpress.android.fluxc.model.stats.LimitMode import org.wordpress.android.fluxc.model.stats.PublicizeModel -import org.wordpress.android.fluxc.store.StatsStore.InsightsTypes.PUBLICIZE +import org.wordpress.android.fluxc.store.StatsStore.InsightType.PUBLICIZE import org.wordpress.android.fluxc.store.stats.insights.PublicizeStore import org.wordpress.android.modules.UI_THREAD import org.wordpress.android.ui.stats.refresh.NavigationTarget.ViewPublicizeStats @@ -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 17e447ceccde..4a74edb4f22a 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 @@ -8,7 +9,7 @@ import org.wordpress.android.analytics.AnalyticsTracker import org.wordpress.android.fluxc.model.stats.LimitMode import org.wordpress.android.fluxc.model.stats.TagsModel import org.wordpress.android.fluxc.model.stats.TagsModel.TagModel -import org.wordpress.android.fluxc.store.StatsStore.InsightsTypes.TAGS_AND_CATEGORIES +import org.wordpress.android.fluxc.store.StatsStore.InsightType.TAGS_AND_CATEGORIES import org.wordpress.android.fluxc.store.stats.insights.TagsStore import org.wordpress.android.modules.UI_THREAD import org.wordpress.android.ui.stats.refresh.NavigationTarget.ViewTag @@ -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..b1711e7f34fe 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,9 +1,11 @@ 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.StatsStore.InsightType.TODAY_STATS import org.wordpress.android.fluxc.store.stats.insights.TodayInsightsStore import org.wordpress.android.modules.UI_THREAD import org.wordpress.android.ui.stats.refresh.lists.sections.BaseStatsUseCase.StatelessUseCase @@ -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/LinkButtonViewHolder.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/viewholders/LinkButtonViewHolder.kt new file mode 100644 index 000000000000..7b99ec6c6ae2 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/sections/viewholders/LinkButtonViewHolder.kt @@ -0,0 +1,19 @@ +package org.wordpress.android.ui.stats.refresh.lists.sections.viewholders + +import android.view.ViewGroup +import android.widget.TextView +import org.wordpress.android.R +import org.wordpress.android.R.id +import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem.LinkButton + +class LinkButtonViewHolder(val parent: ViewGroup) : BlockListItemViewHolder( + parent, + R.layout.stats_block_link_button_item +) { + private val text = itemView.findViewById(id.text) + + fun bind(item: LinkButton) { + text.setText(item.text) + text.setOnClickListener { item.navigateAction.click() } + } +} 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/lists/viewholders/BaseStatsViewHolder.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/viewholders/BaseStatsViewHolder.kt index 37ed84b8acfb..04aad05bb4d9 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/viewholders/BaseStatsViewHolder.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/viewholders/BaseStatsViewHolder.kt @@ -6,9 +6,9 @@ import android.support.v7.widget.RecyclerView.ViewHolder import android.support.v7.widget.StaggeredGridLayoutManager import android.view.LayoutInflater import android.view.ViewGroup -import org.wordpress.android.fluxc.store.StatsStore.InsightsTypes.LATEST_POST_SUMMARY -import org.wordpress.android.fluxc.store.StatsStore.StatsTypes -import org.wordpress.android.fluxc.store.StatsStore.TimeStatsTypes.OVERVIEW +import org.wordpress.android.fluxc.store.StatsStore.InsightType.LATEST_POST_SUMMARY +import org.wordpress.android.fluxc.store.StatsStore.StatsType +import org.wordpress.android.fluxc.store.StatsStore.TimeStatsType.OVERVIEW import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem abstract class BaseStatsViewHolder( @@ -16,8 +16,8 @@ abstract class BaseStatsViewHolder( @LayoutRes layout: Int ) : ViewHolder(LayoutInflater.from(parent.context).inflate(layout, parent, false)) { @CallSuper - open fun bind(statsTypes: StatsTypes, items: List) { - if (statsTypes == OVERVIEW || statsTypes == LATEST_POST_SUMMARY) { + open fun bind(statsType: StatsType?, items: List) { + if (statsType == OVERVIEW || statsType == LATEST_POST_SUMMARY) { val layoutParams = itemView.layoutParams as? StaggeredGridLayoutManager.LayoutParams layoutParams?.isFullSpan = true } diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/viewholders/BlockListViewHolder.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/viewholders/BlockListViewHolder.kt index e657dd780d90..f3fbf508cc4a 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/viewholders/BlockListViewHolder.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/viewholders/BlockListViewHolder.kt @@ -4,18 +4,18 @@ import android.support.v7.widget.LinearLayoutManager import android.support.v7.widget.RecyclerView import android.view.ViewGroup import org.wordpress.android.R -import org.wordpress.android.fluxc.store.StatsStore.StatsTypes +import org.wordpress.android.fluxc.store.StatsStore.StatsType import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListAdapter import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem import org.wordpress.android.util.image.ImageManager -class BlockListViewHolder(parent: ViewGroup, val imageManager: ImageManager) : BaseStatsViewHolder( +open class BlockListViewHolder(parent: ViewGroup, val imageManager: ImageManager) : BaseStatsViewHolder( parent, R.layout.stats_list_block ) { private val list: RecyclerView = itemView.findViewById(R.id.stats_block_list) - override fun bind(statsTypes: StatsTypes, items: List) { - super.bind(statsTypes, items) + override fun bind(statsType: StatsType?, items: List) { + super.bind(statsType, items) list.isNestedScrollingEnabled = false if (list.adapter == null) { list.layoutManager = LinearLayoutManager(list.context, LinearLayoutManager.VERTICAL, false) diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/viewholders/ControlViewHolder.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/viewholders/ControlViewHolder.kt new file mode 100644 index 000000000000..afd9155f6afd --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/viewholders/ControlViewHolder.kt @@ -0,0 +1,15 @@ +package org.wordpress.android.ui.stats.refresh.lists.viewholders + +import android.view.ViewGroup +import org.wordpress.android.ui.stats.refresh.lists.sections.BlockListItem +import org.wordpress.android.util.image.ImageManager + +class ControlViewHolder(val parent: ViewGroup, imageManager: ImageManager) : BlockListViewHolder( + parent, + imageManager +) { + fun bind(items: List) { + super.bind(null, items) + itemView.background = null + } +} diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/viewholders/LoadingViewHolder.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/viewholders/LoadingViewHolder.kt index 4dd36bcaffcd..49a9f72bb01f 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/viewholders/LoadingViewHolder.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/lists/viewholders/LoadingViewHolder.kt @@ -14,8 +14,8 @@ class LoadingViewHolder(parent: ViewGroup, val imageManager: ImageManager) : Bas R.layout.stats_loading_view ) { private val list: RecyclerView = itemView.findViewById(R.id.stats_block_list) - override fun bind(statsTypes: StatsStore.StatsTypes, items: List) { - super.bind(statsTypes, items) + override fun bind(statsType: StatsStore.StatsType?, items: List) { + super.bind(statsType, items) list.isNestedScrollingEnabled = false if (list.adapter == null) { list.layoutManager = LinearLayoutManager(list.context, LinearLayoutManager.VERTICAL, false) 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..06d6d21295a1 --- /dev/null +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/utils/ItemPopupMenuHandler.kt @@ -0,0 +1,81 @@ +package org.wordpress.android.ui.stats.refresh.utils + +import android.arch.lifecycle.LiveData +import android.arch.lifecycle.MutableLiveData +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.InsightType +import org.wordpress.android.fluxc.store.StatsStore.StatsType +import org.wordpress.android.modules.BG_THREAD +import org.wordpress.android.modules.UI_THREAD +import org.wordpress.android.viewmodel.Event +import javax.inject.Inject +import javax.inject.Named +import javax.inject.Singleton +import android.support.v7.widget.ListPopupWindow +import org.wordpress.android.ui.stats.refresh.lists.sections.insights.InsightsMenuAdapter +import org.wordpress.android.ui.stats.refresh.lists.sections.insights.InsightsMenuAdapter.InsightsMenuItem +import org.wordpress.android.ui.stats.refresh.lists.sections.insights.InsightsMenuAdapter.InsightsMenuItem.DOWN +import org.wordpress.android.ui.stats.refresh.lists.sections.insights.InsightsMenuAdapter.InsightsMenuItem.REMOVE +import org.wordpress.android.ui.stats.refresh.lists.sections.insights.InsightsMenuAdapter.InsightsMenuItem.UP + +@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: StatsType) { + GlobalScope.launch(bgDispatcher) { + val type = statsType as InsightType + val insights = statsStore.getAddedInsights(statsSiteProvider.siteModel) + + val indexOfBlock = insights.indexOfFirst { it == type } + val showUpAction = indexOfBlock > 0 + val showDownAction = indexOfBlock < insights.size - 1 + + withContext(mainDispatcher) { + val popup = ListPopupWindow(view.context) + val adapter = InsightsMenuAdapter(view.context, showUpAction, showDownAction) + popup.setAdapter(adapter) + popup.width = view.context.resources.getDimensionPixelSize(R.dimen.stats_insights_menu_item_width) + popup.anchorView = view + popup.isModal = true + popup.setOnItemClickListener { _, _, _, id -> + when (InsightsMenuItem.values()[id.toInt()]) { + UP -> { + GlobalScope.launch(bgDispatcher) { + statsStore.moveTypeUp(statsSiteProvider.siteModel, type) + mutableTypeMoved.postValue(Event(type)) + } + } + DOWN -> { + GlobalScope.launch(bgDispatcher) { + statsStore.moveTypeDown(statsSiteProvider.siteModel, type) + mutableTypeMoved.postValue(Event(type)) + } + } + REMOVE -> { + GlobalScope.launch(bgDispatcher) { + statsStore.removeType(statsSiteProvider.siteModel, type) + mutableTypeMoved.postValue(Event(type)) + } + } + } + popup.dismiss() + } + popup.show() + } + } + } +} diff --git a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/utils/StatsNavigator.kt b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/utils/StatsNavigator.kt index 240181534bfb..1e8d80b4f23f 100644 --- a/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/utils/StatsNavigator.kt +++ b/WordPress/src/main/java/org/wordpress/android/ui/stats/refresh/utils/StatsNavigator.kt @@ -30,6 +30,7 @@ import org.wordpress.android.ui.stats.refresh.NavigationTarget.ViewClicks import org.wordpress.android.ui.stats.refresh.NavigationTarget.ViewCommentsStats import org.wordpress.android.ui.stats.refresh.NavigationTarget.ViewCountries import org.wordpress.android.ui.stats.refresh.NavigationTarget.ViewFollowersStats +import org.wordpress.android.ui.stats.refresh.NavigationTarget.ViewInsightsManagement import org.wordpress.android.ui.stats.refresh.NavigationTarget.ViewMonthsAndYearsStats import org.wordpress.android.ui.stats.refresh.NavigationTarget.ViewPost import org.wordpress.android.ui.stats.refresh.NavigationTarget.ViewPostDetailStats @@ -130,6 +131,9 @@ class StatsNavigator is ViewUrl -> { WPWebViewActivity.openURL(activity, target.url) } + is ViewInsightsManagement -> { + ActivityLauncher.viewInsightsManagement(activity) + } } } } 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 061e7f74ea4f..76156edf7ee1 100644 --- a/WordPress/src/main/java/org/wordpress/android/util/LiveDataUtils.kt +++ b/WordPress/src/main/java/org/wordpress/android/util/LiveDataUtils.kt @@ -203,8 +203,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_add_circle.xml b/WordPress/src/main/res/drawable/ic_add_circle.xml new file mode 100644 index 000000000000..b6826feeb3e3 --- /dev/null +++ b/WordPress/src/main/res/drawable/ic_add_circle.xml @@ -0,0 +1,5 @@ + + + + + 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_material_drag_handle_24dp.xml b/WordPress/src/main/res/drawable/ic_material_drag_handle_24dp.xml new file mode 100644 index 000000000000..6d333d8042a9 --- /dev/null +++ b/WordPress/src/main/res/drawable/ic_material_drag_handle_24dp.xml @@ -0,0 +1,11 @@ + + + + + diff --git a/WordPress/src/main/res/drawable/ic_minus_small_24dp.xml b/WordPress/src/main/res/drawable/ic_minus_small_24dp.xml new file mode 100644 index 000000000000..0da31deb6983 --- /dev/null +++ b/WordPress/src/main/res/drawable/ic_minus_small_24dp.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/WordPress/src/main/res/drawable/ic_plus_small_24dp.xml b/WordPress/src/main/res/drawable/ic_plus_small_24dp.xml new file mode 100644 index 000000000000..9c2634168399 --- /dev/null +++ b/WordPress/src/main/res/drawable/ic_plus_small_24dp.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/WordPress/src/main/res/drawable/ic_remove_circle.xml b/WordPress/src/main/res/drawable/ic_remove_circle.xml new file mode 100644 index 000000000000..581a0cb7cac2 --- /dev/null +++ b/WordPress/src/main/res/drawable/ic_remove_circle.xml @@ -0,0 +1,5 @@ + + + + + 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-w528dp/insights_management_fragment.xml b/WordPress/src/main/res/layout-w528dp/insights_management_fragment.xml new file mode 100644 index 000000000000..fe773fc50a10 --- /dev/null +++ b/WordPress/src/main/res/layout-w528dp/insights_management_fragment.xml @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/WordPress/src/main/res/layout/insights_management_activity.xml b/WordPress/src/main/res/layout/insights_management_activity.xml new file mode 100644 index 000000000000..e44533e02dca --- /dev/null +++ b/WordPress/src/main/res/layout/insights_management_activity.xml @@ -0,0 +1,15 @@ + + + + + + diff --git a/WordPress/src/main/res/layout/insights_management_fragment.xml b/WordPress/src/main/res/layout/insights_management_fragment.xml new file mode 100644 index 000000000000..e75aa05bf766 --- /dev/null +++ b/WordPress/src/main/res/layout/insights_management_fragment.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + diff --git a/WordPress/src/main/res/layout/insights_management_list_item.xml b/WordPress/src/main/res/layout/insights_management_list_item.xml new file mode 100644 index 000000000000..9b7da91766e9 --- /dev/null +++ b/WordPress/src/main/res/layout/insights_management_list_item.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + diff --git a/WordPress/src/main/res/layout/stats_block_four_columns_item.xml b/WordPress/src/main/res/layout/stats_block_four_columns_item.xml index 10fa560668f3..804e1cae3fbb 100644 --- a/WordPress/src/main/res/layout/stats_block_four_columns_item.xml +++ b/WordPress/src/main/res/layout/stats_block_four_columns_item.xml @@ -4,7 +4,8 @@ android:id="@+id/column_container" android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="horizontal"> + android:orientation="horizontal" + android:baselineAligned="false"> + + +