mirror of
https://github.com/fork-maintainers/iceraven-browser
synced 2024-11-17 15:26:23 +00:00
[fenix] Closes https://github.com/mozilla-mobile/fenix/issues/19090: Show snackbar on adding to bookmarks from tabs tray (https://github.com/mozilla-mobile/fenix/pull/19807)
This commit is contained in:
parent
3225b7ae30
commit
125e70ae1b
@ -912,7 +912,6 @@ class SmokeTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Ignore("Disabling until re-implemented by #19090")
|
||||
@Test
|
||||
fun verifyExpandedCollectionItemsTest() {
|
||||
val webPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
@ -22,12 +22,10 @@ import org.mozilla.fenix.ext.getDefaultCollectionNumber
|
||||
/**
|
||||
* A lambda that is invoked when a confirmation button in a [CollectionsDialog] is clicked.
|
||||
*
|
||||
* A [TabCollection] of the selected collected is passed to the delegate when confirmed. If null,
|
||||
* then a new collection is created.
|
||||
*
|
||||
* A list of [TabSessionState] is returned that will be put into the collections storage.
|
||||
* The [TabCollection] ID of the selected collection is passed to the delegate when confirmed.
|
||||
* If the selected collection was newly created then [Boolean] is set to true.
|
||||
*/
|
||||
typealias OnPositiveButtonClick = (collection: TabCollection?) -> List<TabSessionState>
|
||||
typealias OnPositiveButtonClick = (id: Long?, isNewCollection: Boolean) -> Unit
|
||||
|
||||
/**
|
||||
* A lambda that is invoked when a cancel button in a [CollectionsDialog] is clicked.
|
||||
@ -42,6 +40,7 @@ typealias OnNegativeButtonClick = () -> Unit
|
||||
*/
|
||||
data class CollectionsDialog(
|
||||
val storage: TabCollectionStorage,
|
||||
val sessionList: List<TabSessionState>,
|
||||
val onPositiveButtonClick: OnPositiveButtonClick,
|
||||
val onNegativeButtonClick: OnNegativeButtonClick
|
||||
)
|
||||
@ -67,10 +66,10 @@ fun CollectionsDialog.show(
|
||||
val selectedCollection =
|
||||
(list.adapter as CollectionsListAdapter).getSelectedCollection()
|
||||
val collection = storage.cachedTabCollections[selectedCollection]
|
||||
val sessionList = onPositiveButtonClick.invoke(collection)
|
||||
|
||||
MainScope().launch {
|
||||
storage.addTabsToCollection(collection, sessionList)
|
||||
val id = storage.addTabsToCollection(collection, sessionList)
|
||||
onPositiveButtonClick.invoke(id, false)
|
||||
}
|
||||
|
||||
dialog.dismiss()
|
||||
@ -112,13 +111,13 @@ internal fun CollectionsDialog.showAddNewDialog(
|
||||
AlertDialog.Builder(context)
|
||||
.setTitle(R.string.tab_tray_add_new_collection)
|
||||
.setView(layout).setPositiveButton(android.R.string.ok) { dialog, _ ->
|
||||
val sessionList = onPositiveButtonClick.invoke(null)
|
||||
|
||||
MainScope().launch {
|
||||
storage.createCollection(
|
||||
val id = storage.createCollection(
|
||||
collectionNameEditText.text.toString(),
|
||||
sessionList
|
||||
)
|
||||
onPositiveButtonClick.invoke(id, true)
|
||||
}
|
||||
|
||||
dialog.dismiss()
|
||||
|
@ -11,16 +11,17 @@ import androidx.lifecycle.asLiveData
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import mozilla.components.browser.state.state.TabSessionState
|
||||
import mozilla.components.feature.tab.collections.Tab
|
||||
import mozilla.components.feature.tab.collections.TabCollection
|
||||
import mozilla.components.feature.tab.collections.TabCollectionStorage
|
||||
import mozilla.components.support.base.observer.Observable
|
||||
import mozilla.components.support.base.observer.ObserverRegistry
|
||||
import org.mozilla.fenix.perf.StrictModeManager
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.toShortUrl
|
||||
import org.mozilla.fenix.home.sessioncontrol.viewholders.CollectionViewHolder
|
||||
import org.mozilla.fenix.perf.StrictModeManager
|
||||
import org.mozilla.fenix.utils.Mockable
|
||||
|
||||
@Mockable
|
||||
@ -59,15 +60,21 @@ class TabCollectionStorage(
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun createCollection(title: String, sessions: List<TabSessionState>) = ioScope.launch {
|
||||
suspend fun createCollection(title: String, sessions: List<TabSessionState>): Long? {
|
||||
return withContext(ioScope.coroutineContext) {
|
||||
val id = collectionStorage.createCollection(title, sessions)
|
||||
notifyObservers { onCollectionCreated(title, sessions, id) }
|
||||
}.join()
|
||||
id
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun addTabsToCollection(tabCollection: TabCollection, sessions: List<TabSessionState>) = ioScope.launch {
|
||||
collectionStorage.addTabsToCollection(tabCollection, sessions)
|
||||
suspend fun addTabsToCollection(tabCollection: TabCollection, sessions: List<TabSessionState>): Long? {
|
||||
return withContext(ioScope.coroutineContext) {
|
||||
val id = collectionStorage.addTabsToCollection(tabCollection, sessions)
|
||||
notifyObservers { onTabsAdded(tabCollection, sessions) }
|
||||
}.join()
|
||||
id
|
||||
}
|
||||
}
|
||||
|
||||
fun getCollections(): LiveData<List<TabCollection>> {
|
||||
return collectionStorage.getCollections().asLiveData()
|
||||
|
@ -100,6 +100,11 @@ class DefaultNavigationInteractor(
|
||||
private val bookmarksUseCase: BookmarksUseCase,
|
||||
private val tabsTrayStore: TabsTrayStore,
|
||||
private val collectionStorage: TabCollectionStorage,
|
||||
private val showCollectionSnackbar: (
|
||||
tabSize: Int,
|
||||
isNewCollection: Boolean,
|
||||
collectionToSelect: Long?
|
||||
) -> Unit,
|
||||
private val accountManager: FxaAccountManager,
|
||||
private val ioDispatcher: CoroutineContext
|
||||
) : NavigationInteractor {
|
||||
@ -170,18 +175,21 @@ class DefaultNavigationInteractor(
|
||||
|
||||
CollectionsDialog(
|
||||
storage = collectionStorage,
|
||||
onPositiveButtonClick = { existingCollection ->
|
||||
sessionList = browserStore.getTabSessionState(tabs),
|
||||
onPositiveButtonClick = { id, isNewCollection ->
|
||||
tabsTrayStore.dispatch(TabsTrayAction.ExitSelectMode)
|
||||
|
||||
// If collection is null, a new one was created.
|
||||
val event = if (existingCollection == null) {
|
||||
val event = if (isNewCollection) {
|
||||
Event.CollectionSaved(browserStore.state.normalTabs.size, tabs.size)
|
||||
} else {
|
||||
Event.CollectionTabsAdded(browserStore.state.normalTabs.size, tabs.size)
|
||||
}
|
||||
metrics.track(event)
|
||||
id?.apply {
|
||||
showCollectionSnackbar(tabs.size, isNewCollection, id)
|
||||
}
|
||||
|
||||
browserStore.getTabSessionState(tabs)
|
||||
metrics.track(event)
|
||||
},
|
||||
onNegativeButtonClick = {
|
||||
tabsTrayStore.dispatch(TabsTrayAction.ExitSelectMode)
|
||||
|
@ -48,8 +48,13 @@ import org.mozilla.fenix.tabstray.browser.SelectionHandleBinding
|
||||
import org.mozilla.fenix.tabstray.browser.SelectionBannerBinding
|
||||
import org.mozilla.fenix.tabstray.browser.SelectionBannerBinding.VisibilityModifier
|
||||
import org.mozilla.fenix.tabstray.ext.showWithTheme
|
||||
import org.mozilla.fenix.tabstray.ext.anchorWithAction
|
||||
import org.mozilla.fenix.utils.allowUndo
|
||||
import kotlin.math.max
|
||||
import org.mozilla.fenix.components.FenixSnackbar
|
||||
import org.mozilla.fenix.tabstray.ext.make
|
||||
import org.mozilla.fenix.tabstray.ext.orDefault
|
||||
import org.mozilla.fenix.tabstray.ext.message
|
||||
|
||||
@Suppress("TooManyFunctions", "LargeClass")
|
||||
class TabsTrayFragment : AppCompatDialogFragment() {
|
||||
@ -114,6 +119,7 @@ class TabsTrayFragment : AppCompatDialogFragment() {
|
||||
dismissTabTrayAndNavigateHome = ::dismissTabsTrayAndNavigateHome,
|
||||
bookmarksUseCase = requireComponents.useCases.bookmarksUseCases,
|
||||
collectionStorage = requireComponents.core.tabCollectionStorage,
|
||||
showCollectionSnackbar = ::showCollectionSnackbar,
|
||||
accountManager = requireComponents.backgroundServices.accountManager,
|
||||
ioDispatcher = Dispatchers.IO
|
||||
)
|
||||
@ -369,6 +375,32 @@ class TabsTrayFragment : AppCompatDialogFragment() {
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
internal fun showCollectionSnackbar(
|
||||
tabSize: Int,
|
||||
isNewCollection: Boolean = false,
|
||||
collectionToSelect: Long?
|
||||
) {
|
||||
val anchor = if (requireComponents.settings.accessibilityServicesEnabled) {
|
||||
null
|
||||
} else {
|
||||
new_tab_button
|
||||
}
|
||||
|
||||
FenixSnackbar
|
||||
.make(requireView())
|
||||
.message(tabSize, isNewCollection)
|
||||
.anchorWithAction(anchor) {
|
||||
findNavController().navigateBlockingForAsyncNavGraph(
|
||||
TabsTrayFragmentDirections.actionGlobalHome(
|
||||
focusOnAddressBar = false,
|
||||
focusOnCollection = collectionToSelect.orDefault()
|
||||
)
|
||||
)
|
||||
dismissTabsTray()
|
||||
}.show()
|
||||
}
|
||||
|
||||
companion object {
|
||||
// Minimum number of list items for which to show the tabs tray as expanded.
|
||||
const val EXPAND_AT_LIST_SIZE = 4
|
||||
|
@ -0,0 +1,49 @@
|
||||
/* 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.ext
|
||||
|
||||
import android.view.View
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.components.FenixSnackbar
|
||||
import org.mozilla.fenix.tabstray.TabsTrayFragment.Companion.ELEVATION
|
||||
|
||||
internal fun FenixSnackbar.message(
|
||||
tabSize: Int,
|
||||
isNewCollection: Boolean = false
|
||||
): FenixSnackbar {
|
||||
val stringRes = when {
|
||||
isNewCollection -> {
|
||||
R.string.create_collection_tabs_saved_new_collection
|
||||
}
|
||||
tabSize > 1 -> {
|
||||
R.string.create_collection_tabs_saved
|
||||
}
|
||||
else -> {
|
||||
R.string.create_collection_tab_saved
|
||||
}
|
||||
}
|
||||
setText(context.getString(stringRes))
|
||||
return this
|
||||
}
|
||||
|
||||
internal inline fun FenixSnackbar.anchorWithAction(
|
||||
anchor: View?,
|
||||
crossinline action: () -> Unit
|
||||
): FenixSnackbar {
|
||||
anchorView = anchor
|
||||
view.elevation = ELEVATION
|
||||
|
||||
setAction(context.getString(R.string.create_collection_view)) {
|
||||
action.invoke()
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
internal fun FenixSnackbar.Companion.make(view: View) = make(
|
||||
duration = LENGTH_LONG,
|
||||
isDisplayedWithBrowserToolbar = true,
|
||||
view = view
|
||||
)
|
10
app/src/main/java/org/mozilla/fenix/tabstray/ext/Long.kt
Normal file
10
app/src/main/java/org/mozilla/fenix/tabstray/ext/Long.kt
Normal file
@ -0,0 +1,10 @@
|
||||
/* 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.ext
|
||||
|
||||
/**
|
||||
* A helper that will return the default value of -1L for the home fragment navigation if Long is null.
|
||||
*/
|
||||
internal fun Long?.orDefault() = this ?: -1L
|
@ -5,6 +5,7 @@
|
||||
package org.mozilla.fenix.collections
|
||||
|
||||
import io.mockk.MockKAnnotations
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.every
|
||||
import io.mockk.impl.annotations.MockK
|
||||
@ -78,6 +79,9 @@ class DefaultCollectionCreationControllerTest {
|
||||
TabListAction.AddMultipleTabsAction(listOf(tab1, tab2))
|
||||
).joinBlocking()
|
||||
|
||||
coEvery { tabCollectionStorage.addTabsToCollection(any(), any()) } returns 1L
|
||||
coEvery { tabCollectionStorage.createCollection(any(), any()) } returns 1L
|
||||
|
||||
val tabs = listOf(
|
||||
Tab("session-1", "", "", ""),
|
||||
Tab("null-session", "", "", "")
|
||||
@ -166,7 +170,6 @@ class DefaultCollectionCreationControllerTest {
|
||||
fun `WHEN selectCollection is called THEN add tabs should be added to collection`() {
|
||||
val tab1 = createTab("https://www.mozilla.org", id = "session-1")
|
||||
val tab2 = createTab("https://www.mozilla.org", id = "session-2")
|
||||
|
||||
browserStore.dispatch(
|
||||
TabListAction.AddMultipleTabsAction(listOf(tab1, tab2))
|
||||
).joinBlocking()
|
||||
@ -175,6 +178,8 @@ class DefaultCollectionCreationControllerTest {
|
||||
Tab("session-1", "", "", "")
|
||||
)
|
||||
val collection = mockk<TabCollection>()
|
||||
coEvery { tabCollectionStorage.addTabsToCollection(any(), any()) } returns 1L
|
||||
coEvery { tabCollectionStorage.createCollection(any(), any()) } returns 1L
|
||||
|
||||
controller.selectCollection(collection, tabs)
|
||||
|
||||
|
@ -54,6 +54,7 @@ class NavigationInteractorTest {
|
||||
private val bookmarksUseCase: BookmarksUseCase = mockk(relaxed = true)
|
||||
private val context: Context = mockk(relaxed = true)
|
||||
private val collectionStorage: TabCollectionStorage = mockk(relaxed = true)
|
||||
private val showCollectionSnackbar: (Int, Boolean, Long?) -> Unit = mockk(relaxed = true)
|
||||
private val accountManager: FxaAccountManager = mockk(relaxed = true)
|
||||
private val activity: HomeActivity = mockk(relaxed = true)
|
||||
|
||||
@ -80,6 +81,7 @@ class NavigationInteractorTest {
|
||||
bookmarksUseCase,
|
||||
tabsTrayStore,
|
||||
collectionStorage,
|
||||
showCollectionSnackbar,
|
||||
accountManager,
|
||||
testDispatcher
|
||||
)
|
||||
@ -247,6 +249,7 @@ class NavigationInteractorTest {
|
||||
bookmarksUseCase,
|
||||
tabsTrayStore,
|
||||
collectionStorage,
|
||||
showCollectionSnackbar,
|
||||
accountManager,
|
||||
coroutineContext
|
||||
)
|
||||
|
@ -0,0 +1,66 @@
|
||||
package org.mozilla.fenix.tabstray.ext
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.verifyOrder
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.components.FenixSnackbar
|
||||
import org.mozilla.fenix.tabstray.TabsTrayFragment.Companion.ELEVATION
|
||||
|
||||
class FenixSnackbarKtTest {
|
||||
|
||||
@Test
|
||||
fun `WHEN message is called with different parameters THEN correct text will be set`() {
|
||||
val mockContext: Context = mockk {
|
||||
every { getString(R.string.create_collection_tabs_saved_new_collection) }
|
||||
.answers { "test1" }
|
||||
every { getString(R.string.create_collection_tabs_saved) }
|
||||
.answers { "test2" }
|
||||
every { getString(R.string.create_collection_tab_saved) }
|
||||
.answers { "test3" }
|
||||
}
|
||||
val snackbar: FenixSnackbar = mockk {
|
||||
every { context }.answers { mockContext }
|
||||
}
|
||||
every { snackbar.setText(any()) }.answers { snackbar }
|
||||
|
||||
snackbar.message(1, true)
|
||||
snackbar.message(2, false)
|
||||
snackbar.message(1, false)
|
||||
|
||||
verifyOrder {
|
||||
snackbar.setText("test1")
|
||||
snackbar.setText("test2")
|
||||
snackbar.setText("test3")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN anchorWithAction is called THEN correct text will be set`() {
|
||||
val mockContext: Context = mockk {
|
||||
every { getString(R.string.create_collection_view) }
|
||||
.answers { "test1" }
|
||||
}
|
||||
val anchor: View = mockk(relaxed = true)
|
||||
val view: View = mockk(relaxed = true)
|
||||
val snackbar: FenixSnackbar = mockk {
|
||||
every { context }.answers { mockContext }
|
||||
}
|
||||
|
||||
every { snackbar.setAnchorView(anchor) }.answers { snackbar }
|
||||
every { snackbar.view }.answers { view }
|
||||
every { snackbar.setAction(any(), any()) }.answers { mockk(relaxed = true) }
|
||||
every { snackbar.anchorView }.answers { anchor }
|
||||
|
||||
snackbar.anchorWithAction(anchor, mockk(relaxed = true))
|
||||
|
||||
verifyOrder {
|
||||
snackbar.anchorView = anchor
|
||||
view.elevation = ELEVATION
|
||||
snackbar.setAction("test1", any())
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
package org.mozilla.fenix.tabstray.ext
|
||||
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
class LongKtTest {
|
||||
|
||||
@Test
|
||||
fun `WHEN value is null THEN default is returned`() {
|
||||
val value: Long? = null
|
||||
|
||||
assertEquals(value.orDefault(), -1L)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN value is not null THEN value is returned`() {
|
||||
val value: Long? = 100L
|
||||
|
||||
assertEquals(value.orDefault(), 100L)
|
||||
}
|
||||
}
|
@ -3,5 +3,5 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
object AndroidComponents {
|
||||
const val VERSION = "91.0.20210603145049"
|
||||
const val VERSION = "91.0.20210604143054"
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user