Close #18931: Implement add to collections in interactor
We moved the collection dialog code out from the old fragment, because it had nothing to do with tabs tray, and into the collections package to be re-usable in other parts of the app. In addition, we also make use of it in the new tabs tray's NavigationInteractor.upstream-sync
parent
22e7410e4a
commit
d342aeae48
@ -0,0 +1,135 @@
|
||||
/* 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.collections
|
||||
|
||||
import android.content.Context
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.EditText
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.launch
|
||||
import mozilla.components.browser.state.state.TabSessionState
|
||||
import mozilla.components.feature.tab.collections.TabCollection
|
||||
import mozilla.components.support.ktx.android.view.showKeyboard
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.components.TabCollectionStorage
|
||||
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.
|
||||
*/
|
||||
typealias OnPositiveButtonClick = (collection: TabCollection?) -> List<TabSessionState>
|
||||
|
||||
/**
|
||||
* A lambda that is invoked when a cancel button in a [CollectionsDialog] is clicked.
|
||||
*/
|
||||
typealias OnNegativeButtonClick = () -> Unit
|
||||
|
||||
/**
|
||||
* A data class for creating a dialog to prompt adding/creating a collection. See also [show].
|
||||
*
|
||||
* @property onPositiveButtonClick Invoked when a user clicks on a confirmation button in the dialog.
|
||||
* @property onNegativeButtonClick Invoked when a user clicks on a cancel button in the dialog.
|
||||
*/
|
||||
data class CollectionsDialog(
|
||||
val storage: TabCollectionStorage,
|
||||
val onPositiveButtonClick: OnPositiveButtonClick,
|
||||
val onNegativeButtonClick: OnNegativeButtonClick
|
||||
)
|
||||
|
||||
/**
|
||||
* Create and display a [CollectionsDialog] using [AlertDialog].
|
||||
*/
|
||||
fun CollectionsDialog.show(
|
||||
context: Context
|
||||
) {
|
||||
if (storage.cachedTabCollections.isEmpty()) {
|
||||
showAddNewDialog(context, storage)
|
||||
return
|
||||
}
|
||||
|
||||
val collections = storage.cachedTabCollections.map { it.title }
|
||||
val layout = LayoutInflater.from(context).inflate(R.layout.add_new_collection_dialog, null)
|
||||
val list = layout.findViewById<RecyclerView>(R.id.recycler_view)
|
||||
|
||||
val builder = AlertDialog.Builder(context).setTitle(R.string.tab_tray_select_collection)
|
||||
.setView(layout)
|
||||
.setPositiveButton(android.R.string.ok) { dialog, _ ->
|
||||
val selectedCollection =
|
||||
(list.adapter as CollectionsListAdapter).getSelectedCollection()
|
||||
val collection = storage.cachedTabCollections[selectedCollection]
|
||||
val sessionList = onPositiveButtonClick.invoke(collection)
|
||||
|
||||
MainScope().launch {
|
||||
storage.addTabsToCollection(collection, sessionList)
|
||||
}
|
||||
|
||||
dialog.dismiss()
|
||||
}.setNegativeButton(android.R.string.cancel) { dialog, _ ->
|
||||
onNegativeButtonClick.invoke()
|
||||
|
||||
dialog.cancel()
|
||||
}
|
||||
|
||||
val dialog = builder.create()
|
||||
val collectionNames =
|
||||
arrayOf(context.getString(R.string.tab_tray_add_new_collection)) + collections
|
||||
val collectionsListAdapter = CollectionsListAdapter(collectionNames) {
|
||||
dialog.dismiss()
|
||||
showAddNewDialog(context, storage)
|
||||
}
|
||||
|
||||
list.apply {
|
||||
layoutManager = LinearLayoutManager(context)
|
||||
adapter = collectionsListAdapter
|
||||
}
|
||||
dialog.show()
|
||||
}
|
||||
|
||||
internal fun CollectionsDialog.showAddNewDialog(
|
||||
context: Context,
|
||||
collectionsStorage: TabCollectionStorage
|
||||
) {
|
||||
val layout = LayoutInflater.from(context).inflate(R.layout.name_collection_dialog, null)
|
||||
val collectionNameEditText: EditText = layout.findViewById(R.id.collection_name)
|
||||
|
||||
collectionNameEditText.setText(
|
||||
context.getString(
|
||||
R.string.create_collection_default_name,
|
||||
collectionsStorage.cachedTabCollections.getDefaultCollectionNumber()
|
||||
)
|
||||
)
|
||||
|
||||
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(
|
||||
collectionNameEditText.text.toString(),
|
||||
sessionList
|
||||
)
|
||||
}
|
||||
|
||||
dialog.dismiss()
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel) { dialog, _ ->
|
||||
onNegativeButtonClick.invoke()
|
||||
dialog.cancel()
|
||||
}
|
||||
.create()
|
||||
.show()
|
||||
|
||||
collectionNameEditText.setSelection(0, collectionNameEditText.text.length)
|
||||
collectionNameEditText.showKeyboard()
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
/* 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 mozilla.components.browser.state.selector.findTab
|
||||
import mozilla.components.browser.state.state.TabSessionState
|
||||
import mozilla.components.browser.state.store.BrowserStore
|
||||
import mozilla.components.concept.tabstray.Tab
|
||||
|
||||
/**
|
||||
* Find and extract a list [TabSessionState] from the [BrowserStore] using the IDs from [tabs].
|
||||
*/
|
||||
fun BrowserStore.getTabSessionState(tabs: Collection<Tab>): List<TabSessionState> {
|
||||
return tabs.mapNotNull {
|
||||
state.findTab(it.id)
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
/* 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 io.mockk.mockk
|
||||
import mozilla.components.browser.state.state.BrowserState
|
||||
import mozilla.components.browser.state.state.TabSessionState
|
||||
import mozilla.components.browser.state.store.BrowserStore
|
||||
import mozilla.components.concept.tabstray.Tab
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.tabstray.browser.createTab
|
||||
|
||||
class BrowserStoreKtTest {
|
||||
|
||||
@Test
|
||||
fun `WHEN session is found THEN return it`() {
|
||||
val store = BrowserStore(
|
||||
initialState = BrowserState(
|
||||
listOf(
|
||||
TabSessionState(id = "tab1", mockk(), lastAccess = 3),
|
||||
TabSessionState(id = "tab2", mockk(), lastAccess = 5)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val tabs = listOf<Tab>(
|
||||
createTab("tab1"),
|
||||
createTab("tab2")
|
||||
)
|
||||
|
||||
val result = store.getTabSessionState(tabs)
|
||||
|
||||
assertEquals(3, result[0].lastAccess)
|
||||
assertEquals(5, result[1].lastAccess)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WHEN session is not found THEN ignore it`() {
|
||||
val store = BrowserStore(
|
||||
initialState = BrowserState(
|
||||
listOf(
|
||||
TabSessionState(id = "tab2", mockk(), lastAccess = 5)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val tabs = listOf<Tab>(
|
||||
createTab("tab1"),
|
||||
createTab("tab2")
|
||||
)
|
||||
|
||||
val result = store.getTabSessionState(tabs)
|
||||
|
||||
assertEquals(5, result[0].lastAccess)
|
||||
assertEquals(1, result.size)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue