Unverified Commit 56b21426 authored by Gabriel's avatar Gabriel Committed by GitHub
Browse files

For #5574 - Migrate SessionControl to LibState (#6651)

* For #5574 - Part 1: Port TabAction.SaveTabGroup to TabSessionInteractor and SessionControlController. (#6651)

- Introduces the TabSessionInteractor, SessionControlInteractor and SessionControlController classes.
- Removes the TabAction.SaveTabGroup.

* For #5574 - Part 2: Port TabAction.PrivateBrowsingLearnMore to TabSessionInteractor and SessionControlController (#6651)

* For #5574 - Part 3: Port TabAction.ShareTabs to TabSessionInteractor and SessionControlController (#6651)

* For #5574 - Part 4: Remove unused TabAction.Share and TabItemMenu (#6651)

In #2205, the tab overflow button was removed which would have shown the
TabItemMenu when clicked. So, we can remove TabItemMenu since it is not
used and as a result, we can also remove TabAction.Share since there are
no consumers.

* For #5574 - Part 5: Port TabAction.PlayMedia and TabAction.PauseMedia to TabSessionInteractor and SessionControlController (#6651)

* For #5574 - Part 6: Port TabAction.Select to TabSessionInteractor and SessionControlController (#6651)

* For #5574 - Part 7: Port Onboarding.Finish to OnboardingInteractor and SessionControlController (#6651)

* For #5574 - Part 8: Port TabAction.Close and TabAction.CloseAll to TabSessionInteractor and SessionControlController (#6651)

- Removes TabAction

* For #5574 - Part 9: Port CollectionAction.Delete to CollectionInteractor and SessionControlController (#6651)

* For #5574 - Part 10: Port CollectionAction.ShareTabs to CollectionInteractor and SessionControlController (#6651)

* For #5574 - Part 11: Port CollectionAction.AddTab and CollectionAction.Rename to CollectionInteractor and SessionControlController (#6651)

* For #5574 - Part 12: Port CollectionAction.RemoveTab to CollectionInteractor and SessionControlController (#6651)

* For #5574 - Part 13: Port CollectionAction.OpenTab to CollectionInteractor and SessionControlController (#6651)

* For #5574 - Part 14: Port CollectionAction.CloseTabs to CollectionInteractor and SessionControlController (#6651)

* For #5574 - Part 15: Introduce a HomeFragmentStore (#6651)

- We will hook up the HomeFragmentStore in later parts.
- Removes List<Tab>.toSessionBundle(context: Context) since it is unused.

* For #5574 - Part 16: Port CollectionAction.Collapse and CollectionAction.Expand to CollectionInteractor and SessionControlController (#6651)

- We assume the store is hooked up to the SessionControlController in this part,
but this work will be done in a later part.
- Removes CollectionAction.

* For #5574 - Part 20: Remove the architecture module. (#6651)

* For #5574 - Part 17:  Remove duplicate subscribeToTabCollections in BrowserFragment.kt (#6651)

There is a duplicate call of subscribeToTabCollections() in both HomeFragment and BrowserFragment.
In this patch, we remove the call in BrowserFragment to avoid passing the HomeFragmentStore to
BrowserFragment in order to dispatch the CollectionsChange event.

* For #5574 - Part 18: Delete SessionControlComponent and fix TabCollection and Tab imports (#6651)

* For #5574 - Part 19: Use the new HomeFragmentStore in the HomeFragment (#6651)

- Renames SessionControlUIView to SessionControlView

* For #5574 - Part 21: Fix white screen on home fragment (#6651)

* For #5574 - Part 22: Fix formatting in SessionControlInteractor and replace See with @see in SessionControlController (#6651)

* For #5574 - Part 23: Move to metrics.track call to the beginning of handleCollectionRemoveTab (#6651)

This ensures that the metrics.track will be called immediately before the tab is removed from the collection.

* For #5574 - Part 24: Use the sessionManager getter in SessionControlController (#6651)

* For #5574 - Part 25: Use mapNotNull in List<Tab>.toSessionBundle (#6651)

* For #5574 - Part 26: Simplify closeTab and closeAllTabs functions by assigning a deletionJob constant (#6651)

* For #5574 - Part 27: Replace listOf() with emptyList() in removeAllTabsWithUndo (#6651)

* For #5574 - Part 28: Replace the Context parameter with the HomeActivity in SessionControlController (#6651)

