Close #19045: Dismiss tabstray when last tab in a page is closed

upstream-sync
Jonathan Almeida 3 years ago committed by Jonathan Almeida
parent 3d226429aa
commit 6c8b1a7e8f

@ -0,0 +1,50 @@
/* 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 kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.flow.map
import mozilla.components.browser.state.selector.normalTabs
import mozilla.components.browser.state.selector.privateTabs
import mozilla.components.browser.state.state.BrowserState
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged
import org.mozilla.fenix.components.AbstractBinding
/**
* A binding that closes the tabs tray when the last tab is closed.
*/
class CloseOnLastTabBinding(
browserStore: BrowserStore,
private val tabsTrayStore: TabsTrayStore,
private val navigationInteractor: NavigationInteractor
) : AbstractBinding<BrowserState>(browserStore) {
override suspend fun onState(flow: Flow<BrowserState>) {
flow.map { it }
// Ignore the initial state; we don't want to close immediately.
.drop(1)
.ifChanged { it.tabs }
.collect { state ->
val selectedPage = tabsTrayStore.state.selectedPage
val tabs = when (selectedPage) {
Page.NormalTabs -> {
state.normalTabs
}
Page.PrivateTabs -> {
state.privateTabs
}
else -> {
// Do nothing if we're on any other non-browser page.
null
}
}
if (tabs?.isEmpty() == true) {
navigationInteractor.onCloseAllTabsClicked(selectedPage == Page.PrivateTabs)
}
}
}
}

@ -59,6 +59,7 @@ class TabsTrayFragment : AppCompatDialogFragment(), TabsTrayInteractor {
private val selectionBannerBinding = ViewBoundFeatureWrapper<SelectionBannerBinding>()
private val selectionHandleBinding = ViewBoundFeatureWrapper<SelectionHandleBinding>()
private val tabsTrayCtaBinding = ViewBoundFeatureWrapper<TabsTrayInfoBannerBinding>()
private val closeOnLastTabBinding = ViewBoundFeatureWrapper<CloseOnLastTabBinding>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -228,6 +229,16 @@ class TabsTrayFragment : AppCompatDialogFragment(), TabsTrayInteractor {
owner = this,
view = view
)
closeOnLastTabBinding.set(
feature = CloseOnLastTabBinding(
browserStore = requireComponents.core.store,
tabsTrayStore = tabsTrayStore,
navigationInteractor = navigationInteractor
),
owner = this,
view = view
)
}
override fun setCurrentTrayPosition(position: Int, smoothScroll: Boolean) {

@ -0,0 +1,115 @@
/* 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.Called
import io.mockk.mockk
import io.mockk.verify
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.TestCoroutineDispatcher
import mozilla.components.browser.state.action.TabListAction
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.libstate.ext.waitUntilIdle
import mozilla.components.support.test.rule.MainCoroutineRule
import org.junit.Rule
import org.junit.Test
class CloseOnLastTabBindingTest {
@OptIn(ExperimentalCoroutinesApi::class)
@get:Rule
val coroutinesTestRule = MainCoroutineRule(TestCoroutineDispatcher())
@Test
fun `WHEN the binding starts THEN do nothing`() {
val browserStore = BrowserStore()
val tabsTrayStore = TabsTrayStore()
val interactor = mockk<NavigationInteractor>(relaxed = true)
val binding = CloseOnLastTabBinding(browserStore, tabsTrayStore, interactor)
binding.start()
verify { interactor wasNot Called }
}
@Test
fun `WHEN a tab is closed THEN invoke the interactor`() {
val browserStore = BrowserStore(
BrowserState(
tabs = listOf(
createTab(
"https://mozilla.org",
id = "tab1"
)
)
)
)
val tabsTrayStore = TabsTrayStore()
val interactor = mockk<NavigationInteractor>(relaxed = true)
val binding = CloseOnLastTabBinding(browserStore, tabsTrayStore, interactor)
binding.start()
browserStore.dispatch(TabListAction.RemoveTabAction("tab1"))
browserStore.waitUntilIdle()
verify { interactor.onCloseAllTabsClicked(false) }
}
@Test
fun `WHEN a private tab is closed THEN invoke the interactor`() {
val browserStore = BrowserStore(
BrowserState(
tabs = listOf(
createTab(
"https://mozilla.org",
id = "tab1",
private = true
)
)
)
)
val tabsTrayStore = TabsTrayStore(TabsTrayState(selectedPage = Page.PrivateTabs))
val interactor = mockk<NavigationInteractor>(relaxed = true)
val binding = CloseOnLastTabBinding(browserStore, tabsTrayStore, interactor)
binding.start()
browserStore.dispatch(TabListAction.RemoveTabAction("tab1"))
browserStore.waitUntilIdle()
verify { interactor.onCloseAllTabsClicked(true) }
}
@Test
fun `WHEN on the synced tabs page THEN nothing is invoked`() {
val browserStore = BrowserStore(
BrowserState(
tabs = listOf(
createTab(
"https://mozilla.org",
id = "tab1",
private = true
)
)
)
)
val tabsTrayStore = TabsTrayStore(TabsTrayState(selectedPage = Page.SyncedTabs))
val interactor = mockk<NavigationInteractor>(relaxed = true)
val binding = CloseOnLastTabBinding(browserStore, tabsTrayStore, interactor)
binding.start()
browserStore.dispatch(TabListAction.RemoveAllTabsAction)
browserStore.waitUntilIdle()
verify { interactor wasNot Called }
}
}
Loading…
Cancel
Save