Issue #19175: Fix SyncTabs list not updating on changes
The main cause for this is that the `LifecycleProvider` needs to be set to `State.RESUMED` to avoid the account manager's internal `ObserverRegistry` from putting the UI observers into the paused state. The rest of the changes is to rely the internal (safe) logic to correctly sync and then update the tabs list.pull/420/head
parent
6cfd6bb63e
commit
f2c599dc1f
@ -0,0 +1,35 @@
|
||||
/* 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.syncedtabs
|
||||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.map
|
||||
import mozilla.components.feature.syncedtabs.view.SyncedTabsView
|
||||
import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged
|
||||
import org.mozilla.fenix.components.AbstractBinding
|
||||
import org.mozilla.fenix.tabstray.TabsTrayState
|
||||
import org.mozilla.fenix.tabstray.TabsTrayStore
|
||||
|
||||
/**
|
||||
* An [AbstractBinding] that invokes the [onSyncNow] callback when the [TabsTrayState.syncing] is
|
||||
* set.
|
||||
*
|
||||
* This binding is useful for connecting with [SyncedTabsView.Listener].
|
||||
*/
|
||||
class SyncButtonBinding(
|
||||
tabsTrayStore: TabsTrayStore,
|
||||
private val onSyncNow: () -> Unit
|
||||
) : AbstractBinding<TabsTrayState>(tabsTrayStore) {
|
||||
override suspend fun onState(flow: Flow<TabsTrayState>) {
|
||||
flow.map { it.syncing }
|
||||
.ifChanged()
|
||||
.collect { syncingNow ->
|
||||
if (syncingNow) {
|
||||
onSyncNow()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
/* 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.syncedtabs
|
||||
|
||||
import mozilla.components.browser.storage.sync.Tab
|
||||
import mozilla.components.feature.syncedtabs.view.SyncedTabsView
|
||||
import org.mozilla.fenix.BrowserDirection
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
import org.mozilla.fenix.components.metrics.Event
|
||||
import org.mozilla.fenix.components.metrics.MetricController
|
||||
import org.mozilla.fenix.tabstray.TabsTrayController
|
||||
import org.mozilla.fenix.tabstray.TabsTrayInteractor
|
||||
|
||||
class SyncedTabsInteractor(
|
||||
private val metrics: MetricController,
|
||||
private val activity: HomeActivity,
|
||||
private val trayInteractor: TabsTrayInteractor,
|
||||
private val controller: TabsTrayController
|
||||
) : SyncedTabsView.Listener {
|
||||
override fun onRefresh() {
|
||||
controller.onSyncStarted()
|
||||
}
|
||||
override fun onTabClicked(tab: Tab) {
|
||||
metrics.track(Event.SyncedTabOpened)
|
||||
activity.openToBrowserAndLoad(
|
||||
searchTermOrURL = tab.active().url,
|
||||
newTab = true,
|
||||
from = BrowserDirection.FromTabTray
|
||||
)
|
||||
trayInteractor.navigateToBrowser()
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
/* 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.syncedtabs
|
||||
|
||||
import mozilla.components.browser.storage.sync.Tab
|
||||
import mozilla.components.feature.syncedtabs.view.SyncedTabsView
|
||||
import org.mozilla.fenix.tabstray.NavigationInteractor
|
||||
|
||||
/**
|
||||
* A wrapper class that handles tab clicks from a Synced Tabs list.
|
||||
*/
|
||||
class TabClickDelegate(
|
||||
private val interactor: NavigationInteractor
|
||||
) : SyncedTabsView.Listener {
|
||||
override fun onTabClicked(tab: Tab) {
|
||||
interactor.onSyncedTabClicked(tab)
|
||||
}
|
||||
|
||||
override fun onRefresh() = Unit
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/* 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.syncedtabs
|
||||
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.TestCoroutineDispatcher
|
||||
import mozilla.components.support.test.libstate.ext.waitUntilIdle
|
||||
import mozilla.components.support.test.rule.MainCoroutineRule
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.tabstray.TabsTrayAction
|
||||
import org.mozilla.fenix.tabstray.TabsTrayStore
|
||||
|
||||
class SyncButtonBindingTest {
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@get:Rule
|
||||
val coroutinesTestRule = MainCoroutineRule(TestCoroutineDispatcher())
|
||||
|
||||
@Test
|
||||
fun `WHEN syncing state is true THEN invoke callback`() {
|
||||
var invoked = false
|
||||
val store = TabsTrayStore()
|
||||
val binding = SyncButtonBinding(store) { invoked = true }
|
||||
|
||||
binding.start()
|
||||
|
||||
store.dispatch(TabsTrayAction.SyncNow)
|
||||
store.waitUntilIdle()
|
||||
|
||||
assertTrue(invoked)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN syncing state is false THEN nothing is invoked`() {
|
||||
var invoked = false
|
||||
val store = TabsTrayStore()
|
||||
val binding = SyncButtonBinding(store) { invoked = true }
|
||||
|
||||
binding.start()
|
||||
|
||||
store.waitUntilIdle()
|
||||
|
||||
assertFalse(invoked)
|
||||
|
||||
store.dispatch(TabsTrayAction.SyncCompleted)
|
||||
store.waitUntilIdle()
|
||||
|
||||
assertFalse(invoked)
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/* 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.syncedtabs
|
||||
|
||||
import io.mockk.Called
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verify
|
||||
import mozilla.components.browser.storage.sync.Tab
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.tabstray.NavigationInteractor
|
||||
|
||||
class TabClickDelegateTest {
|
||||
|
||||
private val interactor = mockk<NavigationInteractor>(relaxed = true)
|
||||
private val tab = mockk<Tab>()
|
||||
|
||||
@Test
|
||||
fun `WHEN tab is clicked THEN invoke the interactor`() {
|
||||
val delegate = TabClickDelegate(interactor)
|
||||
|
||||
delegate.onTabClicked(tab)
|
||||
|
||||
verify { interactor.onSyncedTabClicked(tab) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN refresh is invoked THEN do nothing`() {
|
||||
val delegate = TabClickDelegate(interactor)
|
||||
|
||||
delegate.onRefresh()
|
||||
|
||||
verify { interactor wasNot Called }
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue