For #12377 - Choice to hide no collections placeholder from homescreen
parent
a1a211d6e4
commit
655d2b8a88
|
@ -146,8 +146,11 @@ class HomeFragment : Fragment() {
|
||||||
private val store: BrowserStore
|
private val store: BrowserStore
|
||||||
get() = requireComponents.core.store
|
get() = requireComponents.core.store
|
||||||
|
|
||||||
private val onboarding by lazy { StrictMode.allowThreadDiskReads().resetPoliciesAfter {
|
private val onboarding by lazy {
|
||||||
FenixOnboarding(requireContext()) } }
|
StrictMode.allowThreadDiskReads().resetPoliciesAfter {
|
||||||
|
FenixOnboarding(requireContext())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private lateinit var homeFragmentStore: HomeFragmentStore
|
private lateinit var homeFragmentStore: HomeFragmentStore
|
||||||
private var _sessionControlInteractor: SessionControlInteractor? = null
|
private var _sessionControlInteractor: SessionControlInteractor? = null
|
||||||
|
@ -193,7 +196,8 @@ class HomeFragment : Fragment() {
|
||||||
topSites = StrictMode.allowThreadDiskReads().resetPoliciesAfter {
|
topSites = StrictMode.allowThreadDiskReads().resetPoliciesAfter {
|
||||||
components.core.topSiteStorage.cachedTopSites
|
components.core.topSiteStorage.cachedTopSites
|
||||||
},
|
},
|
||||||
tip = FenixTipManager(listOf(MigrationTipProvider(requireContext()))).getTip()
|
tip = FenixTipManager(listOf(MigrationTipProvider(requireContext()))).getTip(),
|
||||||
|
showCollectionPlaceholder = components.settings.showCollectionsPlaceholderOnHome
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -201,6 +205,7 @@ class HomeFragment : Fragment() {
|
||||||
_sessionControlInteractor = SessionControlInteractor(
|
_sessionControlInteractor = SessionControlInteractor(
|
||||||
DefaultSessionControlController(
|
DefaultSessionControlController(
|
||||||
activity = activity,
|
activity = activity,
|
||||||
|
settings = components.settings,
|
||||||
engine = components.core.engine,
|
engine = components.core.engine,
|
||||||
metrics = components.analytics.metrics,
|
metrics = components.analytics.metrics,
|
||||||
sessionManager = sessionManager,
|
sessionManager = sessionManager,
|
||||||
|
@ -220,9 +225,9 @@ class HomeFragment : Fragment() {
|
||||||
updateLayout(view)
|
updateLayout(view)
|
||||||
sessionControlView = SessionControlView(
|
sessionControlView = SessionControlView(
|
||||||
view.sessionControlRecyclerView,
|
view.sessionControlRecyclerView,
|
||||||
|
viewLifecycleOwner,
|
||||||
sessionControlInteractor,
|
sessionControlInteractor,
|
||||||
homeViewModel,
|
homeViewModel
|
||||||
requireComponents.core.store.state.normalTabs.isNotEmpty()
|
|
||||||
)
|
)
|
||||||
|
|
||||||
updateSessionControlView(view)
|
updateSessionControlView(view)
|
||||||
|
@ -516,7 +521,8 @@ class HomeFragment : Fragment() {
|
||||||
collections = components.core.tabCollectionStorage.cachedTabCollections,
|
collections = components.core.tabCollectionStorage.cachedTabCollections,
|
||||||
mode = currentMode.getCurrentMode(),
|
mode = currentMode.getCurrentMode(),
|
||||||
topSites = components.core.topSiteStorage.cachedTopSites,
|
topSites = components.core.topSiteStorage.cachedTopSites,
|
||||||
tip = FenixTipManager(listOf(MigrationTipProvider(requireContext()))).getTip()
|
tip = FenixTipManager(listOf(MigrationTipProvider(requireContext()))).getTip(),
|
||||||
|
showCollectionPlaceholder = components.settings.showCollectionsPlaceholderOnHome
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -41,13 +41,16 @@ data class Tab(
|
||||||
* @property mode The state of the [HomeFragment] UI.
|
* @property mode The state of the [HomeFragment] UI.
|
||||||
* @property tabs The list of opened [Tab] in the [HomeFragment].
|
* @property tabs The list of opened [Tab] in the [HomeFragment].
|
||||||
* @property topSites The list of [TopSite] in the [HomeFragment].
|
* @property topSites The list of [TopSite] in the [HomeFragment].
|
||||||
|
* @property tip The current [Tip] to show on the [HomeFragment].
|
||||||
|
* @property showCollectionPlaceholder If true, shows a placeholder when there are no collections.
|
||||||
*/
|
*/
|
||||||
data class HomeFragmentState(
|
data class HomeFragmentState(
|
||||||
val collections: List<TabCollection>,
|
val collections: List<TabCollection>,
|
||||||
val expandedCollections: Set<Long>,
|
val expandedCollections: Set<Long>,
|
||||||
val mode: Mode,
|
val mode: Mode,
|
||||||
val topSites: List<TopSite>,
|
val topSites: List<TopSite>,
|
||||||
val tip: Tip? = null
|
val tip: Tip? = null,
|
||||||
|
val showCollectionPlaceholder: Boolean
|
||||||
) : State
|
) : State
|
||||||
|
|
||||||
sealed class HomeFragmentAction : Action {
|
sealed class HomeFragmentAction : Action {
|
||||||
|
@ -55,7 +58,8 @@ sealed class HomeFragmentAction : Action {
|
||||||
val topSites: List<TopSite>,
|
val topSites: List<TopSite>,
|
||||||
val mode: Mode,
|
val mode: Mode,
|
||||||
val collections: List<TabCollection>,
|
val collections: List<TabCollection>,
|
||||||
val tip: Tip? = null
|
val tip: Tip? = null,
|
||||||
|
val showCollectionPlaceholder: Boolean
|
||||||
) :
|
) :
|
||||||
HomeFragmentAction()
|
HomeFragmentAction()
|
||||||
|
|
||||||
|
@ -66,6 +70,7 @@ sealed class HomeFragmentAction : Action {
|
||||||
data class ModeChange(val mode: Mode) : HomeFragmentAction()
|
data class ModeChange(val mode: Mode) : HomeFragmentAction()
|
||||||
data class TopSitesChange(val topSites: List<TopSite>) : HomeFragmentAction()
|
data class TopSitesChange(val topSites: List<TopSite>) : HomeFragmentAction()
|
||||||
data class RemoveTip(val tip: Tip) : HomeFragmentAction()
|
data class RemoveTip(val tip: Tip) : HomeFragmentAction()
|
||||||
|
object RemoveCollectionsPlaceholder : HomeFragmentAction()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun homeFragmentStateReducer(
|
private fun homeFragmentStateReducer(
|
||||||
|
@ -93,6 +98,11 @@ private fun homeFragmentStateReducer(
|
||||||
is HomeFragmentAction.CollectionsChange -> state.copy(collections = action.collections)
|
is HomeFragmentAction.CollectionsChange -> state.copy(collections = action.collections)
|
||||||
is HomeFragmentAction.ModeChange -> state.copy(mode = action.mode)
|
is HomeFragmentAction.ModeChange -> state.copy(mode = action.mode)
|
||||||
is HomeFragmentAction.TopSitesChange -> state.copy(topSites = action.topSites)
|
is HomeFragmentAction.TopSitesChange -> state.copy(topSites = action.topSites)
|
||||||
is HomeFragmentAction.RemoveTip -> { state.copy(tip = null) }
|
is HomeFragmentAction.RemoveTip -> {
|
||||||
|
state.copy(tip = null)
|
||||||
|
}
|
||||||
|
is HomeFragmentAction.RemoveCollectionsPlaceholder -> {
|
||||||
|
state.copy(showCollectionPlaceholder = false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,11 +8,13 @@ import android.content.Context
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.annotation.LayoutRes
|
import androidx.annotation.LayoutRes
|
||||||
|
import androidx.lifecycle.LifecycleOwner
|
||||||
import androidx.recyclerview.widget.DiffUtil
|
import androidx.recyclerview.widget.DiffUtil
|
||||||
import androidx.recyclerview.widget.ListAdapter
|
import androidx.recyclerview.widget.ListAdapter
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import mozilla.components.feature.tab.collections.TabCollection
|
import mozilla.components.feature.tab.collections.TabCollection
|
||||||
import mozilla.components.feature.top.sites.TopSite
|
import mozilla.components.feature.top.sites.TopSite
|
||||||
|
import org.mozilla.fenix.components.Components
|
||||||
import org.mozilla.fenix.components.tips.Tip
|
import org.mozilla.fenix.components.tips.Tip
|
||||||
import org.mozilla.fenix.home.OnboardingState
|
import org.mozilla.fenix.home.OnboardingState
|
||||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.CollectionHeaderViewHolder
|
import org.mozilla.fenix.home.sessioncontrol.viewholders.CollectionHeaderViewHolder
|
||||||
|
@ -135,7 +137,8 @@ class AdapterItemDiffCallback : DiffUtil.ItemCallback<AdapterItem>() {
|
||||||
|
|
||||||
class SessionControlAdapter(
|
class SessionControlAdapter(
|
||||||
private val interactor: SessionControlInteractor,
|
private val interactor: SessionControlInteractor,
|
||||||
private val hasNormalTabsOpened: Boolean
|
private val viewLifecycleOwner: LifecycleOwner,
|
||||||
|
private val components: Components
|
||||||
) : ListAdapter<AdapterItem, RecyclerView.ViewHolder>(AdapterItemDiffCallback()) {
|
) : ListAdapter<AdapterItem, RecyclerView.ViewHolder>(AdapterItemDiffCallback()) {
|
||||||
|
|
||||||
// This method triggers the ComplexMethod lint error when in fact it's quite simple.
|
// This method triggers the ComplexMethod lint error when in fact it's quite simple.
|
||||||
|
@ -150,7 +153,12 @@ class SessionControlAdapter(
|
||||||
interactor
|
interactor
|
||||||
)
|
)
|
||||||
NoCollectionsMessageViewHolder.LAYOUT_ID ->
|
NoCollectionsMessageViewHolder.LAYOUT_ID ->
|
||||||
NoCollectionsMessageViewHolder(view, interactor, hasNormalTabsOpened)
|
NoCollectionsMessageViewHolder(
|
||||||
|
view,
|
||||||
|
viewLifecycleOwner,
|
||||||
|
components.core.store,
|
||||||
|
interactor
|
||||||
|
)
|
||||||
CollectionHeaderViewHolder.LAYOUT_ID -> CollectionHeaderViewHolder(view)
|
CollectionHeaderViewHolder.LAYOUT_ID -> CollectionHeaderViewHolder(view)
|
||||||
CollectionViewHolder.LAYOUT_ID -> CollectionViewHolder(view, interactor)
|
CollectionViewHolder.LAYOUT_ID -> CollectionViewHolder(view, interactor)
|
||||||
TabInCollectionViewHolder.LAYOUT_ID -> TabInCollectionViewHolder(
|
TabInCollectionViewHolder.LAYOUT_ID -> TabInCollectionViewHolder(
|
||||||
|
|
|
@ -37,6 +37,7 @@ import org.mozilla.fenix.home.HomeFragmentAction
|
||||||
import org.mozilla.fenix.home.HomeFragmentDirections
|
import org.mozilla.fenix.home.HomeFragmentDirections
|
||||||
import org.mozilla.fenix.home.HomeFragmentStore
|
import org.mozilla.fenix.home.HomeFragmentStore
|
||||||
import org.mozilla.fenix.settings.SupportUtils
|
import org.mozilla.fenix.settings.SupportUtils
|
||||||
|
import org.mozilla.fenix.utils.Settings
|
||||||
import mozilla.components.feature.tab.collections.Tab as ComponentTab
|
import mozilla.components.feature.tab.collections.Tab as ComponentTab
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -144,11 +145,17 @@ interface SessionControlController {
|
||||||
* @see [CollectionInteractor.onAddTabsToCollectionTapped]
|
* @see [CollectionInteractor.onAddTabsToCollectionTapped]
|
||||||
*/
|
*/
|
||||||
fun handleCreateCollection()
|
fun handleCreateCollection()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see [CollectionInteractor.onRemoveCollectionsPlaceholder]
|
||||||
|
*/
|
||||||
|
fun handleRemoveCollectionsPlaceholder()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("TooManyFunctions", "LargeClass")
|
@Suppress("TooManyFunctions", "LargeClass")
|
||||||
class DefaultSessionControlController(
|
class DefaultSessionControlController(
|
||||||
private val activity: HomeActivity,
|
private val activity: HomeActivity,
|
||||||
|
private val settings: Settings,
|
||||||
private val engine: Engine,
|
private val engine: Engine,
|
||||||
private val metrics: MetricController,
|
private val metrics: MetricController,
|
||||||
private val sessionManager: SessionManager,
|
private val sessionManager: SessionManager,
|
||||||
|
@ -213,7 +220,11 @@ class DefaultSessionControlController(
|
||||||
metrics.track(Event.CollectionAllTabsRestored)
|
metrics.track(Event.CollectionAllTabsRestored)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun handleCollectionRemoveTab(collection: TabCollection, tab: ComponentTab, wasSwiped: Boolean) {
|
override fun handleCollectionRemoveTab(
|
||||||
|
collection: TabCollection,
|
||||||
|
tab: ComponentTab,
|
||||||
|
wasSwiped: Boolean
|
||||||
|
) {
|
||||||
metrics.track(Event.CollectionTabRemoved)
|
metrics.track(Event.CollectionTabRemoved)
|
||||||
|
|
||||||
if (collection.tabs.size == 1) {
|
if (collection.tabs.size == 1) {
|
||||||
|
@ -223,7 +234,13 @@ class DefaultSessionControlController(
|
||||||
)
|
)
|
||||||
val message =
|
val message =
|
||||||
activity.resources.getString(R.string.delete_tab_and_collection_dialog_message)
|
activity.resources.getString(R.string.delete_tab_and_collection_dialog_message)
|
||||||
showDeleteCollectionPrompt(collection, title, message, wasSwiped, handleSwipedItemDeletionCancel)
|
showDeleteCollectionPrompt(
|
||||||
|
collection,
|
||||||
|
title,
|
||||||
|
message,
|
||||||
|
wasSwiped,
|
||||||
|
handleSwipedItemDeletionCancel
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
viewLifecycleScope.launch(Dispatchers.IO) {
|
viewLifecycleScope.launch(Dispatchers.IO) {
|
||||||
tabCollectionStorage.removeTabFromCollection(collection, tab)
|
tabCollectionStorage.removeTabFromCollection(collection, tab)
|
||||||
|
@ -369,6 +386,11 @@ class DefaultSessionControlController(
|
||||||
showTabTrayCollectionCreation()
|
showTabTrayCollectionCreation()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun handleRemoveCollectionsPlaceholder() {
|
||||||
|
settings.showCollectionsPlaceholderOnHome = false
|
||||||
|
fragmentStore.dispatch(HomeFragmentAction.RemoveCollectionsPlaceholder)
|
||||||
|
}
|
||||||
|
|
||||||
private fun showShareFragment(shareSubject: String, data: List<ShareData>) {
|
private fun showShareFragment(shareSubject: String, data: List<ShareData>) {
|
||||||
val directions = HomeFragmentDirections.actionGlobalShareFragment(
|
val directions = HomeFragmentDirections.actionGlobalShareFragment(
|
||||||
shareSubject = shareSubject,
|
shareSubject = shareSubject,
|
||||||
|
|
|
@ -93,6 +93,11 @@ interface CollectionInteractor {
|
||||||
* Opens the collection creator
|
* Opens the collection creator
|
||||||
*/
|
*/
|
||||||
fun onAddTabsToCollectionTapped()
|
fun onAddTabsToCollectionTapped()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User has removed the collections placeholder from home.
|
||||||
|
*/
|
||||||
|
fun onRemoveCollectionsPlaceholder()
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ToolbarInteractor {
|
interface ToolbarInteractor {
|
||||||
|
@ -256,4 +261,8 @@ class SessionControlInteractor(
|
||||||
override fun onPaste(clipboardText: String) {
|
override fun onPaste(clipboardText: String) {
|
||||||
controller.handlePaste(clipboardText)
|
controller.handlePaste(clipboardText)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onRemoveCollectionsPlaceholder() {
|
||||||
|
controller.handleRemoveCollectionsPlaceholder()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
package org.mozilla.fenix.home.sessioncontrol
|
package org.mozilla.fenix.home.sessioncontrol
|
||||||
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
import androidx.lifecycle.LifecycleOwner
|
||||||
import androidx.recyclerview.widget.ItemTouchHelper
|
import androidx.recyclerview.widget.ItemTouchHelper
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
@ -13,6 +14,7 @@ import mozilla.components.feature.tab.collections.TabCollection
|
||||||
import mozilla.components.feature.top.sites.TopSite
|
import mozilla.components.feature.top.sites.TopSite
|
||||||
import org.mozilla.fenix.R
|
import org.mozilla.fenix.R
|
||||||
import org.mozilla.fenix.components.tips.Tip
|
import org.mozilla.fenix.components.tips.Tip
|
||||||
|
import org.mozilla.fenix.ext.components
|
||||||
import org.mozilla.fenix.home.HomeFragmentState
|
import org.mozilla.fenix.home.HomeFragmentState
|
||||||
import org.mozilla.fenix.home.HomeScreenViewModel
|
import org.mozilla.fenix.home.HomeScreenViewModel
|
||||||
import org.mozilla.fenix.home.Mode
|
import org.mozilla.fenix.home.Mode
|
||||||
|
@ -25,7 +27,8 @@ private fun normalModeAdapterItems(
|
||||||
topSites: List<TopSite>,
|
topSites: List<TopSite>,
|
||||||
collections: List<TabCollection>,
|
collections: List<TabCollection>,
|
||||||
expandedCollections: Set<Long>,
|
expandedCollections: Set<Long>,
|
||||||
tip: Tip?
|
tip: Tip?,
|
||||||
|
showCollectionsPlaceholder: Boolean
|
||||||
): List<AdapterItem> {
|
): List<AdapterItem> {
|
||||||
val items = mutableListOf<AdapterItem>()
|
val items = mutableListOf<AdapterItem>()
|
||||||
|
|
||||||
|
@ -36,8 +39,9 @@ private fun normalModeAdapterItems(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (collections.isEmpty()) {
|
if (collections.isEmpty()) {
|
||||||
items.add(AdapterItem.CollectionHeader)
|
if (showCollectionsPlaceholder) {
|
||||||
items.add(AdapterItem.NoCollectionsMessage)
|
items.add(AdapterItem.NoCollectionsMessage)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
showCollections(collections, expandedCollections, items)
|
showCollections(collections, expandedCollections, items)
|
||||||
}
|
}
|
||||||
|
@ -68,62 +72,77 @@ private fun onboardingAdapterItems(onboardingState: OnboardingState): List<Adapt
|
||||||
val items: MutableList<AdapterItem> = mutableListOf(AdapterItem.OnboardingHeader)
|
val items: MutableList<AdapterItem> = mutableListOf(AdapterItem.OnboardingHeader)
|
||||||
|
|
||||||
// Customize FxA items based on where we are with the account state:
|
// Customize FxA items based on where we are with the account state:
|
||||||
items.addAll(when (onboardingState) {
|
items.addAll(
|
||||||
OnboardingState.SignedOutNoAutoSignIn -> {
|
when (onboardingState) {
|
||||||
listOf(
|
OnboardingState.SignedOutNoAutoSignIn -> {
|
||||||
AdapterItem.OnboardingManualSignIn
|
listOf(
|
||||||
)
|
AdapterItem.OnboardingManualSignIn
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is OnboardingState.SignedOutCanAutoSignIn -> {
|
||||||
|
listOf(
|
||||||
|
AdapterItem.OnboardingAutomaticSignIn(onboardingState)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
OnboardingState.SignedIn -> listOf()
|
||||||
}
|
}
|
||||||
is OnboardingState.SignedOutCanAutoSignIn -> {
|
)
|
||||||
listOf(
|
|
||||||
AdapterItem.OnboardingAutomaticSignIn(onboardingState)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
OnboardingState.SignedIn -> listOf()
|
|
||||||
})
|
|
||||||
|
|
||||||
items.addAll(listOf(
|
items.addAll(
|
||||||
AdapterItem.OnboardingSectionHeader {
|
listOf(
|
||||||
val appName = it.getString(R.string.app_name)
|
AdapterItem.OnboardingSectionHeader {
|
||||||
it.getString(R.string.onboarding_feature_section_header, appName)
|
val appName = it.getString(R.string.app_name)
|
||||||
},
|
it.getString(R.string.onboarding_feature_section_header, appName)
|
||||||
AdapterItem.OnboardingWhatsNew,
|
},
|
||||||
AdapterItem.OnboardingTrackingProtection,
|
AdapterItem.OnboardingWhatsNew,
|
||||||
AdapterItem.OnboardingThemePicker,
|
AdapterItem.OnboardingTrackingProtection,
|
||||||
AdapterItem.OnboardingPrivateBrowsing,
|
AdapterItem.OnboardingThemePicker,
|
||||||
AdapterItem.OnboardingToolbarPositionPicker,
|
AdapterItem.OnboardingPrivateBrowsing,
|
||||||
AdapterItem.OnboardingPrivacyNotice,
|
AdapterItem.OnboardingToolbarPositionPicker,
|
||||||
AdapterItem.OnboardingFinish
|
AdapterItem.OnboardingPrivacyNotice,
|
||||||
))
|
AdapterItem.OnboardingFinish
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
return items
|
return items
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun HomeFragmentState.toAdapterList(): List<AdapterItem> = when (mode) {
|
private fun HomeFragmentState.toAdapterList(): List<AdapterItem> = when (mode) {
|
||||||
is Mode.Normal -> normalModeAdapterItems(topSites, collections, expandedCollections, tip)
|
is Mode.Normal -> normalModeAdapterItems(
|
||||||
|
topSites,
|
||||||
|
collections,
|
||||||
|
expandedCollections,
|
||||||
|
tip,
|
||||||
|
showCollectionPlaceholder
|
||||||
|
)
|
||||||
is Mode.Private -> privateModeAdapterItems()
|
is Mode.Private -> privateModeAdapterItems()
|
||||||
is Mode.Onboarding -> onboardingAdapterItems(mode.state)
|
is Mode.Onboarding -> onboardingAdapterItems(mode.state)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun collectionTabItems(collection: TabCollection) = collection.tabs.mapIndexed { index, tab ->
|
private fun collectionTabItems(collection: TabCollection) =
|
||||||
|
collection.tabs.mapIndexed { index, tab ->
|
||||||
AdapterItem.TabInCollectionItem(collection, tab, index == collection.tabs.lastIndex)
|
AdapterItem.TabInCollectionItem(collection, tab, index == collection.tabs.lastIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
class SessionControlView(
|
class SessionControlView(
|
||||||
override val containerView: View?,
|
override val containerView: View,
|
||||||
|
viewLifecycleOwner: LifecycleOwner,
|
||||||
interactor: SessionControlInteractor,
|
interactor: SessionControlInteractor,
|
||||||
private var homeScreenViewModel: HomeScreenViewModel,
|
private var homeScreenViewModel: HomeScreenViewModel
|
||||||
private val hasNormalTabsOpened: Boolean
|
|
||||||
) : LayoutContainer {
|
) : LayoutContainer {
|
||||||
|
|
||||||
val view: RecyclerView = containerView as RecyclerView
|
val view: RecyclerView = containerView as RecyclerView
|
||||||
|
|
||||||
private val sessionControlAdapter = SessionControlAdapter(interactor, hasNormalTabsOpened)
|
private val sessionControlAdapter = SessionControlAdapter(
|
||||||
|
interactor,
|
||||||
|
viewLifecycleOwner,
|
||||||
|
containerView.context.components
|
||||||
|
)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
view.apply {
|
view.apply {
|
||||||
adapter = sessionControlAdapter
|
adapter = sessionControlAdapter
|
||||||
layoutManager = LinearLayoutManager(containerView!!.context)
|
layoutManager = LinearLayoutManager(containerView.context)
|
||||||
val itemTouchHelper =
|
val itemTouchHelper =
|
||||||
ItemTouchHelper(
|
ItemTouchHelper(
|
||||||
SwipeToDeleteCallback(
|
SwipeToDeleteCallback(
|
||||||
|
|
|
@ -6,22 +6,50 @@ package org.mozilla.fenix.home.sessioncontrol.viewholders
|
||||||
|
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.lifecycle.LifecycleOwner
|
||||||
import kotlinx.android.synthetic.main.no_collections_message.*
|
import kotlinx.android.synthetic.main.no_collections_message.*
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.flow.collect
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import mozilla.components.browser.state.selector.normalTabs
|
||||||
|
import mozilla.components.browser.state.store.BrowserStore
|
||||||
|
import mozilla.components.lib.state.ext.flowScoped
|
||||||
|
import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged
|
||||||
import org.mozilla.fenix.R
|
import org.mozilla.fenix.R
|
||||||
import org.mozilla.fenix.utils.view.ViewHolder
|
import org.mozilla.fenix.ext.increaseTapArea
|
||||||
import org.mozilla.fenix.home.sessioncontrol.CollectionInteractor
|
import org.mozilla.fenix.home.sessioncontrol.CollectionInteractor
|
||||||
|
import org.mozilla.fenix.utils.view.ViewHolder
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
open class NoCollectionsMessageViewHolder(
|
open class NoCollectionsMessageViewHolder(
|
||||||
view: View,
|
view: View,
|
||||||
interactor: CollectionInteractor,
|
viewLifecycleOwner: LifecycleOwner,
|
||||||
hasNormalTabsOpened: Boolean
|
store: BrowserStore,
|
||||||
|
interactor: CollectionInteractor
|
||||||
) : ViewHolder(view) {
|
) : ViewHolder(view) {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
add_tabs_to_collections_button.setOnClickListener {
|
add_tabs_to_collections_button.setOnClickListener {
|
||||||
interactor.onAddTabsToCollectionTapped()
|
interactor.onAddTabsToCollectionTapped()
|
||||||
}
|
}
|
||||||
add_tabs_to_collections_button.isVisible = hasNormalTabsOpened
|
|
||||||
|
remove_collection_placeholder.increaseTapArea(
|
||||||
|
view.resources.getDimensionPixelSize(R.dimen.tap_increase_16)
|
||||||
|
)
|
||||||
|
|
||||||
|
remove_collection_placeholder.setOnClickListener {
|
||||||
|
interactor.onRemoveCollectionsPlaceholder()
|
||||||
|
}
|
||||||
|
|
||||||
|
add_tabs_to_collections_button.isVisible = store.state.normalTabs.isNotEmpty()
|
||||||
|
|
||||||
|
store.flowScoped(viewLifecycleOwner) { flow ->
|
||||||
|
flow.map { state -> state.normalTabs.size }
|
||||||
|
.ifChanged()
|
||||||
|
.collect { tabs ->
|
||||||
|
add_tabs_to_collections_button.isVisible = tabs > 0
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -249,6 +249,11 @@ class Settings(private val appContext: Context) : PreferencesHolder {
|
||||||
default = false
|
default = false
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var showCollectionsPlaceholderOnHome by booleanPreference(
|
||||||
|
appContext.getPreferenceKey(R.string.pref_key_show_collections_placeholder_home),
|
||||||
|
default = true
|
||||||
|
)
|
||||||
|
|
||||||
val isCrashReportingEnabled: Boolean
|
val isCrashReportingEnabled: Boolean
|
||||||
get() = isCrashReportEnabledInBuild &&
|
get() = isCrashReportEnabledInBuild &&
|
||||||
preferences.getBoolean(
|
preferences.getBoolean(
|
||||||
|
|
|
@ -2,42 +2,63 @@
|
||||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||||
<LinearLayout
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:id="@+id/no_collections_wrapper"
|
android:id="@+id/no_collections_wrapper"
|
||||||
android:background="@drawable/empty_session_control_background"
|
|
||||||
android:layout_marginBottom="12dp"
|
|
||||||
android:padding="16dp"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginBottom="12dp"
|
||||||
|
android:background="@drawable/empty_session_control_background"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="16dp">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/no_collections_header"
|
android:id="@+id/no_collections_header"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/no_collections_header1"
|
android:text="@string/collections_header"
|
||||||
tools:drawableEnd="@drawable/ic_tab_collection"
|
|
||||||
android:textAppearance="@style/HeaderTextStyle"
|
android:textAppearance="@style/HeaderTextStyle"
|
||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
app:fontFamily="@font/metropolis_semibold" />
|
app:fontFamily="@font/metropolis_semibold"
|
||||||
|
app:layout_constraintEnd_toStartOf="@id/remove_collection_placeholder"
|
||||||
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.AppCompatImageButton
|
||||||
|
android:id="@+id/remove_collection_placeholder"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="?android:attr/selectableItemBackgroundBorderless"
|
||||||
|
android:contentDescription="@string/remove_home_collection_placeholder_content_description"
|
||||||
|
app:layout_constraintBottom_toBottomOf="@id/no_collections_header"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="@id/no_collections_header"
|
||||||
|
app:srcCompat="@drawable/ic_close" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/no_collections_description"
|
android:id="@+id/no_collections_description"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="4dp"
|
android:layout_marginTop="4dp"
|
||||||
android:text="@string/no_collections_description1"
|
android:text="@string/no_collections_description2"
|
||||||
|
android:textAlignment="viewStart"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
android:textAlignment="viewStart" />
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/no_collections_header" />
|
||||||
|
|
||||||
<com.google.android.material.button.MaterialButton
|
<com.google.android.material.button.MaterialButton
|
||||||
android:id="@+id/add_tabs_to_collections_button"
|
android:id="@+id/add_tabs_to_collections_button"
|
||||||
style="@style/PositiveButton"
|
style="@style/PositiveButton"
|
||||||
app:icon="@drawable/ic_tab_collection"
|
android:layout_marginTop="8dp"
|
||||||
android:visibility="gone"
|
|
||||||
android:text="@string/tabs_menu_save_to_collection1"
|
android:text="@string/tabs_menu_save_to_collection1"
|
||||||
android:layout_marginTop="8dp"/>
|
android:visibility="gone"
|
||||||
</LinearLayout>
|
app:icon="@drawable/ic_tab_collection"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/no_collections_description" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
|
@ -172,4 +172,5 @@
|
||||||
<!-- a11y -->
|
<!-- a11y -->
|
||||||
<dimen name="accessibility_min_height">48dp</dimen>
|
<dimen name="accessibility_min_height">48dp</dimen>
|
||||||
|
|
||||||
|
<dimen name="tap_increase_16">16dp</dimen>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -195,4 +195,6 @@
|
||||||
<string name="pref_key_default_browser" translatable="false">pref_key_default_browser</string>
|
<string name="pref_key_default_browser" translatable="false">pref_key_default_browser</string>
|
||||||
|
|
||||||
<string name="pref_key_login_exceptions" translatable="false">pref_key_login_exceptions</string>
|
<string name="pref_key_login_exceptions" translatable="false">pref_key_login_exceptions</string>
|
||||||
|
|
||||||
|
<string name="pref_key_show_collections_placeholder_home" translatable="false">pref_key_show_collections_home</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -725,10 +725,8 @@
|
||||||
<string name="collections_header">Collections</string>
|
<string name="collections_header">Collections</string>
|
||||||
<!-- Content description (not visible, for screen readers etc.): Opens the collection menu when pressed -->
|
<!-- Content description (not visible, for screen readers etc.): Opens the collection menu when pressed -->
|
||||||
<string name="collection_menu_button_content_description">Collection menu</string>
|
<string name="collection_menu_button_content_description">Collection menu</string>
|
||||||
<!-- No Open Tabs Message Header -->
|
|
||||||
<string name="no_collections_header1">Collect the things that matter to you</string>
|
|
||||||
<!-- Label to describe what collections are to a new user without any collections -->
|
<!-- Label to describe what collections are to a new user without any collections -->
|
||||||
<string name="no_collections_description1">Group together similar searches, sites, and tabs for quick access later.</string>
|
<string name="no_collections_description2">Collect the things that matter to you.\nGroup together similar searches, sites, and tabs for quick access later.</string>
|
||||||
<!-- Title for the "select tabs" step of the collection creator -->
|
<!-- Title for the "select tabs" step of the collection creator -->
|
||||||
<string name="create_collection_select_tabs">Select Tabs</string>
|
<string name="create_collection_select_tabs">Select Tabs</string>
|
||||||
<!-- Title for the "select collection" step of the collection creator -->
|
<!-- Title for the "select collection" step of the collection creator -->
|
||||||
|
@ -1467,4 +1465,6 @@
|
||||||
<!-- Confirmation dialog button text when top sites limit is reached. -->
|
<!-- Confirmation dialog button text when top sites limit is reached. -->
|
||||||
<string name="top_sites_max_limit_confirmation_button">OK, Got It</string>
|
<string name="top_sites_max_limit_confirmation_button">OK, Got It</string>
|
||||||
|
|
||||||
|
<!-- Content description for close button in collection placeholder. -->
|
||||||
|
<string name="remove_home_collection_placeholder_content_description">Remove</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -78,8 +78,10 @@ class DefaultSessionControlControllerTest {
|
||||||
collections = emptyList(),
|
collections = emptyList(),
|
||||||
expandedCollections = emptySet(),
|
expandedCollections = emptySet(),
|
||||||
mode = Mode.Normal,
|
mode = Mode.Normal,
|
||||||
topSites = emptyList()
|
topSites = emptyList(),
|
||||||
|
showCollectionPlaceholder = true
|
||||||
)
|
)
|
||||||
|
|
||||||
every { sessionManager.sessions } returns emptyList()
|
every { sessionManager.sessions } returns emptyList()
|
||||||
every { navController.currentDestination } returns mockk {
|
every { navController.currentDestination } returns mockk {
|
||||||
every { id } returns R.id.homeFragment
|
every { id } returns R.id.homeFragment
|
||||||
|
@ -94,6 +96,7 @@ class DefaultSessionControlControllerTest {
|
||||||
|
|
||||||
controller = DefaultSessionControlController(
|
controller = DefaultSessionControlController(
|
||||||
activity = activity,
|
activity = activity,
|
||||||
|
settings = settings,
|
||||||
engine = engine,
|
engine = engine,
|
||||||
metrics = metrics,
|
metrics = metrics,
|
||||||
sessionManager = sessionManager,
|
sessionManager = sessionManager,
|
||||||
|
@ -414,4 +417,14 @@ class DefaultSessionControlControllerTest {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun handleRemoveCollectionsPlaceholder() {
|
||||||
|
controller.handleRemoveCollectionsPlaceholder()
|
||||||
|
|
||||||
|
verify {
|
||||||
|
settings.showCollectionsPlaceholderOnHome = false
|
||||||
|
fragmentStore.dispatch(HomeFragmentAction.RemoveCollectionsPlaceholder)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import mozilla.components.feature.tab.collections.TabCollection
|
||||||
import mozilla.components.feature.top.sites.TopSite
|
import mozilla.components.feature.top.sites.TopSite
|
||||||
import mozilla.components.service.fxa.manager.FxaAccountManager
|
import mozilla.components.service.fxa.manager.FxaAccountManager
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Assert.assertFalse
|
||||||
import org.junit.Assert.assertTrue
|
import org.junit.Assert.assertTrue
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
@ -53,7 +54,8 @@ class HomeFragmentStoreTest {
|
||||||
collections = emptyList(),
|
collections = emptyList(),
|
||||||
expandedCollections = emptySet(),
|
expandedCollections = emptySet(),
|
||||||
mode = currentMode.getCurrentMode(),
|
mode = currentMode.getCurrentMode(),
|
||||||
topSites = emptyList()
|
topSites = emptyList(),
|
||||||
|
showCollectionPlaceholder = true
|
||||||
)
|
)
|
||||||
|
|
||||||
homeFragmentStore = HomeFragmentStore(homeFragmentState)
|
homeFragmentStore = HomeFragmentStore(homeFragmentState)
|
||||||
|
@ -95,6 +97,15 @@ class HomeFragmentStoreTest {
|
||||||
assertEquals(topSites, homeFragmentStore.state.topSites)
|
assertEquals(topSites, homeFragmentStore.state.topSites)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Test changing hiding collections placeholder`() = runBlocking {
|
||||||
|
assertTrue(homeFragmentStore.state.showCollectionPlaceholder)
|
||||||
|
|
||||||
|
homeFragmentStore.dispatch(HomeFragmentAction.RemoveCollectionsPlaceholder).join()
|
||||||
|
|
||||||
|
assertFalse(homeFragmentStore.state.showCollectionPlaceholder)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `Test changing the expanded collections in HomeFragmentStore`() = runBlocking {
|
fun `Test changing the expanded collections in HomeFragmentStore`() = runBlocking {
|
||||||
val collection: TabCollection = mockk<TabCollection>().apply {
|
val collection: TabCollection = mockk<TabCollection>().apply {
|
||||||
|
@ -110,25 +121,27 @@ class HomeFragmentStoreTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `Test changing the collections, mode and top sites in the HomeFragmentStore`() = runBlocking {
|
fun `Test changing the collections, mode and top sites in the HomeFragmentStore`() =
|
||||||
// Verify that the default state of the HomeFragment is correct.
|
runBlocking {
|
||||||
assertEquals(0, homeFragmentStore.state.collections.size)
|
// Verify that the default state of the HomeFragment is correct.
|
||||||
assertEquals(0, homeFragmentStore.state.topSites.size)
|
assertEquals(0, homeFragmentStore.state.collections.size)
|
||||||
assertEquals(Mode.Normal, homeFragmentStore.state.mode)
|
assertEquals(0, homeFragmentStore.state.topSites.size)
|
||||||
|
assertEquals(Mode.Normal, homeFragmentStore.state.mode)
|
||||||
|
|
||||||
val collections: List<TabCollection> = listOf(mockk())
|
val collections: List<TabCollection> = listOf(mockk())
|
||||||
val topSites: List<TopSite> = listOf(mockk(), mockk())
|
val topSites: List<TopSite> = listOf(mockk(), mockk())
|
||||||
|
|
||||||
homeFragmentStore.dispatch(
|
homeFragmentStore.dispatch(
|
||||||
HomeFragmentAction.Change(
|
HomeFragmentAction.Change(
|
||||||
collections = collections,
|
collections = collections,
|
||||||
mode = Mode.Private,
|
mode = Mode.Private,
|
||||||
topSites = topSites
|
topSites = topSites,
|
||||||
)
|
showCollectionPlaceholder = true
|
||||||
).join()
|
)
|
||||||
|
).join()
|
||||||
|
|
||||||
assertEquals(1, homeFragmentStore.state.collections.size)
|
assertEquals(1, homeFragmentStore.state.collections.size)
|
||||||
assertEquals(Mode.Private, homeFragmentStore.state.mode)
|
assertEquals(Mode.Private, homeFragmentStore.state.mode)
|
||||||
assertEquals(2, homeFragmentStore.state.topSites.size)
|
assertEquals(2, homeFragmentStore.state.topSites.size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,4 +110,10 @@ class SessionControlInteractorTest {
|
||||||
interactor.onPasteAndGo("text")
|
interactor.onPasteAndGo("text")
|
||||||
verify { controller.handlePasteAndGo("text") }
|
verify { controller.handlePasteAndGo("text") }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun onRemoveCollectionsPlaceholder() {
|
||||||
|
interactor.onRemoveCollectionsPlaceholder()
|
||||||
|
verify { controller.handleRemoveCollectionsPlaceholder() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,9 +8,13 @@ import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.appcompat.view.ContextThemeWrapper
|
import androidx.appcompat.view.ContextThemeWrapper
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
|
import androidx.lifecycle.LifecycleOwner
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import io.mockk.verify
|
import io.mockk.verify
|
||||||
import kotlinx.android.synthetic.main.no_collections_message.view.*
|
import kotlinx.android.synthetic.main.no_collections_message.view.*
|
||||||
|
import mozilla.components.browser.state.state.BrowserState
|
||||||
|
import mozilla.components.browser.state.state.createTab
|
||||||
|
import mozilla.components.browser.state.store.BrowserStore
|
||||||
import mozilla.components.support.test.robolectric.testContext
|
import mozilla.components.support.test.robolectric.testContext
|
||||||
import org.junit.Assert.assertFalse
|
import org.junit.Assert.assertFalse
|
||||||
import org.junit.Assert.assertTrue
|
import org.junit.Assert.assertTrue
|
||||||
|
@ -25,6 +29,14 @@ import org.mozilla.fenix.home.sessioncontrol.CollectionInteractor
|
||||||
class NoCollectionsMessageViewHolderTest {
|
class NoCollectionsMessageViewHolderTest {
|
||||||
|
|
||||||
private lateinit var view: View
|
private lateinit var view: View
|
||||||
|
private val store: BrowserStore = BrowserStore(
|
||||||
|
initialState = BrowserState(
|
||||||
|
listOf(
|
||||||
|
createTab("https://www.mozilla.org", id = "reader-inactive-tab")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
private lateinit var lifecycleOwner: LifecycleOwner
|
||||||
private lateinit var interactor: CollectionInteractor
|
private lateinit var interactor: CollectionInteractor
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
|
@ -32,28 +44,40 @@ class NoCollectionsMessageViewHolderTest {
|
||||||
val appCompatContext = ContextThemeWrapper(testContext, R.style.NormalTheme)
|
val appCompatContext = ContextThemeWrapper(testContext, R.style.NormalTheme)
|
||||||
view = LayoutInflater.from(appCompatContext)
|
view = LayoutInflater.from(appCompatContext)
|
||||||
.inflate(NoCollectionsMessageViewHolder.LAYOUT_ID, null)
|
.inflate(NoCollectionsMessageViewHolder.LAYOUT_ID, null)
|
||||||
|
lifecycleOwner = mockk(relaxed = true)
|
||||||
interactor = mockk(relaxed = true)
|
interactor = mockk(relaxed = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `hide button when hasNormalTabsOpened is false`() {
|
fun `hide add to collection button when there are no tabs open`() {
|
||||||
NoCollectionsMessageViewHolder(view, interactor, hasNormalTabsOpened = false)
|
val noTabsStore = BrowserStore()
|
||||||
|
NoCollectionsMessageViewHolder(view, lifecycleOwner, noTabsStore, interactor)
|
||||||
|
|
||||||
assertFalse(view.add_tabs_to_collections_button.isVisible)
|
assertFalse(view.add_tabs_to_collections_button.isVisible)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `show button when hasNormalTabsOpened is true`() {
|
fun `show add to collection button when there are tabs`() {
|
||||||
NoCollectionsMessageViewHolder(view, interactor, hasNormalTabsOpened = true)
|
NoCollectionsMessageViewHolder(view, lifecycleOwner, store, interactor)
|
||||||
|
|
||||||
assertTrue(view.add_tabs_to_collections_button.isVisible)
|
assertTrue(view.add_tabs_to_collections_button.isVisible)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `call interactor on click`() {
|
fun `call interactor on click`() {
|
||||||
NoCollectionsMessageViewHolder(view, interactor, hasNormalTabsOpened = true)
|
NoCollectionsMessageViewHolder(view, lifecycleOwner, store, interactor)
|
||||||
|
|
||||||
view.add_tabs_to_collections_button.performClick()
|
view.add_tabs_to_collections_button.performClick()
|
||||||
verify { interactor.onAddTabsToCollectionTapped() }
|
verify { interactor.onAddTabsToCollectionTapped() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `hide view and change setting on remove placeholder click`() {
|
||||||
|
NoCollectionsMessageViewHolder(view, lifecycleOwner, store, interactor)
|
||||||
|
|
||||||
|
view.remove_collection_placeholder.performClick()
|
||||||
|
verify {
|
||||||
|
interactor.onRemoveCollectionsPlaceholder()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -289,6 +289,19 @@ class SettingsTest {
|
||||||
assertFalse(settings.shouldUseTrackingProtection)
|
assertFalse(settings.shouldUseTrackingProtection)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun shouldShowCollectionsPlaceholderOnHome() {
|
||||||
|
// When
|
||||||
|
// Then
|
||||||
|
assertTrue(settings.showCollectionsPlaceholderOnHome)
|
||||||
|
|
||||||
|
// When
|
||||||
|
settings.showCollectionsPlaceholderOnHome = false
|
||||||
|
|
||||||
|
// Then
|
||||||
|
assertFalse(settings.showCollectionsPlaceholderOnHome)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun shouldSetOpenInAppOpened() {
|
fun shouldSetOpenInAppOpened() {
|
||||||
// When
|
// When
|
||||||
|
|
Loading…
Reference in New Issue