mirror of
https://github.com/fork-maintainers/iceraven-browser
synced 2024-11-03 23:15:31 +00:00
[fenix] Close https://github.com/mozilla-mobile/fenix/issues/17821: Add TabsTrayStore with actions and reducer (https://github.com/mozilla-mobile/fenix/pull/18773)
This commit is contained in:
parent
5beadfdb15
commit
94688bf59b
154
app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayStore.kt
Normal file
154
app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayStore.kt
Normal file
@ -0,0 +1,154 @@
|
||||
/* 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 mozilla.components.concept.tabstray.Tab
|
||||
import mozilla.components.lib.state.Action
|
||||
import mozilla.components.lib.state.State
|
||||
import mozilla.components.lib.state.Store
|
||||
|
||||
/**
|
||||
* Value type that represents the state of the tabs tray.
|
||||
*
|
||||
* @property selectedPage The current page in the tray can be on.
|
||||
* @property mode Whether the browser tab list is in multi-select mode or not with the set of
|
||||
* currently selected tabs.
|
||||
* @property syncing Whether the Synced Tabs feature should fetch the latest tabs from paired
|
||||
* devices.
|
||||
*/
|
||||
data class TabsTrayState(
|
||||
val selectedPage: Page = Page.NormalTabs,
|
||||
val mode: Mode = Mode.Normal,
|
||||
val syncing: Boolean = false
|
||||
) : State {
|
||||
|
||||
/**
|
||||
* The current mode that the tabs list is in.
|
||||
*/
|
||||
sealed class Mode {
|
||||
|
||||
/**
|
||||
* A set of selected tabs which we would want to perform an action on.
|
||||
*/
|
||||
open val selectedTabs = emptySet<Tab>()
|
||||
|
||||
/**
|
||||
* The default mode the tabs list is in.
|
||||
*/
|
||||
object Normal : Mode()
|
||||
|
||||
/**
|
||||
* The multi-select mode that the tabs list is in containing the set of currently
|
||||
* selected tabs.
|
||||
*/
|
||||
data class Select(override val selectedTabs: Set<Tab>) : Mode()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The different pagers in the tray that we can switch between in the [TrayPagerAdapter].
|
||||
*/
|
||||
enum class Page {
|
||||
|
||||
/**
|
||||
* The pager position that displays normal tabs.
|
||||
*/
|
||||
NormalTabs,
|
||||
|
||||
/**
|
||||
* The pager position that displays private tabs.
|
||||
*/
|
||||
PrivateTabs,
|
||||
|
||||
/**
|
||||
* The pager position that displays Synced Tabs.
|
||||
*/
|
||||
SyncedTabs
|
||||
}
|
||||
|
||||
/**
|
||||
* [Action] implementation related to [TabsTrayStore].
|
||||
*/
|
||||
sealed class TabsTrayAction : Action {
|
||||
|
||||
/**
|
||||
* Entered multi-select mode.
|
||||
*/
|
||||
object EnterSelectMode : TabsTrayAction()
|
||||
|
||||
/**
|
||||
* Exited multi-select mode.
|
||||
*/
|
||||
object ExitSelectMode : TabsTrayAction()
|
||||
|
||||
/**
|
||||
* Added a new [Tab] to the selection set.
|
||||
*/
|
||||
data class AddSelectTab(val tab: Tab) : TabsTrayAction()
|
||||
|
||||
/**
|
||||
* Removed a [Tab] from the selection set.
|
||||
*/
|
||||
data class RemoveSelectTab(val tab: Tab) : TabsTrayAction()
|
||||
|
||||
/**
|
||||
* The active page in the tray that is now in focus.
|
||||
*/
|
||||
data class PageSelected(val page: Page) : TabsTrayAction()
|
||||
|
||||
/**
|
||||
* A request to perform a "sync" action.
|
||||
*/
|
||||
object SyncNow : TabsTrayAction()
|
||||
|
||||
/**
|
||||
* When a "sync" action has completed; this can be triggered immediately after [SyncNow] if
|
||||
* no sync action was able to be performed.
|
||||
*/
|
||||
object SyncCompleted : TabsTrayAction()
|
||||
}
|
||||
|
||||
/**
|
||||
* Reducer for [TabsTrayStore].
|
||||
*/
|
||||
internal object TabsTrayReducer {
|
||||
fun reduce(state: TabsTrayState, action: TabsTrayAction): TabsTrayState {
|
||||
return when (action) {
|
||||
is TabsTrayAction.EnterSelectMode ->
|
||||
state.copy(mode = TabsTrayState.Mode.Select(emptySet()))
|
||||
is TabsTrayAction.ExitSelectMode ->
|
||||
state.copy(mode = TabsTrayState.Mode.Normal)
|
||||
is TabsTrayAction.AddSelectTab ->
|
||||
state.copy(mode = TabsTrayState.Mode.Select(state.mode.selectedTabs + action.tab))
|
||||
is TabsTrayAction.RemoveSelectTab -> {
|
||||
val selected = state.mode.selectedTabs - action.tab
|
||||
state.copy(
|
||||
mode = if (selected.isEmpty()) {
|
||||
TabsTrayState.Mode.Normal
|
||||
} else {
|
||||
TabsTrayState.Mode.Select(selected)
|
||||
}
|
||||
)
|
||||
}
|
||||
is TabsTrayAction.PageSelected ->
|
||||
state.copy(selectedPage = action.page)
|
||||
is TabsTrayAction.SyncNow ->
|
||||
state.copy(syncing = true)
|
||||
is TabsTrayAction.SyncCompleted ->
|
||||
state.copy(syncing = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A [Store] that holds the [TabsTrayState] for the tabs tray and reduces [TabsTrayAction]s
|
||||
* dispatched to the store.
|
||||
*/
|
||||
class TabsTrayStore(
|
||||
initialState: TabsTrayState = TabsTrayState()
|
||||
) : Store<TabsTrayState, TabsTrayAction>(
|
||||
initialState,
|
||||
TabsTrayReducer::reduce
|
||||
)
|
@ -74,8 +74,8 @@ class TrayPagerAdapter(
|
||||
companion object {
|
||||
const val TRAY_TABS_COUNT = 3
|
||||
|
||||
const val POSITION_NORMAL_TABS = 0
|
||||
const val POSITION_PRIVATE_TABS = 1
|
||||
const val POSITION_SYNCED_TABS = 2
|
||||
val POSITION_NORMAL_TABS = Page.NormalTabs.ordinal
|
||||
val POSITION_PRIVATE_TABS = Page.PrivateTabs.ordinal
|
||||
val POSITION_SYNCED_TABS = Page.SyncedTabs.ordinal
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,131 @@
|
||||
/* 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 mozilla.components.support.test.libstate.ext.waitUntilIdle
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.tabstray.browser.createTab
|
||||
|
||||
class TabsTrayStoreTest {
|
||||
|
||||
@Test
|
||||
fun `WHEN entering select mode THEN selected tabs are empty`() {
|
||||
val store = TabsTrayStore()
|
||||
|
||||
store.dispatch(TabsTrayAction.EnterSelectMode)
|
||||
|
||||
store.waitUntilIdle()
|
||||
|
||||
assertTrue(store.state.mode.selectedTabs.isEmpty())
|
||||
assertTrue(store.state.mode is TabsTrayState.Mode.Select)
|
||||
|
||||
store.dispatch(TabsTrayAction.AddSelectTab(createTab()))
|
||||
|
||||
store.dispatch(TabsTrayAction.ExitSelectMode)
|
||||
store.dispatch(TabsTrayAction.EnterSelectMode)
|
||||
|
||||
store.waitUntilIdle()
|
||||
|
||||
assertTrue(store.state.mode.selectedTabs.isEmpty())
|
||||
assertTrue(store.state.mode is TabsTrayState.Mode.Select)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN exiting select mode THEN the mode in the state updates`() {
|
||||
val store = TabsTrayStore()
|
||||
|
||||
store.dispatch(TabsTrayAction.EnterSelectMode)
|
||||
|
||||
store.waitUntilIdle()
|
||||
|
||||
assertTrue(store.state.mode is TabsTrayState.Mode.Select)
|
||||
|
||||
store.dispatch(TabsTrayAction.ExitSelectMode)
|
||||
|
||||
store.waitUntilIdle()
|
||||
|
||||
assertTrue(store.state.mode is TabsTrayState.Mode.Normal)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN adding a tab to selection THEN it is added to the selectedTabs`() {
|
||||
val store = TabsTrayStore()
|
||||
|
||||
store.dispatch(TabsTrayAction.AddSelectTab(createTab("tab1")))
|
||||
|
||||
store.waitUntilIdle()
|
||||
|
||||
assertEquals("tab1", store.state.mode.selectedTabs.take(1).first().id)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN removing a tab THEN it is removed from the selectedTabs`() {
|
||||
val store = TabsTrayStore()
|
||||
val tabForRemoval = createTab("tab1")
|
||||
|
||||
store.dispatch(TabsTrayAction.AddSelectTab(tabForRemoval))
|
||||
store.dispatch(TabsTrayAction.AddSelectTab(createTab("tab2")))
|
||||
|
||||
store.waitUntilIdle()
|
||||
|
||||
assertEquals(2, store.state.mode.selectedTabs.size)
|
||||
|
||||
store.dispatch(TabsTrayAction.RemoveSelectTab(tabForRemoval))
|
||||
|
||||
store.waitUntilIdle()
|
||||
|
||||
assertEquals(1, store.state.mode.selectedTabs.size)
|
||||
assertEquals("tab2", store.state.mode.selectedTabs.take(1).first().id)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN store is initialized THEN the default page selected in normal tabs`() {
|
||||
val store = TabsTrayStore()
|
||||
|
||||
assertEquals(Page.NormalTabs, store.state.selectedPage)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN page changes THEN the selectedPage is updated`() {
|
||||
val store = TabsTrayStore()
|
||||
|
||||
assertEquals(Page.NormalTabs, store.state.selectedPage)
|
||||
|
||||
store.dispatch(TabsTrayAction.PageSelected(Page.SyncedTabs))
|
||||
|
||||
store.waitUntilIdle()
|
||||
|
||||
assertEquals(Page.SyncedTabs, store.state.selectedPage)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN sync now action is triggered THEN update the sync now boolean`() {
|
||||
val store = TabsTrayStore()
|
||||
|
||||
assertFalse(store.state.syncing)
|
||||
|
||||
store.dispatch(TabsTrayAction.SyncNow)
|
||||
|
||||
store.waitUntilIdle()
|
||||
|
||||
assertTrue(store.state.syncing)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN sync is complete THEN the syncing boolean is updated`() {
|
||||
val store = TabsTrayStore(initialState = TabsTrayState(syncing = true))
|
||||
|
||||
assertTrue(store.state.syncing)
|
||||
|
||||
store.dispatch(TabsTrayAction.SyncCompleted)
|
||||
|
||||
store.waitUntilIdle()
|
||||
|
||||
assertFalse(store.state.syncing)
|
||||
}
|
||||
}
|
18
app/src/test/java/org/mozilla/fenix/tabstray/browser/Tab.kt
Normal file
18
app/src/test/java/org/mozilla/fenix/tabstray/browser/Tab.kt
Normal file
@ -0,0 +1,18 @@
|
||||
/* 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.browser
|
||||
|
||||
import mozilla.components.concept.tabstray.Tab
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
* Helper for writing tests that need a [Tab].
|
||||
*/
|
||||
fun createTab(
|
||||
tabId: String = UUID.randomUUID().toString()
|
||||
) = Tab(
|
||||
tabId,
|
||||
"https://mozilla.org"
|
||||
)
|
@ -4,13 +4,11 @@
|
||||
|
||||
package org.mozilla.fenix.tabstray.browser
|
||||
|
||||
import mozilla.components.concept.tabstray.Tab
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotEquals
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
||||
import java.util.UUID
|
||||
|
||||
@RunWith(FenixRobolectricTestRunner::class)
|
||||
class TabAdapterIdStorageTest {
|
||||
@ -78,8 +76,3 @@ class TabAdapterIdStorageTest {
|
||||
assertNotEquals(id2, id3)
|
||||
}
|
||||
}
|
||||
|
||||
fun createTab() = Tab(
|
||||
UUID.randomUUID().toString(),
|
||||
"https://mozilla.org"
|
||||
)
|
||||
|
Loading…
Reference in New Issue
Block a user