* For #5574 - Part 29: Add test for HomeFragmentStore, DefaultSessionControlController and SessionControlInteractor (#6651)

* For #5574 - Removes running CI against the architecture debug build varient
parent a07a77c0
......@@ -363,8 +363,6 @@ androidExtensions {
}
dependencies {
implementation project(':architecture')
geckoNightlyImplementation Deps.mozilla_browser_engine_gecko_nightly
geckoBetaImplementation Deps.mozilla_browser_engine_gecko_beta
......@@ -437,6 +435,7 @@ dependencies {
implementation Deps.mozilla_service_glean
implementation Deps.mozilla_service_experiments
implementation Deps.mozilla_support_base
implementation Deps.mozilla_support_ktx
implementation Deps.mozilla_support_rustlog
implementation Deps.mozilla_support_utils
......
/* 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
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import org.mozilla.fenix.mvi.Change
import org.mozilla.fenix.mvi.UIComponentViewModelBase
import org.mozilla.fenix.mvi.UIComponentViewModelProvider
import org.mozilla.fenix.mvi.ViewState
object FenixViewModelProvider {
fun <S : ViewState, C : Change, T : UIComponentViewModelBase<S, C>> create(
fragment: Fragment,
modelClass: Class<T>,
viewModelCreator: () -> T
): UIComponentViewModelProvider<S, C> {
val factory = object : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return viewModelCreator() as T
}
}
return object : UIComponentViewModelProvider<S, C> {
override fun fetchViewModel(): T {
return ViewModelProvider(fragment, factory).get(modelClass)
}
}
}
}
......@@ -14,7 +14,6 @@ import android.widget.Button
import android.widget.RadioButton
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.content.ContextCompat
import androidx.lifecycle.Observer
import androidx.transition.TransitionInflater
import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.fragment_browser.*
......@@ -25,6 +24,7 @@ import mozilla.components.feature.contextmenu.ContextMenuCandidate
import mozilla.components.feature.readerview.ReaderViewFeature
import mozilla.components.feature.session.TrackingProtectionUseCases
import mozilla.components.feature.sitepermissions.SitePermissions
import mozilla.components.feature.tab.collections.TabCollection
import mozilla.components.feature.tabs.WindowFeature
import mozilla.components.lib.state.ext.consumeFrom
import mozilla.components.support.base.feature.UserInteractionHandler
......@@ -39,9 +39,6 @@ import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.nav
import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.home.sessioncontrol.SessionControlChange
import org.mozilla.fenix.home.sessioncontrol.TabCollection
import org.mozilla.fenix.mvi.getManagedEmitter
import org.mozilla.fenix.trackingprotection.TrackingProtectionOverlay
/**
......@@ -119,7 +116,6 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler {
override fun onStart() {
super.onStart()
subscribeToTabCollections()
val toolbarSessionObserver = TrackingProtectionOverlay(
context = requireContext(),
settings = requireContext().settings()
......@@ -232,17 +228,6 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler {
}
}
private fun subscribeToTabCollections() {
requireComponents.core.tabCollectionStorage.getCollections().observe(this, Observer {
requireComponents.core.tabCollectionStorage.cachedTabCollections = it
getManagedEmitter<SessionControlChange>().onNext(
SessionControlChange.CollectionsChange(
it
)
)
})
}
private val collectionStorageObserver = object : TabCollectionStorage.Observer {
override fun onCollectionCreated(title: String, sessions: List<Session>) {
showTabSavedToCollectionSnackbar()
......
......@@ -17,8 +17,8 @@ import org.mozilla.fenix.R
import org.mozilla.fenix.components.Analytics
import org.mozilla.fenix.components.TabCollectionStorage
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.home.sessioncontrol.Tab
import org.mozilla.fenix.home.sessioncontrol.toSessionBundle
import org.mozilla.fenix.home.Tab
import org.mozilla.fenix.home.toSessionBundle
interface CollectionCreationController {
......
......@@ -22,7 +22,7 @@ import org.mozilla.fenix.R
import org.mozilla.fenix.components.StoreProvider
import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.ext.toTab
import org.mozilla.fenix.home.sessioncontrol.Tab
import org.mozilla.fenix.home.Tab
@ExperimentalCoroutinesApi
class CollectionCreationFragment : DialogFragment() {
......
......@@ -6,8 +6,8 @@
package org.mozilla.fenix.collections
import org.mozilla.fenix.home.sessioncontrol.Tab
import org.mozilla.fenix.home.sessioncontrol.TabCollection
import mozilla.components.feature.tab.collections.TabCollection
import org.mozilla.fenix.home.Tab
interface CollectionCreationInteractor {
......
......@@ -9,7 +9,7 @@ import mozilla.components.lib.state.Action
import mozilla.components.lib.state.State
import mozilla.components.lib.state.Store
import org.mozilla.fenix.collections.CollectionCreationAction.StepChanged
import org.mozilla.fenix.home.sessioncontrol.Tab
import org.mozilla.fenix.home.Tab
class CollectionCreationStore(
initialState: CollectionCreationState
......
......@@ -15,7 +15,7 @@ import kotlinx.android.synthetic.main.collection_tab_list_row.view.*
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.loadIntoView
import org.mozilla.fenix.home.sessioncontrol.Tab
import org.mozilla.fenix.home.Tab
class CollectionCreationTabListAdapter(
private val interactor: CollectionCreationInteractor
......
......@@ -21,6 +21,7 @@ import androidx.transition.TransitionManager
import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.component_collection_creation.*
import kotlinx.android.synthetic.main.component_collection_creation.view.*
import mozilla.components.feature.tab.collections.TabCollection
import mozilla.components.support.ktx.android.view.hideKeyboard
import mozilla.components.support.ktx.android.view.showKeyboard
import org.mozilla.fenix.R
......@@ -28,8 +29,7 @@ import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.increaseTapArea
import org.mozilla.fenix.ext.toShortUrl
import org.mozilla.fenix.home.sessioncontrol.Tab
import org.mozilla.fenix.home.sessioncontrol.TabCollection
import org.mozilla.fenix.home.Tab
@SuppressWarnings("LargeClass")
class CollectionCreationView(
......
......@@ -10,11 +10,11 @@ import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.collections_list_item.view.*
import mozilla.components.feature.tab.collections.TabCollection
import org.mozilla.fenix.R
import org.mozilla.fenix.components.description
import org.mozilla.fenix.ext.getIconColor
import org.mozilla.fenix.home.sessioncontrol.Tab
import org.mozilla.fenix.home.sessioncontrol.TabCollection
import org.mozilla.fenix.home.Tab
class SaveCollectionListAdapter(
private val interactor: CollectionCreationInteractor
......
......@@ -8,7 +8,7 @@ import android.content.Context
import mozilla.components.browser.session.Session
import mozilla.components.feature.media.state.MediaState
import mozilla.components.lib.publicsuffixlist.PublicSuffixList
import org.mozilla.fenix.home.sessioncontrol.Tab
import org.mozilla.fenix.home.Tab
fun Session.toTab(context: Context, selected: Boolean? = null, mediaState: MediaState? = null): Tab =
this.toTab(context.components.publicSuffixList, selected, mediaState)
......
......@@ -7,8 +7,8 @@ package org.mozilla.fenix.ext
import android.content.Context
import androidx.annotation.ColorInt
import androidx.core.content.ContextCompat
import mozilla.components.feature.tab.collections.TabCollection
import org.mozilla.fenix.R
import org.mozilla.fenix.home.sessioncontrol.TabCollection
import kotlin.math.abs
/**
......
/* 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.home
import android.graphics.Bitmap
import mozilla.components.browser.session.Session
import mozilla.components.browser.session.SessionManager
import mozilla.components.feature.media.state.MediaState
import mozilla.components.feature.tab.collections.TabCollection
import mozilla.components.lib.state.Action
import mozilla.components.lib.state.State
import mozilla.components.lib.state.Store
/**
* The [Store] for holding the [HomeFragmentState] and applying [HomeFragmentAction]s.
*/
class HomeFragmentStore(
initialState: HomeFragmentState
) : Store<HomeFragmentState, HomeFragmentAction>(
initialState, ::homeFragmentStateReducer
)
data class Tab(
val sessionId: String,
val url: String,
val hostname: String,
val title: String,
val selected: Boolean? = null,
var mediaState: MediaState? = null,
val icon: Bitmap? = null
)
fun List<Tab>.toSessionBundle(sessionManager: SessionManager): List<Session> {
return this.mapNotNull { sessionManager.findSessionById(it.sessionId) }
}
/**
* The state for the [HomeFragment].
*
* @property collections The list of [TabCollection] to display in the [HomeFragment].
* @property expandedCollections A set containing the ids of the [TabCollection] that are expanded
* in the [HomeFragment].
* @property mode The state of the [HomeFragment] UI.
* @property tabs The list of opened [Tab] in the [HomeFragment].
*/
data class HomeFragmentState(
val collections: List<TabCollection>,
val expandedCollections: Set<Long>,
val mode: Mode,
val tabs: List<Tab>
) : State
sealed class HomeFragmentAction : Action {
data class Change(val tabs: List<Tab>, val mode: Mode, val collections: List<TabCollection>) :
HomeFragmentAction()
data class CollectionExpanded(val collection: TabCollection, val expand: Boolean) : HomeFragmentAction()
data class CollectionsChange(val collections: List<TabCollection>) : HomeFragmentAction()
data class ModeChange(val mode: Mode) : HomeFragmentAction()
data class TabsChange(val tabs: List<Tab>) : HomeFragmentAction()
}
private fun homeFragmentStateReducer(
state: HomeFragmentState,
action: HomeFragmentAction
): HomeFragmentState {
return when (action) {
is HomeFragmentAction.Change -> state.copy(
collections = action.collections,
mode = action.mode,
tabs = action.tabs
)
is HomeFragmentAction.CollectionExpanded -> {
val newExpandedCollection = state.expandedCollections.toMutableSet()
if (action.expand) {
newExpandedCollection.add(action.collection.id)
} else {
newExpandedCollection.remove(action.collection.id)
}
state.copy(expandedCollections = newExpandedCollection)
}
is HomeFragmentAction.CollectionsChange -> state.copy(collections = action.collections)
is HomeFragmentAction.ModeChange -> state.copy(mode = action.mode, tabs = emptyList())
is HomeFragmentAction.TabsChange -> state.copy(tabs = action.tabs)
}
}
......@@ -5,7 +5,6 @@
package org.mozilla.fenix.home
import android.content.Context
import io.reactivex.Observer
import mozilla.components.concept.sync.AccountObserver
import mozilla.components.concept.sync.AuthType
import mozilla.components.concept.sync.OAuthAccount
......@@ -14,7 +13,6 @@ import mozilla.components.service.fxa.sharing.ShareableAccount
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
import org.mozilla.fenix.browser.browsingmode.BrowsingModeManager
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.home.sessioncontrol.SessionControlChange
import org.mozilla.fenix.onboarding.FenixOnboarding
/**
......@@ -49,7 +47,7 @@ class CurrentMode(
private val context: Context,
private val onboarding: FenixOnboarding,
private val browsingModeManager: BrowsingModeManager,
private val emitter: Observer<SessionControlChange>
private val dispatchModeChanges: (mode: Mode) -> Unit
) : AccountObserver {
private val accountManager = context.components.backgroundServices.accountManager
......@@ -71,7 +69,7 @@ class CurrentMode(
}
fun emitModeChanges() {
emitter.onNext(SessionControlChange.ModeChange(getCurrentMode()))
dispatchModeChanges(getCurrentMode())
}
override fun onAuthenticated(account: OAuthAccount, authType: AuthType) = emitModeChanges()
......
......@@ -14,10 +14,11 @@ import androidx.annotation.StringRes
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import io.reactivex.Observer
import kotlinx.android.synthetic.main.tab_list_row.*
import mozilla.components.feature.media.state.MediaState
import mozilla.components.feature.tab.collections.TabCollection
import org.mozilla.fenix.home.OnboardingState
import org.mozilla.fenix.home.Tab
import org.mozilla.fenix.home.sessioncontrol.viewholders.CollectionHeaderViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.CollectionViewHolder
import org.mozilla.fenix.home.sessioncontrol.viewholders.NoContentMessageViewHolder
......@@ -139,7 +140,7 @@ class AdapterItemDiffCallback : DiffUtil.ItemCallback<AdapterItem>() {
}
class SessionControlAdapter(
private val actionEmitter: Observer<SessionControlAction>
private val interactor: SessionControlInteractor
) : ListAdapter<AdapterItem, RecyclerView.ViewHolder>(AdapterItemDiffCallback()) {
// This method triggers the ComplexMethod lint error when in fact it's quite simple.
......@@ -147,14 +148,14 @@ class SessionControlAdapter(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(viewType, parent, false)
return when (viewType) {
TabHeaderViewHolder.LAYOUT_ID -> TabHeaderViewHolder(view, actionEmitter)
TabViewHolder.LAYOUT_ID -> TabViewHolder(view, actionEmitter)
SaveTabGroupViewHolder.LAYOUT_ID -> SaveTabGroupViewHolder(view, actionEmitter)
PrivateBrowsingDescriptionViewHolder.LAYOUT_ID -> PrivateBrowsingDescriptionViewHolder(view, actionEmitter)
TabHeaderViewHolder.LAYOUT_ID -> TabHeaderViewHolder(view, interactor)
TabViewHolder.LAYOUT_ID -> TabViewHolder(view, interactor)
SaveTabGroupViewHolder.LAYOUT_ID -> SaveTabGroupViewHolder(view, interactor)
PrivateBrowsingDescriptionViewHolder.LAYOUT_ID -> PrivateBrowsingDescriptionViewHolder(view, interactor)
NoContentMessageViewHolder.LAYOUT_ID -> NoContentMessageViewHolder(view)
CollectionHeaderViewHolder.LAYOUT_ID -> CollectionHeaderViewHolder(view)
CollectionViewHolder.LAYOUT_ID -> CollectionViewHolder(view, actionEmitter)
TabInCollectionViewHolder.LAYOUT_ID -> TabInCollectionViewHolder(view, actionEmitter)
CollectionViewHolder.LAYOUT_ID -> CollectionViewHolder(view, interactor)
TabInCollectionViewHolder.LAYOUT_ID -> TabInCollectionViewHolder(view, interactor)
OnboardingHeaderViewHolder.LAYOUT_ID -> OnboardingHeaderViewHolder(view)
OnboardingSectionHeaderViewHolder.LAYOUT_ID -> OnboardingSectionHeaderViewHolder(view)
OnboardingAutomaticSignInViewHolder.LAYOUT_ID -> OnboardingAutomaticSignInViewHolder(view)
......@@ -163,7 +164,7 @@ class SessionControlAdapter(
OnboardingTrackingProtectionViewHolder.LAYOUT_ID -> OnboardingTrackingProtectionViewHolder(view)
OnboardingPrivateBrowsingViewHolder.LAYOUT_ID -> OnboardingPrivateBrowsingViewHolder(view)
OnboardingPrivacyNoticeViewHolder.LAYOUT_ID -> OnboardingPrivacyNoticeViewHolder(view)
OnboardingFinishViewHolder.LAYOUT_ID -> OnboardingFinishViewHolder(view, actionEmitter)
OnboardingFinishViewHolder.LAYOUT_ID -> OnboardingFinishViewHolder(view, interactor)
else -> throw IllegalStateException()
}
}
......
/* 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.home.sessioncontrol
import android.content.Context
import android.graphics.Bitmap
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import io.reactivex.Observer
import mozilla.components.browser.session.Session
import mozilla.components.browser.session.SessionManager
import mozilla.components.feature.media.state.MediaState
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.home.Mode
import org.mozilla.fenix.mvi.Action
import org.mozilla.fenix.mvi.ActionBusFactory
import org.mozilla.fenix.mvi.Change
import org.mozilla.fenix.mvi.UIComponent
import org.mozilla.fenix.mvi.UIComponentViewModelBase
import org.mozilla.fenix.mvi.UIComponentViewModelProvider
import org.mozilla.fenix.mvi.ViewState
import mozilla.components.feature.tab.collections.Tab as ComponentTab
import mozilla.components.feature.tab.collections.TabCollection as ACTabCollection
class SessionControlComponent(
private val container: ViewGroup,
bus: ActionBusFactory,
viewModelProvider: UIComponentViewModelProvider<SessionControlState, SessionControlChange>
) :
UIComponent<SessionControlState, SessionControlAction, SessionControlChange>(
bus.getManagedEmitter(SessionControlAction::class.java),
bus.getSafeManagedObservable(SessionControlChange::class.java),
viewModelProvider
) {
override fun initView() = SessionControlUIView(container, actionEmitter, changesObservable)
val view: RecyclerView
get() = uiView.view as RecyclerView
init {
bind()
}
}
data class Tab(
val sessionId: String,
val url: String,
val hostname: String,
val title: String,
val selected: Boolean? = null,
var mediaState: MediaState? = null,
val icon: Bitmap? = null
)
fun List<Tab>.toSessionBundle(context: Context): MutableList<Session> =
this.toSessionBundle(context.components.core.sessionManager)
fun List<Tab>.toSessionBundle(sessionManager: SessionManager): MutableList<Session> {
val sessionBundle = mutableListOf<Session>()
this.forEach {
sessionManager.findSessionById(it.sessionId)?.let { session ->
sessionBundle.add(session)
}
}
return sessionBundle
}
data class SessionControlState(
val tabs: List<Tab>,
val expandedCollections: Set<Long>,
val collections: List<TabCollection>,
val mode: Mode
) : ViewState
typealias TabCollection = ACTabCollection
sealed class TabAction : Action {
data class SaveTabGroup(val selectedTabSessionId: String?) : TabAction()
object ShareTabs : TabAction()
data class CloseAll(val private: Boolean) : TabAction()
data class Select(val tabView: View, val sessionId: String) : TabAction()
data class Close(val sessionId: String) : TabAction()
data class Share(val sessionId: String) : TabAction()
data class PauseMedia(val sessionId: String) : TabAction()
data class PlayMedia(val sessionId: String) : TabAction()
object PrivateBrowsingLearnMore : TabAction()
}
sealed class CollectionAction : Action {
data class Expand(val collection: TabCollection) : CollectionAction()
data class Collapse(val collection: TabCollection) : CollectionAction()
data class Delete(val collection: TabCollection) : CollectionAction()
data class AddTab(val collection: TabCollection) : CollectionAction()
data class Rename(val collection: TabCollection) : CollectionAction()
data class OpenTab(val tab: ComponentTab) : CollectionAction()
data class OpenTabs(val collection: TabCollection) : CollectionAction()
data class ShareTabs(val collection: TabCollection) : CollectionAction()
data class RemoveTab(val collection: TabCollection, val tab: ComponentTab) : CollectionAction()
}
sealed class OnboardingAction : Action {
object Finish : OnboardingAction()
}
sealed class SessionControlAction : Action {
data class Tab(val action: TabAction) : SessionControlAction()
data class Collection(val action: CollectionAction) : SessionControlAction()
data class Onboarding(val action: OnboardingAction) : SessionControlAction()
}
fun Observer<SessionControlAction>.onNext(tabAction: TabAction) {
onNext(SessionControlAction.Tab(tabAction))
}
fun Observer<SessionControlAction>.onNext(collectionAction: CollectionAction) {
onNext(SessionControlAction.Collection(collectionAction))
}
fun Observer<SessionControlAction>.onNext(onboardingAction: OnboardingAction) {
onNext(SessionControlAction.Onboarding(onboardingAction))
}
sealed class SessionControlChange : Change {
data class Change(val tabs: List<Tab>, val mode: Mode, val collections: List<TabCollection>) :
SessionControlChange()
data class TabsChange(val tabs: List<Tab>) : SessionControlChange()
data class ModeChange(val mode: Mode) : SessionControlChange()
data class CollectionsChange(val collections: List<TabCollection>) : SessionControlChange()
data class ExpansionChange(val collection: TabCollection, val expand: Boolean) : SessionControlChange()
}
class SessionControlViewModel(
initialState: SessionControlState
) : UIComponentViewModelBase<SessionControlState, SessionControlChange>(initialState, reducer) {
companion object {
val reducer: (SessionControlState, SessionControlChange) -> SessionControlState = { state, change ->
when (change) {
is SessionControlChange.CollectionsChange -> state.copy(collections = change.collections)
is SessionControlChange.TabsChange -> state.copy(tabs = change.tabs)
is SessionControlChange.ModeChange -> state.copy(mode = change.mode, tabs = emptyList())
is SessionControlChange.ExpansionChange -> {
val newExpandedCollection = state.expandedCollections.toMutableSet()
if (change.expand) {
newExpandedCollection.add(change.collection.id)
} else {
newExpandedCollection.remove(change.collection.id)
}
state.copy(expandedCollections = newExpandedCollection)
}
is SessionControlChange.Change -> state.copy(
tabs = change.tabs,
mode = change.mode,
collections = change.collections
)
}
}
}
}
/* 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.home.sessioncontrol
import android.view.View
import androidx.constraintlayout.motion.widget.MotionLayout
import androidx.navigation.NavController
import androidx.navigation.fragment.FragmentNavigator
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import mozilla.components.browser.session.SessionManager
import mozilla.components.concept.engine.prompt.ShareData
import mozilla.components.feature.media.ext.pauseIfPlaying
import mozilla.components.feature.media.ext.playIfPaused
import mozilla.components.feature.media.state.MediaStateMachine
import mozilla.components.feature.tab.collections.TabCollection
import mozilla.components.feature.tab.collections.Tab as ComponentTab