Close #19045: Dismiss tabstray when last tab in a page is closed
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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…
Reference in New Issue