Commit b54d4d1d authored by Colin Lee's avatar Colin Lee
Browse files

Closes #1312, #1236, #1237, #1238, #1239: Creating, Editing, and Deleting...

Closes #1312, #1236, #1237, #1238, #1239: Creating, Editing, and Deleting Bookmarks and Bookmark Folders
parent 8f6dca99
......@@ -17,6 +17,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- #1195 - Adds telemetry for quick action sheet
- #627 - Sets engine preferred color scheme based on light/dark theme
- #904 - Added tab counter in browser toolbar
- #1312 - Added the ability to edit bookmarks
- #1236 - Added the ability to create bookmark folders
- #1237 - Added the ability to delete bookmark folders
- #1238 - Added the ability to edit bookmark folders
- #1239 - Added the ability to move bookmark folders
### Changed
- #1429 - Updated site permissions ui for MVP
### Removed
\ No newline at end of file
......@@ -251,6 +251,11 @@ dependencies {
implementation Deps.rxAndroid
implementation Deps.rxKotlin
implementation Deps.rxBindings
implementation Deps.autodispose
implementation Deps.autodispose_android
implementation Deps.autodispose_android_aac
implementation Deps.anko_commons
implementation Deps.anko_sdk
implementation Deps.anko_constraintlayout
......@@ -316,6 +321,7 @@ dependencies {
implementation Deps.androidx_navigation_fragment
implementation Deps.androidx_navigation_ui
implementation Deps.androidx_recyclerview
implementation Deps.androidx_lifecycle_viewmodel_ktx
implementation Deps.autodispose
......
......@@ -27,6 +27,7 @@ import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.home.HomeFragmentDirections
import org.mozilla.fenix.library.bookmarks.BookmarkFragmentDirections
import org.mozilla.fenix.library.bookmarks.selectfolder.SelectBookmarkFolderFragmentDirections
import org.mozilla.fenix.library.history.HistoryFragmentDirections
import org.mozilla.fenix.search.SearchFragmentDirections
import org.mozilla.fenix.settings.SettingsFragmentDirections
......@@ -155,6 +156,8 @@ open class HomeActivity : AppCompatActivity() {
SettingsFragmentDirections.actionSettingsFragmentToBrowserFragment(sessionId)
BrowserDirection.FromBookmarks ->
BookmarkFragmentDirections.actionBookmarkFragmentToBrowserFragment(sessionId)
BrowserDirection.FromBookmarksFolderSelect ->
SelectBookmarkFolderFragmentDirections.actionBookmarkSelectFolderFragmentToBrowserFragment(sessionId)
BrowserDirection.FromHistory ->
HistoryFragmentDirections.actionHistoryFragmentToBrowserFragment(sessionId)
}
......@@ -193,5 +196,5 @@ open class HomeActivity : AppCompatActivity() {
}
enum class BrowserDirection {
FromGlobal, FromHome, FromSearch, FromSettings, FromBookmarks, FromHistory
FromGlobal, FromHome, FromSearch, FromSettings, FromBookmarks, FromBookmarksFolderSelect, FromHistory
}
......@@ -48,6 +48,7 @@ import org.mozilla.fenix.DefaultThemeManager
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.IntentReceiverActivity
import org.mozilla.fenix.R
import org.mozilla.fenix.components.FenixSnackbar
import org.mozilla.fenix.components.FindInPageIntegration
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.components.metrics.Event.BrowserMenuItemTapped.Item
......@@ -67,9 +68,9 @@ import org.mozilla.fenix.mvi.ActionBusFactory
import org.mozilla.fenix.mvi.getAutoDisposeObservable
import org.mozilla.fenix.quickactionsheet.QuickActionAction
import org.mozilla.fenix.quickactionsheet.QuickActionComponent
import org.mozilla.fenix.settings.quicksettings.QuickSettingsSheetDialogFragment
import org.mozilla.fenix.utils.ItsNotBrokenSnack
import org.mozilla.fenix.utils.Settings
import org.mozilla.fenix.settings.quicksettings.QuickSettingsSheetDialogFragment
@SuppressWarnings("TooManyFunctions", "LargeClass")
class BrowserFragment : Fragment(), BackHandler {
......@@ -323,24 +324,26 @@ class BrowserFragment : Fragment(), BackHandler {
requireComponents.analytics.metrics.track(Event.QuickActionSheetBookmarkTapped)
val session = requireComponents.core.sessionManager.selectedSession
CoroutineScope(IO).launch {
requireComponents.core.bookmarksStorage
val guid = requireComponents.core.bookmarksStorage
.addItem(BookmarkRoot.Mobile.id, session!!.url, session.title, null)
launch(Main) {
val rootView =
context?.asActivity()?.window?.decorView?.findViewById<View>(android.R.id.content)
rootView?.let { view ->
Snackbar.make(
view,
getString(R.string.bookmark_created_snackbar),
Snackbar.LENGTH_LONG
)
.setAction(getString(R.string.edit_bookmark_snackbar_action)) {
ItsNotBrokenSnack(
context!!
).showSnackbar(issueNumber = "90")
}
.show()
}
context?.asActivity()?.window?.decorView
?.findViewById<View>(android.R.id.content)?.let { view ->
FenixSnackbar.make(
view as ViewGroup,
Snackbar.LENGTH_LONG
)
.setAction(getString(R.string.edit_bookmark_snackbar_action)) {
Navigation.findNavController(requireActivity(), R.id.container)
.navigate(
BrowserFragmentDirections
.actionBrowserFragmentToBookmarkEditFragment(
guid
)
)
}
.setText(getString(R.string.bookmark_created_snackbar))
}!!.show()
}
}
}
......
/* 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.ext
import android.content.Context
import android.util.TypedValue
fun Int.getColorFromAttr(context: Context): Int {
val typedValue = TypedValue()
val typedArray = context.obtainStyledAttributes(typedValue.data, intArrayOf(this))
val color = typedArray.getColor(0, 0)
typedArray.recycle()
return color
}
......@@ -15,6 +15,7 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import mozilla.appservices.places.BookmarkRoot
import mozilla.components.browser.icons.BrowserIcons
import mozilla.components.browser.icons.IconRequest
import mozilla.components.browser.menu.BrowserMenu
......@@ -22,17 +23,24 @@ import mozilla.components.concept.storage.BookmarkNode
import mozilla.components.concept.storage.BookmarkNodeType
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.increaseTapArea
import kotlin.coroutines.CoroutineContext
class BookmarkAdapter(val actionEmitter: Observer<BookmarkAction>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
class BookmarkAdapter(val emptyView: View, val actionEmitter: Observer<BookmarkAction>) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var tree: List<BookmarkNode> = listOf()
private var mode: BookmarkState.Mode = BookmarkState.Mode.Normal
private var isFirstRun = true
lateinit var job: Job
fun updateData(tree: BookmarkNode?, mode: BookmarkState.Mode) {
this.tree = tree?.children?.filterNotNull() ?: listOf()
isFirstRun = if (isFirstRun) false else {
emptyView.visibility = if (this.tree.isEmpty()) View.VISIBLE else View.GONE
false
}
this.mode = mode
notifyDataSetChanged()
}
......@@ -78,36 +86,10 @@ class BookmarkAdapter(val actionEmitter: Observer<BookmarkAction>) : RecyclerVie
@SuppressWarnings("ComplexMethod")
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
val bookmarkItemMenu = BookmarkItemMenu(holder.itemView.context) {
when (it) {
is BookmarkItemMenu.Item.Edit -> {
actionEmitter.onNext(BookmarkAction.Edit(tree[position]))
}
is BookmarkItemMenu.Item.Select -> {
actionEmitter.onNext(BookmarkAction.Select(tree[position]))
}
is BookmarkItemMenu.Item.Copy -> {
actionEmitter.onNext(BookmarkAction.Copy(tree[position]))
}
is BookmarkItemMenu.Item.Share -> {
actionEmitter.onNext(BookmarkAction.Share(tree[position]))
}
is BookmarkItemMenu.Item.OpenInNewTab -> {
actionEmitter.onNext(BookmarkAction.OpenInNewTab(tree[position]))
}
is BookmarkItemMenu.Item.OpenInPrivateTab -> {
actionEmitter.onNext(BookmarkAction.OpenInPrivateTab(tree[position]))
}
is BookmarkItemMenu.Item.Delete -> {
actionEmitter.onNext(BookmarkAction.Delete(tree[position]))
}
}
}
when (holder) {
is BookmarkAdapter.BookmarkItemViewHolder -> holder.bind(tree[position], bookmarkItemMenu, mode)
is BookmarkAdapter.BookmarkFolderViewHolder -> holder.bind(tree[position], bookmarkItemMenu, mode)
is BookmarkAdapter.BookmarkSeparatorViewHolder -> holder.bind(bookmarkItemMenu)
is BookmarkAdapter.BookmarkItemViewHolder -> holder.bind(tree[position], mode)
is BookmarkAdapter.BookmarkFolderViewHolder -> holder.bind(tree[position], mode)
is BookmarkAdapter.BookmarkSeparatorViewHolder -> holder.bind(tree[position])
}
}
......@@ -133,12 +115,39 @@ class BookmarkAdapter(val actionEmitter: Observer<BookmarkAction>) : RecyclerVie
bookmark_layout.isClickable = true
}
fun bind(item: BookmarkNode, bookmarkItemMenu: BookmarkItemMenu, mode: BookmarkState.Mode) {
fun bind(item: BookmarkNode, mode: BookmarkState.Mode) {
this.item = item
this.mode = mode
val bookmarkItemMenu = BookmarkItemMenu(containerView!!.context, item) {
when (it) {
is BookmarkItemMenu.Item.Edit -> {
actionEmitter.onNext(BookmarkAction.Edit(item))
}
is BookmarkItemMenu.Item.Select -> {
actionEmitter.onNext(BookmarkAction.Select(item))
}
is BookmarkItemMenu.Item.Copy -> {
actionEmitter.onNext(BookmarkAction.Copy(item))
}
is BookmarkItemMenu.Item.Share -> {
actionEmitter.onNext(BookmarkAction.Share(item))
}
is BookmarkItemMenu.Item.OpenInNewTab -> {
actionEmitter.onNext(BookmarkAction.OpenInNewTab(item))
}
is BookmarkItemMenu.Item.OpenInPrivateTab -> {
actionEmitter.onNext(BookmarkAction.OpenInPrivateTab(item))
}
is BookmarkItemMenu.Item.Delete -> {
actionEmitter.onNext(BookmarkAction.Delete(item))
}
}
}
bookmark_overflow.increaseTapArea(bookmarkOverflowExtraDips)
bookmark_overflow.setOnClickListener {
bookmarkItemMenu.menuBuilder.build(containerView!!.context).show(
bookmarkItemMenu.menuBuilder.build(containerView.context).show(
anchor = it,
orientation = BrowserMenu.Orientation.DOWN
)
......@@ -195,12 +204,34 @@ class BookmarkAdapter(val actionEmitter: Observer<BookmarkAction>) : RecyclerVie
bookmark_layout.isClickable = true
}
fun bind(folder: BookmarkNode, bookmarkItemMenu: BookmarkItemMenu, mode: BookmarkState.Mode) {
bookmark_overflow.setOnClickListener {
bookmarkItemMenu.menuBuilder.build(containerView!!.context).show(
anchor = it,
orientation = BrowserMenu.Orientation.DOWN
)
fun bind(folder: BookmarkNode, mode: BookmarkState.Mode) {
val bookmarkItemMenu = BookmarkItemMenu(containerView!!.context, folder) {
when (it) {
is BookmarkItemMenu.Item.Edit -> {
actionEmitter.onNext(BookmarkAction.Edit(folder))
}
is BookmarkItemMenu.Item.Select -> {
actionEmitter.onNext(BookmarkAction.Select(folder))
}
is BookmarkItemMenu.Item.Copy -> {
actionEmitter.onNext(BookmarkAction.Copy(folder))
}
is BookmarkItemMenu.Item.Delete -> {
actionEmitter.onNext(BookmarkAction.Delete(folder))
}
}
}
if (enumValues<BookmarkRoot>().all { it.id != folder.guid }) {
bookmark_overflow.increaseTapArea(bookmarkOverflowExtraDips)
bookmark_overflow.setOnClickListener {
bookmarkItemMenu.menuBuilder.build(containerView.context).show(
anchor = it,
orientation = BrowserMenu.Orientation.DOWN
)
}
} else {
bookmark_overflow.visibility = View.GONE
}
bookmark_title?.text = folder.title
bookmark_layout.setOnClickListener {
......@@ -222,14 +253,23 @@ class BookmarkAdapter(val actionEmitter: Observer<BookmarkAction>) : RecyclerVie
init {
bookmark_favicon.visibility = View.GONE
bookmark_title.visibility = View.GONE
bookmark_overflow.increaseTapArea(bookmarkOverflowExtraDips)
bookmark_overflow.visibility = View.VISIBLE
bookmark_separator.visibility = View.VISIBLE
bookmark_layout.isClickable = false
}
fun bind(bookmarkItemMenu: BookmarkItemMenu) {
fun bind(separator: BookmarkNode) {
val bookmarkItemMenu = BookmarkItemMenu(containerView!!.context, separator) {
when (it) {
is BookmarkItemMenu.Item.Delete -> {
actionEmitter.onNext(BookmarkAction.Delete(separator))
}
}
}
bookmark_overflow.setOnClickListener {
bookmarkItemMenu.menuBuilder.build(containerView!!.context).show(
bookmarkItemMenu.menuBuilder.build(containerView.context).show(
anchor = it,
orientation = BrowserMenu.Orientation.DOWN
)
......@@ -241,6 +281,10 @@ class BookmarkAdapter(val actionEmitter: Observer<BookmarkAction>) : RecyclerVie
}
}
companion object {
private const val bookmarkOverflowExtraDips = 8
}
enum class ViewType {
ITEM, FOLDER, SEPARATOR
}
......
......@@ -24,6 +24,9 @@ import kotlinx.coroutines.launch
import mozilla.appservices.places.BookmarkRoot
import mozilla.components.concept.storage.BookmarkNode
import mozilla.components.concept.storage.BookmarkNodeType
import mozilla.components.concept.sync.AccountObserver
import mozilla.components.concept.sync.OAuthAccount
import mozilla.components.concept.sync.Profile
import mozilla.components.support.base.feature.BackHandler
import org.mozilla.fenix.BrowserDirection
import org.mozilla.fenix.BrowsingModeManager
......@@ -37,10 +40,12 @@ import org.mozilla.fenix.mvi.getManagedEmitter
import org.mozilla.fenix.utils.ItsNotBrokenSnack
import kotlin.coroutines.CoroutineContext
class BookmarkFragment : Fragment(), CoroutineScope, BackHandler {
@SuppressWarnings("TooManyFunctions")
class BookmarkFragment : Fragment(), CoroutineScope, BackHandler, AccountObserver {
private lateinit var job: Job
private lateinit var bookmarkComponent: BookmarkComponent
private lateinit var signInComponent: SignInComponent
private lateinit var currentRoot: BookmarkNode
override val coroutineContext: CoroutineContext
......@@ -49,6 +54,7 @@ class BookmarkFragment : Fragment(), CoroutineScope, BackHandler {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_bookmark, container, false)
bookmarkComponent = BookmarkComponent(view.bookmark_layout, ActionBusFactory.get(this))
signInComponent = SignInComponent(view.bookmark_layout, ActionBusFactory.get(this))
return view
}
......@@ -61,6 +67,14 @@ class BookmarkFragment : Fragment(), CoroutineScope, BackHandler {
override fun onResume() {
super.onResume()
(activity as AppCompatActivity).supportActionBar?.show()
checkIfSignedIn()
}
private fun checkIfSignedIn() {
val accountManager = requireComponents.backgroundServices.accountManager
accountManager.register(this, owner = this)
accountManager.authenticatedAccount()?.let { getManagedEmitter<SignInChange>().onNext(SignInChange.SignedIn) }
?: getManagedEmitter<SignInChange>().onNext(SignInChange.SignedOut)
}
override fun onDestroy() {
......@@ -94,7 +108,11 @@ class BookmarkFragment : Fragment(), CoroutineScope, BackHandler {
Navigation.findNavController(requireActivity(), R.id.container).popBackStack()
}
is BookmarkAction.Edit -> {
ItsNotBrokenSnack(context!!).showSnackbar(issueNumber = "1238")
Navigation.findNavController(requireActivity(), R.id.container)
.navigate(
BookmarkFragmentDirections
.actionBookmarkFragmentToBookmarkEditFragment(it.item.guid)
)
}
is BookmarkAction.Select -> {
ItsNotBrokenSnack(context!!).showSnackbar(issueNumber = "1239")
......@@ -131,6 +149,16 @@ class BookmarkFragment : Fragment(), CoroutineScope, BackHandler {
}
}
}
getAutoDisposeObservable<SignInAction>()
.subscribe {
when (it) {
is SignInAction.ClickedSignIn -> {
requireComponents.services.accountsAuthFeature.beginAuthentication()
(activity as HomeActivity).openToBrowser(null, from = BrowserDirection.FromBookmarks)
}
}
}
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
......@@ -157,10 +185,25 @@ class BookmarkFragment : Fragment(), CoroutineScope, BackHandler {
currentRoot = requireComponents.core.bookmarksStorage.getTree(currentGuid) as BookmarkNode
launch(Main) {
if (currentGuid != BookmarkRoot.Root.id) (activity as HomeActivity).title = currentRoot.title
getManagedEmitter<BookmarkChange>().onNext(BookmarkChange.Change(currentRoot))
}
}
}
override fun onBackPressed(): Boolean = (bookmarkComponent.uiView as BookmarkUIView).onBackPressed()
override fun onAuthenticated(account: OAuthAccount) {
getManagedEmitter<SignInChange>().onNext(SignInChange.SignedIn)
}
override fun onError(error: Exception) {
}
override fun onLoggedOut() {
getManagedEmitter<SignInChange>().onNext(SignInChange.SignedOut)
}
override fun onProfileUpdated(profile: Profile) {
}
}
......@@ -7,11 +7,14 @@ package org.mozilla.fenix.library.bookmarks
import android.content.Context
import mozilla.components.browser.menu.BrowserMenuBuilder
import mozilla.components.browser.menu.item.SimpleBrowserMenuItem
import mozilla.components.concept.storage.BookmarkNode
import mozilla.components.concept.storage.BookmarkNodeType
import org.mozilla.fenix.DefaultThemeManager
import org.mozilla.fenix.R
class BookmarkItemMenu(
private val context: Context,
private val item: BookmarkNode,
private val onItemTapped: (BookmarkItemMenu.Item) -> Unit = {}
) {
......@@ -29,24 +32,36 @@ class BookmarkItemMenu(
private val menuItems by lazy {
listOf(
SimpleBrowserMenuItem(context.getString(R.string.bookmark_menu_edit_button)) {
onItemTapped.invoke(BookmarkItemMenu.Item.Edit)
},
SimpleBrowserMenuItem(context.getString(R.string.bookmark_menu_select_button)) {
onItemTapped.invoke(BookmarkItemMenu.Item.Select)
},
SimpleBrowserMenuItem(context.getString(R.string.bookmark_menu_copy_button)) {
onItemTapped.invoke(BookmarkItemMenu.Item.Copy)
},
SimpleBrowserMenuItem(context.getString(R.string.bookmark_menu_share_button)) {
onItemTapped.invoke(BookmarkItemMenu.Item.Share)
},
SimpleBrowserMenuItem(context.getString(R.string.bookmark_menu_open_in_new_tab_button)) {
onItemTapped.invoke(BookmarkItemMenu.Item.OpenInNewTab)
},
SimpleBrowserMenuItem(context.getString(R.string.bookmark_menu_open_in_private_tab_button)) {
onItemTapped.invoke(BookmarkItemMenu.Item.OpenInPrivateTab)
},
if (item.type in listOf(BookmarkNodeType.ITEM, BookmarkNodeType.FOLDER)) {
SimpleBrowserMenuItem(context.getString(R.string.bookmark_menu_edit_button)) {
onItemTapped.invoke(BookmarkItemMenu.Item.Edit)
}
} else null,
if (item.type in listOf(BookmarkNodeType.ITEM, BookmarkNodeType.FOLDER)) {
SimpleBrowserMenuItem(context.getString(R.string.bookmark_menu_select_button)) {
onItemTapped.invoke(BookmarkItemMenu.Item.Select)
}
} else null,
if (item.type in listOf(BookmarkNodeType.ITEM, BookmarkNodeType.FOLDER)) {
SimpleBrowserMenuItem(context.getString(R.string.bookmark_menu_copy_button)) {
onItemTapped.invoke(BookmarkItemMenu.Item.Copy)
}
} else null,
if (item.type == BookmarkNodeType.ITEM) {
SimpleBrowserMenuItem(context.getString(R.string.bookmark_menu_share_button)) {
onItemTapped.invoke(BookmarkItemMenu.Item.Share)
}
} else null,
if (item.type == BookmarkNodeType.ITEM) {
SimpleBrowserMenuItem(context.getString(R.string.bookmark_menu_open_in_new_tab_button)) {
onItemTapped.invoke(BookmarkItemMenu.Item.OpenInNewTab)
}
} else null,
if (item.type == BookmarkNodeType.ITEM) {
SimpleBrowserMenuItem(context.getString(R.string.bookmark_menu_open_in_private_tab_button)) {
onItemTapped.invoke(BookmarkItemMenu.Item.OpenInPrivateTab)
}
} else null,
SimpleBrowserMenuItem(
context.getString(R.string.bookmark_menu_delete_button),
textColorResource = DefaultThemeManager.resolveAttribute(
......@@ -56,6 +71,6 @@ class BookmarkItemMenu(
) {
onItemTapped.invoke(BookmarkItemMenu.Item.Delete)
}
)
).filterNotNull()
}
}
......@@ -6,11 +6,11 @@ package org.mozilla.fenix.library.bookmarks
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import android.widget.LinearLayout
import io.reactivex.Observable
import io.reactivex.Observer
import io.reactivex.functions.Consumer
import kotlinx.android.synthetic.main.component_bookmark.view.*
import mozilla.appservices.places.BookmarkRoot
import mozilla.components.support.base.feature.BackHandler
import org.mozilla.fenix.R
......@@ -29,16 +29,15 @@ class BookmarkUIView(
var canGoBack = false
override val view: RecyclerView = LayoutInflater.from(container.context)
.inflate(R.layout.component_bookmark, container, true)
.findViewById(R.id.bookmark_list)
override val view: LinearLayout = LayoutInflater.from(container.context)
.inflate(R.layout.component_bookmark, container, true) as LinearLayout
private val bookmarkAdapter = BookmarkAdapter(actionEmitter)
private val bookmarkAdapter: BookmarkAdapter
init {
view.apply {
view.bookmark_list.apply {
bookmarkAdapter = BookmarkAdapter(view.bookmarks_empty_view, actionEmitter)
adapter = bookmarkAdapter
layoutManager = LinearLayoutManager(container.context)
}
}
......
/* 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.library.bookmarks
import androidx.lifecycle.ViewModel
import mozilla.components.concept.storage.BookmarkNode
class BookmarksSharedViewModel : ViewModel() {
var selectedFolder: BookmarkNode? = null
}
/* 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.library.bookmarks
import android.view.ViewGroup
import org.mozilla.fenix.mvi.Action
import org.mozilla.fenix.mvi.ActionBusFactory
import org.mozilla.fenix.mvi.Change
import org.mozilla.fenix.mvi.Reducer
import org.mozilla.fenix.mvi.UIComponent
import org.mozilla.fenix.mvi.UIView
import org.mozilla.fenix.mvi.ViewState
class SignInComponent(
private val container: ViewGroup,
bus: ActionBusFactory,