No issue: Move menu to separate integrations class

upstream-sync
Jonathan Almeida 3 years ago committed by Jonathan Almeida
parent dd3e95b4cf
commit 2e3086ac40

@ -0,0 +1,73 @@
/* 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
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.tabstray
import android.content.Context
import android.view.View
import androidx.annotation.VisibleForTesting
import androidx.cardview.widget.CardView
import androidx.core.content.ContextCompat
import com.google.android.material.tabs.TabLayout
import mozilla.components.browser.menu.BrowserMenu
import mozilla.components.browser.menu.BrowserMenuBuilder
import mozilla.components.browser.state.store.BrowserStore
import org.mozilla.fenix.R
/**
* A wrapper class that building the tabs tray menu that handles item clicks.
*/
class MenuIntegration(
private val context: Context,
private val browserStore: BrowserStore,
private val tabsTrayStore: TabsTrayStore,
private val tabLayout: TabLayout,
private val navigationInteractor: NavigationInteractor
) {
private val tabsTrayItemMenu by lazy {
TabsTrayMenu(
context = context,
browserStore = browserStore,
tabLayout = tabLayout,
onItemTapped = ::handleMenuClicked
)
}
private val isPrivateMode: Boolean
get() = tabsTrayStore.state.selectedPage == Page.PrivateTabs
/**
* Builds the internal menu items list. See [BrowserMenuBuilder.build].
*/
fun build() = tabsTrayItemMenu.menuBuilder.build(context)
@VisibleForTesting
internal fun handleMenuClicked(item: TabsTrayMenu.Item) = when (item) {
is TabsTrayMenu.Item.ShareAllTabs ->
navigationInteractor.onShareTabsOfTypeClicked(isPrivateMode)
is TabsTrayMenu.Item.OpenTabSettings ->
navigationInteractor.onTabSettingsClicked()
is TabsTrayMenu.Item.CloseAllTabs ->
navigationInteractor.onCloseAllTabsClicked(isPrivateMode)
is TabsTrayMenu.Item.OpenRecentlyClosed ->
navigationInteractor.onOpenRecentlyClosedClicked()
is TabsTrayMenu.Item.SelectTabs -> {
/* TODO implement when mulitiselect call is available */
}
}
}
/**
* Invokes [BrowserMenu.show] and applies the default theme color background.
*/
fun BrowserMenu.showWithTheme(view: View) {
show(view).also { popupMenu ->
(popupMenu.contentView as? CardView)?.setCardBackgroundColor(
ContextCompat.getColor(
view.context,
R.color.foundation_normal_theme
)
)
}
}

