From 729acdba39f44d36da306f9fe8bd8ce91aca712b Mon Sep 17 00:00:00 2001 From: Jonathan Almeida Date: Tue, 6 Apr 2021 18:39:54 -0400 Subject: [PATCH] No issue: Observe only normal tabs when updating counter --- .../fenix/tabstray/TabCounterBinding.kt | 44 +++++++++++++++ .../fenix/tabstray/TabsTrayFragment.kt | 20 +++---- .../fenix/tabstray/TabCounterBindingTest.kt | 53 +++++++++++++++++++ 3 files changed, 108 insertions(+), 9 deletions(-) create mode 100644 app/src/main/java/org/mozilla/fenix/tabstray/TabCounterBinding.kt create mode 100644 app/src/test/java/org/mozilla/fenix/tabstray/TabCounterBindingTest.kt diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/TabCounterBinding.kt b/app/src/main/java/org/mozilla/fenix/tabstray/TabCounterBinding.kt new file mode 100644 index 000000000..e6b833c7d --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/tabstray/TabCounterBinding.kt @@ -0,0 +1,44 @@ +/* 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.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.cancel +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.map +import mozilla.components.browser.state.selector.normalTabs +import mozilla.components.browser.state.state.BrowserState +import mozilla.components.browser.state.store.BrowserStore +import mozilla.components.lib.state.ext.flowScoped +import mozilla.components.support.base.feature.LifecycleAwareFeature +import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged +import mozilla.components.ui.tabcounter.TabCounter + +/** + * Updates the tab counter to the size of [BrowserState.normalTabs]. + */ +class TabCounterBinding( + private val store: BrowserStore, + private val counter: TabCounter +) : LifecycleAwareFeature { + + private var scope: CoroutineScope? = null + + @OptIn(ExperimentalCoroutinesApi::class) + override fun start() { + scope = store.flowScoped { flow -> + flow.map { it.normalTabs } + .ifChanged() + .collect { + counter.setCount(it.size) + } + } + } + + override fun stop() { + scope?.cancel() + } +} diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayFragment.kt b/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayFragment.kt index ad942ace9..e0c3a77a1 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayFragment.kt @@ -19,16 +19,14 @@ 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.view.* +import kotlinx.android.synthetic.main.tabs_tray_tab_counter2.* import kotlinx.coroutines.ExperimentalCoroutinesApi -import mozilla.components.browser.state.selector.normalTabs -import mozilla.components.lib.state.ext.consumeFrom import mozilla.components.support.base.feature.ViewBoundFeatureWrapper -import mozilla.components.ui.tabcounter.TabCounter +import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.NavGraphDirections import org.mozilla.fenix.R import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.ext.components -import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.components.StoreProvider import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.home.HomeScreenViewModel @@ -52,6 +50,7 @@ class TabsTrayFragment : AppCompatDialogFragment(), TabsTrayInteractor { private lateinit var behavior: BottomSheetBehavior private val tabLayoutMediator = ViewBoundFeatureWrapper() + private val tabCounterBinding = ViewBoundFeatureWrapper() private val selectTabUseCase by lazy { SelectTabUseCaseWrapper( @@ -140,11 +139,14 @@ class TabsTrayFragment : AppCompatDialogFragment(), TabsTrayInteractor { view = view ) - consumeFrom(requireComponents.core.store) { - view.findViewById(R.id.tab_counter)?.apply { - setCount(requireComponents.core.store.state.normalTabs.size) - } - } + tabCounterBinding.set( + feature = TabCounterBinding( + store = requireComponents.core.store, + counter = tab_counter + ), + owner = this, + view = view + ) } override fun setCurrentTrayPosition(position: Int, smoothScroll: Boolean) { diff --git a/app/src/test/java/org/mozilla/fenix/tabstray/TabCounterBindingTest.kt b/app/src/test/java/org/mozilla/fenix/tabstray/TabCounterBindingTest.kt new file mode 100644 index 000000000..faa3e42ac --- /dev/null +++ b/app/src/test/java/org/mozilla/fenix/tabstray/TabCounterBindingTest.kt @@ -0,0 +1,53 @@ +/* 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 kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestCoroutineDispatcher +import mozilla.components.browser.state.action.TabListAction +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 mozilla.components.ui.tabcounter.TabCounter +import org.junit.Rule +import org.junit.Test + +class TabCounterBindingTest { + + @OptIn(ExperimentalCoroutinesApi::class) + @get:Rule + val coroutinesTestRule = MainCoroutineRule(TestCoroutineDispatcher()) + + @Test + fun `WHEN normalTabs changes THEN update counter`() { + val store = BrowserStore() + val counter = mockk(relaxed = true) + val binding = TabCounterBinding(store, counter) + + binding.start() + + store.dispatch(TabListAction.AddTabAction(createTab("https://mozilla.org"))) + + store.waitUntilIdle() + + verify { counter.setCount(1) } + } + + @Test + fun `WHEN feature starts THEN update counter`() { + val store = BrowserStore() + val counter = mockk(relaxed = true) + val binding = TabCounterBinding(store, counter) + + binding.start() + + store.waitUntilIdle() + + verify { counter.setCount(0) } + } +}