@ -10,13 +10,10 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.appcompat.app.AppCompatDialogFragment import androidx.appcompat.app.AppCompatDialogFragment
import androidx.cardview.widget.CardView
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.findNavController
import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.tabs.TabLayout
import kotlinx.android.synthetic.main.component_tabstray2.* import kotlinx.android.synthetic.main.component_tabstray2.*
import kotlinx.android.synthetic.main.component_tabstray2.view.* import kotlinx.android.synthetic.main.component_tabstray2.view.*
import kotlinx.android.synthetic.main.tabs_tray_tab_counter2.* import kotlinx.android.synthetic.main.tabs_tray_tab_counter2.*
@ -26,7 +23,6 @@ import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.NavGraphDirections import org.mozilla.fenix.NavGraphDirections
import org.mozilla.fenix.R import org.mozilla.fenix.R
import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.components.StoreProvider import org.mozilla.fenix.components.StoreProvider
import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.home.HomeScreenViewModel import org.mozilla.fenix.home.HomeScreenViewModel
@ -38,13 +34,6 @@ import org.mozilla.fenix.tabstray.syncedtabs.SyncedTabsInteractor
class TabsTrayFragment : AppCompatDialogFragment(), TabsTrayInteractor { class TabsTrayFragment : AppCompatDialogFragment(), TabsTrayInteractor {
private lateinit var navigationInteractor: NavigationInteractor
private val tabLayout: TabLayout? get() =
view?.tab_layout
private val isPrivateModeSelected: Boolean get() =
tabLayout?.selectedTabPosition == TrayPagerAdapter.POSITION_PRIVATE_TABS
private lateinit var tabsTrayStore: TabsTrayStore private lateinit var tabsTrayStore: TabsTrayStore
private lateinit var browserTrayInteractor: BrowserTrayInteractor private lateinit var browserTrayInteractor: BrowserTrayInteractor
private lateinit var behavior: BottomSheetBehavior<ConstraintLayout> private lateinit var behavior: BottomSheetBehavior<ConstraintLayout>
@ -85,19 +74,9 @@ class TabsTrayFragment : AppCompatDialogFragment(), TabsTrayInteractor {
val containerView = inflater.inflate(R.layout.fragment_tab_tray_dialog, container, false) val containerView = inflater.inflate(R.layout.fragment_tab_tray_dialog, container, false)
val view: View = LayoutInflater.from(containerView.context) val view: View = LayoutInflater.from(containerView.context)
.inflate(R.layout.component_tabstray2, containerView as ViewGroup, true) .inflate(R.layout.component_tabstray2, containerView as ViewGroup, true)
val activity = activity as HomeActivity
behavior = BottomSheetBehavior.from(view.tab_wrapper) behavior = BottomSheetBehavior.from(view.tab_wrapper)
navigationInteractor =
DefaultNavigationInteractor(
browserStore = activity.components.core.store,
navController = findNavController(),
metrics = activity.components.analytics.metrics,
dismissTabTray = ::dismissAllowingStateLoss,
dismissTabTrayAndNavigateHome = ::dismissTabTrayAndNavigateHome
)
tabsTrayStore = StoreProvider.get(this) { TabsTrayStore() } tabsTrayStore = StoreProvider.get(this) { TabsTrayStore() }
return containerView return containerView
@ -106,7 +85,6 @@ class TabsTrayFragment : AppCompatDialogFragment(), TabsTrayInteractor {
@ExperimentalCoroutinesApi @ExperimentalCoroutinesApi
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
setupMenu(view)
browserTrayInteractor = DefaultBrowserTrayInteractor( browserTrayInteractor = DefaultBrowserTrayInteractor(
tabsTrayStore, tabsTrayStore,
@ -116,12 +94,22 @@ class TabsTrayFragment : AppCompatDialogFragment(), TabsTrayInteractor {
this this
) )
val navigationInteractor =
DefaultNavigationInteractor(
browserStore = requireComponents.core.store,
navController = findNavController(),
metrics = requireComponents.analytics.metrics,
dismissTabTray = ::dismissAllowingStateLoss,
dismissTabTrayAndNavigateHome = ::dismissTabTrayAndNavigateHome
)
val syncedTabsTrayInteractor = SyncedTabsInteractor( val syncedTabsTrayInteractor = SyncedTabsInteractor(
requireComponents.analytics.metrics, requireComponents.analytics.metrics,
requireActivity() as HomeActivity, requireActivity() as HomeActivity,
this this
) )
setupMenu(view, navigationInteractor)
setupPager( setupPager(
view.context, view.context,
tabsTrayStore, tabsTrayStore,
@ -195,40 +183,23 @@ class TabsTrayFragment : AppCompatDialogFragment(), TabsTrayInteractor {
} }
} }
private fun setupMenu(view: View) { private fun setupMenu(view: View, navigationInteractor: NavigationInteractor) {
view.tab_tray_overflow.setOnClickListener { anchor -> view.tab_tray_overflow.setOnClickListener { anchor ->
val tabTrayItemMenu =
TabsTrayMenu(
context = view.context,
browserStore = requireComponents.core.store,
tabLayout = tab_layout
) {
when (it) {
is TabsTrayMenu.Item.ShareAllTabs ->
navigationInteractor.onShareTabsOfTypeClicked(isPrivateModeSelected)
is TabsTrayMenu.Item.OpenTabSettings ->
navigationInteractor.onTabSettingsClicked()
is TabsTrayMenu.Item.CloseAllTabs ->
navigationInteractor.onCloseAllTabsClicked(isPrivateModeSelected)
is TabsTrayMenu.Item.OpenRecentlyClosed ->
navigationInteractor.onOpenRecentlyClosedClicked()
is TabsTrayMenu.Item.SelectTabs ->
{ /* TODO implement when mulitiselect call is available */ }
}
}
requireComponents.analytics.metrics.track(Event.TabsTrayMenuOpened) requireComponents.analytics.metrics.track(Event.TabsTrayMenuOpened)
val menu = tabTrayItemMenu.menuBuilder.build(view.context)
menu.show(anchor).also { popupMenu -> val menu = MenuIntegration(
(popupMenu.contentView as? CardView)?.setCardBackgroundColor( context = requireContext(),
ContextCompat.getColor( browserStore = requireComponents.core.store,
view.context, tabsTrayStore = TabsTrayStore(),
R.color.foundation_normal_theme tabLayout = tab_layout,
) navigationInteractor = navigationInteractor
) ).build()
}
menu.showWithTheme(anchor)
} }
} }
private val homeViewModel: HomeScreenViewModel by activityViewModels() private val homeViewModel: HomeScreenViewModel by activityViewModels()
private fun dismissTabTrayAndNavigateHome(sessionId: String) { private fun dismissTabTrayAndNavigateHome(sessionId: String) {

@ -6,6 +6,7 @@ package org.mozilla.fenix.tabstray
import mozilla.components.concept.tabstray.Tab import mozilla.components.concept.tabstray.Tab
import mozilla.components.lib.state.Action import mozilla.components.lib.state.Action
import mozilla.components.lib.state.Middleware
import mozilla.components.lib.state.State import mozilla.components.lib.state.State
import mozilla.components.lib.state.Store import mozilla.components.lib.state.Store
@ -147,8 +148,10 @@ internal object TabsTrayReducer {
* dispatched to the store. * dispatched to the store.
*/ */
class TabsTrayStore( class TabsTrayStore(
initialState: TabsTrayState = TabsTrayState() initialState: TabsTrayState = TabsTrayState(),
middlewares: List<Middleware<TabsTrayState, TabsTrayAction>> = emptyList()
) : Store<TabsTrayState, TabsTrayAction>( ) : Store<TabsTrayState, TabsTrayAction>(
initialState, initialState,
TabsTrayReducer::reduce TabsTrayReducer::reduce,
middlewares
) )

@ -0,0 +1,65 @@
/* 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
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.tabstray
import io.mockk.mockk
import io.mockk.verify
import mozilla.components.support.test.middleware.CaptureActionsMiddleware
import org.junit.Assert.assertNotNull
import org.junit.Ignore
import org.junit.Test
class MenuIntegrationTest {
private val captureMiddleware = CaptureActionsMiddleware<TabsTrayState, TabsTrayAction>()
private val tabsTrayStore = TabsTrayStore(middlewares = listOf(captureMiddleware))
private val interactor = mockk<NavigationInteractor>(relaxed = true)
@Test
fun `WHEN the share all menu item is clicked THEN invoke the action`() {
val menu = MenuIntegration(mockk(), mockk(), tabsTrayStore, mockk(), interactor)
menu.handleMenuClicked(TabsTrayMenu.Item.ShareAllTabs)
verify { interactor.onShareTabsOfTypeClicked(false) }
}
@Test
fun `WHEN the open settings menu item is clicked THEN invoke the action`() {
val menu = MenuIntegration(mockk(), mockk(), tabsTrayStore, mockk(), interactor)
menu.handleMenuClicked(TabsTrayMenu.Item.OpenTabSettings)
verify { interactor.onTabSettingsClicked() }
}
@Test
fun `WHEN the close all menu item is clicked THEN invoke the action`() {
val menu = MenuIntegration(mockk(), mockk(), tabsTrayStore, mockk(), interactor)
menu.handleMenuClicked(TabsTrayMenu.Item.CloseAllTabs)
verify { interactor.onCloseAllTabsClicked(false) }
}
@Test
fun `WHEN the recently menu item is clicked THEN invoke the action`() {
val menu = MenuIntegration(mockk(), mockk(), tabsTrayStore, mockk(), interactor)
menu.handleMenuClicked(TabsTrayMenu.Item.OpenRecentlyClosed)
verify { interactor.onOpenRecentlyClosedClicked() }
}
@Ignore("Enable after we connect this menu item to the store")
@Test
fun `WHEN the select menu item is clicked THEN invoke the action`() {
val menu = MenuIntegration(mockk(), mockk(), tabsTrayStore, mockk(), interactor)
menu.handleMenuClicked(TabsTrayMenu.Item.ShareAllTabs)
assertNotNull(captureMiddleware.findLastAction(TabsTrayAction.EnterSelectMode::class))
}
}
Loading…
Cancel
Save