Teases apart ViewModel dependencies (#2499)
* No Issue - pulls render outside of the viewmodel * No Issue - Properly subscribes to the changesObservable * No Issue - Fixes ViewModel testsmaster
parent
0143c54817
commit
c5e5ef4b25
|
@ -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
|
||||
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.ViewModelProviders
|
||||
import org.mozilla.fenix.mvi.ViewState
|
||||
import org.mozilla.fenix.mvi.Change
|
||||
import org.mozilla.fenix.mvi.UIComponentViewModelBase
|
||||
import org.mozilla.fenix.mvi.UIComponentViewModelProvider
|
||||
|
||||
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 ViewModelProviders.of(fragment, factory).get(modelClass)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -55,6 +55,7 @@ import mozilla.components.support.ktx.android.view.exitImmersiveModeIfNeeded
|
|||
import mozilla.components.support.ktx.kotlin.toUri
|
||||
import org.mozilla.fenix.BrowsingModeManager
|
||||
import org.mozilla.fenix.DefaultThemeManager
|
||||
import org.mozilla.fenix.FenixViewModelProvider
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
import org.mozilla.fenix.IntentReceiverActivity
|
||||
import org.mozilla.fenix.R
|
||||
|
@ -70,6 +71,7 @@ import org.mozilla.fenix.components.toolbar.ToolbarComponent
|
|||
import org.mozilla.fenix.components.toolbar.ToolbarIntegration
|
||||
import org.mozilla.fenix.components.toolbar.ToolbarMenu
|
||||
import org.mozilla.fenix.components.toolbar.ToolbarUIView
|
||||
import org.mozilla.fenix.components.toolbar.ToolbarViewModel
|
||||
import org.mozilla.fenix.customtabs.CustomTabsIntegration
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.requireComponents
|
||||
|
@ -84,6 +86,7 @@ import org.mozilla.fenix.quickactionsheet.QuickActionAction
|
|||
import org.mozilla.fenix.quickactionsheet.QuickActionChange
|
||||
import org.mozilla.fenix.quickactionsheet.QuickActionComponent
|
||||
import org.mozilla.fenix.quickactionsheet.QuickActionState
|
||||
import org.mozilla.fenix.quickactionsheet.QuickActionViewModel
|
||||
import org.mozilla.fenix.utils.ItsNotBrokenSnack
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
@ -129,11 +132,17 @@ class BrowserFragment : Fragment(), BackHandler, CoroutineScope {
|
|||
|
||||
toolbarComponent = ToolbarComponent(
|
||||
view.browserLayout,
|
||||
this,
|
||||
ActionBusFactory.get(this), customTabSessionId,
|
||||
(activity as HomeActivity).browsingModeManager.isPrivate,
|
||||
SearchState("", getSessionById()?.searchTerms ?: "", isEditing = false),
|
||||
search_engine_icon
|
||||
search_engine_icon,
|
||||
FenixViewModelProvider.create(
|
||||
this,
|
||||
ToolbarViewModel::class.java
|
||||
) {
|
||||
ToolbarViewModel(
|
||||
SearchState("", getSessionById()?.searchTerms ?: "", isEditing = false)
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
toolbarComponent.uiView.view.apply {
|
||||
|
@ -154,14 +163,20 @@ class BrowserFragment : Fragment(), BackHandler, CoroutineScope {
|
|||
|
||||
QuickActionComponent(
|
||||
view.nestedScrollQuickAction,
|
||||
this,
|
||||
ActionBusFactory.get(this),
|
||||
QuickActionState(
|
||||
readable = getSessionById()?.readerable ?: false,
|
||||
bookmarked = findBookmarkedURL(getSessionById()),
|
||||
readerActive = getSessionById()?.readerMode ?: false,
|
||||
bounceNeeded = false
|
||||
FenixViewModelProvider.create(
|
||||
this,
|
||||
QuickActionViewModel::class.java
|
||||
) {
|
||||
QuickActionViewModel(
|
||||
QuickActionState(
|
||||
readable = getSessionById()?.readerable ?: false,
|
||||
bookmarked = findBookmarkedURL(getSessionById()),
|
||||
readerActive = getSessionById()?.readerMode ?: false,
|
||||
bounceNeeded = false
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
val activity = activity as HomeActivity
|
||||
|
|
|
@ -5,19 +5,18 @@ package org.mozilla.fenix.collections
|
|||
file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import io.reactivex.Observable
|
||||
import org.mozilla.fenix.home.sessioncontrol.Tab
|
||||
import org.mozilla.fenix.home.sessioncontrol.TabCollection
|
||||
import org.mozilla.fenix.mvi.ViewState
|
||||
import org.mozilla.fenix.mvi.Change
|
||||
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.UIComponentViewModel
|
||||
import org.mozilla.fenix.mvi.ViewState
|
||||
import org.mozilla.fenix.mvi.UIComponentViewModelBase
|
||||
import org.mozilla.fenix.mvi.UIComponentViewModelProvider
|
||||
|
||||
sealed class SaveCollectionStep {
|
||||
object SelectTabs : SaveCollectionStep()
|
||||
|
@ -56,30 +55,24 @@ sealed class CollectionCreationAction : Action {
|
|||
|
||||
class CollectionCreationComponent(
|
||||
private val container: ViewGroup,
|
||||
owner: Fragment,
|
||||
bus: ActionBusFactory,
|
||||
override var initialState: CollectionCreationState = CollectionCreationState()
|
||||
viewModelProvider: UIComponentViewModelProvider<CollectionCreationState, CollectionCreationChange>
|
||||
) : UIComponent<CollectionCreationState, CollectionCreationAction, CollectionCreationChange>(
|
||||
owner,
|
||||
bus.getManagedEmitter(CollectionCreationAction::class.java),
|
||||
bus.getSafeManagedObservable(CollectionCreationChange::class.java)
|
||||
bus.getSafeManagedObservable(CollectionCreationChange::class.java),
|
||||
viewModelProvider
|
||||
) {
|
||||
override fun initView() = CollectionCreationUIView(container, actionEmitter, changesObservable)
|
||||
|
||||
override fun render(): Observable<CollectionCreationState> =
|
||||
ViewModelProvider(owner, CollectionCreationViewModel.Factory(initialState)).get(
|
||||
CollectionCreationViewModel::class.java
|
||||
).render(changesObservable, uiView)
|
||||
|
||||
init {
|
||||
render()
|
||||
bind()
|
||||
}
|
||||
}
|
||||
|
||||
class CollectionCreationViewModel(
|
||||
initialState: CollectionCreationState
|
||||
) :
|
||||
UIComponentViewModel<CollectionCreationState, CollectionCreationAction, CollectionCreationChange>(
|
||||
UIComponentViewModelBase<CollectionCreationState, CollectionCreationChange>(
|
||||
initialState,
|
||||
reducer
|
||||
) {
|
||||
|
|
|
@ -14,6 +14,7 @@ import androidx.fragment.app.DialogFragment
|
|||
import androidx.lifecycle.ViewModelProviders
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import kotlinx.android.synthetic.main.fragment_create_collection.view.*
|
||||
import org.mozilla.fenix.FenixViewModelProvider
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.components.FenixSnackbar
|
||||
import org.mozilla.fenix.ext.getRootView
|
||||
|
@ -50,13 +51,13 @@ class CreateCollectionFragment : DialogFragment() {
|
|||
|
||||
collectionCreationComponent = CollectionCreationComponent(
|
||||
view.create_collection_wrapper,
|
||||
this,
|
||||
ActionBusFactory.get(this),
|
||||
CollectionCreationState(
|
||||
tabs = tabs,
|
||||
selectedTabs = selectedTabs,
|
||||
saveCollectionStep = step
|
||||
)
|
||||
FenixViewModelProvider.create(
|
||||
this,
|
||||
CollectionCreationViewModel::class.java
|
||||
) {
|
||||
CollectionCreationViewModel(CollectionCreationState(tabs, selectedTabs, step))
|
||||
}
|
||||
)
|
||||
return view
|
||||
}
|
||||
|
|
|
@ -7,37 +7,32 @@ package org.mozilla.fenix.components.toolbar
|
|||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.ViewModelProviders
|
||||
import io.reactivex.Observable
|
||||
import kotlinx.android.synthetic.main.component_search.*
|
||||
import mozilla.components.browser.search.SearchEngine
|
||||
import mozilla.components.browser.toolbar.BrowserToolbar
|
||||
import org.mozilla.fenix.DefaultThemeManager
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.mvi.ViewState
|
||||
import org.mozilla.fenix.mvi.Change
|
||||
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.UIComponentViewModel
|
||||
import org.mozilla.fenix.mvi.ViewState
|
||||
import org.mozilla.fenix.mvi.UIComponentViewModelBase
|
||||
import org.mozilla.fenix.mvi.UIComponentViewModelProvider
|
||||
|
||||
class ToolbarComponent(
|
||||
private val container: ViewGroup,
|
||||
owner: Fragment,
|
||||
bus: ActionBusFactory,
|
||||
private val sessionId: String?,
|
||||
private val isPrivate: Boolean,
|
||||
override var initialState: SearchState = SearchState("", "", false),
|
||||
private val engineIconView: ImageView? = null
|
||||
private val engineIconView: ImageView? = null,
|
||||
viewModelProvider: UIComponentViewModelProvider<SearchState, SearchChange>
|
||||
) :
|
||||
UIComponent<SearchState, SearchAction, SearchChange>(
|
||||
owner,
|
||||
bus.getManagedEmitter(SearchAction::class.java),
|
||||
bus.getSafeManagedObservable(SearchChange::class.java)
|
||||
bus.getSafeManagedObservable(SearchChange::class.java),
|
||||
viewModelProvider
|
||||
) {
|
||||
|
||||
fun getView(): BrowserToolbar = uiView.toolbar
|
||||
|
@ -51,12 +46,8 @@ class ToolbarComponent(
|
|||
engineIconView
|
||||
)
|
||||
|
||||
override fun render(): Observable<SearchState> =
|
||||
ViewModelProviders.of(owner, ToolbarViewModel.Factory(initialState, changesObservable))
|
||||
.get(ToolbarViewModel::class.java).render(changesObservable, uiView)
|
||||
|
||||
init {
|
||||
render()
|
||||
bind()
|
||||
applyTheme()
|
||||
}
|
||||
|
||||
|
@ -100,16 +91,7 @@ sealed class SearchChange : Change {
|
|||
}
|
||||
|
||||
class ToolbarViewModel(initialState: SearchState) :
|
||||
UIComponentViewModel<SearchState, SearchAction, SearchChange>(initialState, reducer) {
|
||||
|
||||
class Factory(
|
||||
private val initialState: SearchState,
|
||||
val changesObservable: Observable<SearchChange>
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T =
|
||||
ToolbarViewModel(initialState) as T
|
||||
}
|
||||
UIComponentViewModelBase<SearchState, SearchChange>(initialState, reducer) {
|
||||
|
||||
companion object {
|
||||
val reducer: Reducer<SearchState, SearchChange> = { state, change ->
|
||||
|
|
|
@ -4,17 +4,13 @@
|
|||
package org.mozilla.fenix.exceptions
|
||||
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.ViewModelProviders
|
||||
import io.reactivex.Observable
|
||||
import org.mozilla.fenix.mvi.ViewState
|
||||
import org.mozilla.fenix.mvi.Change
|
||||
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.UIComponentViewModel
|
||||
import org.mozilla.fenix.mvi.ViewState
|
||||
import org.mozilla.fenix.mvi.UIComponentViewModelBase
|
||||
import org.mozilla.fenix.mvi.UIComponentViewModelProvider
|
||||
import org.mozilla.fenix.test.Mockable
|
||||
|
||||
data class ExceptionsItem(val url: String)
|
||||
|
@ -22,24 +18,19 @@ data class ExceptionsItem(val url: String)
|
|||
@Mockable
|
||||
class ExceptionsComponent(
|
||||
private val container: ViewGroup,
|
||||
owner: Fragment,
|
||||
bus: ActionBusFactory,
|
||||
override var initialState: ExceptionsState = ExceptionsState(emptyList())
|
||||
viewModelProvider: UIComponentViewModelProvider<ExceptionsState, ExceptionsChange>
|
||||
) :
|
||||
UIComponent<ExceptionsState, ExceptionsAction, ExceptionsChange>(
|
||||
owner,
|
||||
bus.getManagedEmitter(ExceptionsAction::class.java),
|
||||
bus.getSafeManagedObservable(ExceptionsChange::class.java)
|
||||
bus.getSafeManagedObservable(ExceptionsChange::class.java),
|
||||
viewModelProvider
|
||||
) {
|
||||
|
||||
override fun render(): Observable<ExceptionsState> =
|
||||
ViewModelProviders.of(owner, ExceptionsViewModel.Factory(initialState))
|
||||
.get(ExceptionsViewModel::class.java).render(changesObservable, uiView)
|
||||
|
||||
override fun initView() = ExceptionsUIView(container, actionEmitter, changesObservable)
|
||||
|
||||
init {
|
||||
render()
|
||||
bind()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,20 +47,9 @@ sealed class ExceptionsChange : Change {
|
|||
data class Change(val list: List<ExceptionsItem>) : ExceptionsChange()
|
||||
}
|
||||
|
||||
class ExceptionsViewModel(initialState: ExceptionsState) :
|
||||
UIComponentViewModel<ExceptionsState, ExceptionsAction, ExceptionsChange>(
|
||||
initialState,
|
||||
reducer
|
||||
) {
|
||||
|
||||
class Factory(
|
||||
private val initialState: ExceptionsState
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T =
|
||||
ExceptionsViewModel(initialState) as T
|
||||
}
|
||||
|
||||
class ExceptionsViewModel(
|
||||
initialState: ExceptionsState
|
||||
) : UIComponentViewModelBase<ExceptionsState, ExceptionsChange>(initialState, reducer) {
|
||||
companion object {
|
||||
val reducer: (ExceptionsState, ExceptionsChange) -> ExceptionsState = { state, change ->
|
||||
when (change) {
|
||||
|
|
|
@ -17,6 +17,7 @@ import kotlinx.coroutines.Dispatchers
|
|||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import org.mozilla.fenix.FenixViewModelProvider
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.mvi.ActionBusFactory
|
||||
import org.mozilla.fenix.mvi.getAutoDisposeObservable
|
||||
|
@ -48,9 +49,13 @@ class ExceptionsFragment : Fragment(), CoroutineScope {
|
|||
val view = inflater.inflate(R.layout.fragment_exceptions, container, false)
|
||||
exceptionsComponent = ExceptionsComponent(
|
||||
view.exceptions_layout,
|
||||
this,
|
||||
ActionBusFactory.get(this),
|
||||
ExceptionsState(loadAndMapExceptions())
|
||||
FenixViewModelProvider.create(
|
||||
this,
|
||||
ExceptionsViewModel::class.java
|
||||
) {
|
||||
ExceptionsViewModel(ExceptionsState(loadAndMapExceptions()))
|
||||
}
|
||||
)
|
||||
return view
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ import org.jetbrains.anko.constraint.layout.applyConstraintSet
|
|||
import org.mozilla.fenix.BrowserDirection
|
||||
import org.mozilla.fenix.BrowsingModeManager
|
||||
import org.mozilla.fenix.DefaultThemeManager
|
||||
import org.mozilla.fenix.FenixViewModelProvider
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.collections.CreateCollectionViewModel
|
||||
|
@ -48,6 +49,7 @@ import org.mozilla.fenix.home.sessioncontrol.SessionControlAction
|
|||
import org.mozilla.fenix.home.sessioncontrol.SessionControlChange
|
||||
import org.mozilla.fenix.home.sessioncontrol.SessionControlComponent
|
||||
import org.mozilla.fenix.home.sessioncontrol.SessionControlState
|
||||
import org.mozilla.fenix.home.sessioncontrol.SessionControlViewModel
|
||||
import org.mozilla.fenix.home.sessioncontrol.Tab
|
||||
import org.mozilla.fenix.home.sessioncontrol.TabAction
|
||||
import org.mozilla.fenix.home.sessioncontrol.TabCollection
|
||||
|
@ -88,9 +90,13 @@ class HomeFragment : Fragment(), CoroutineScope {
|
|||
|
||||
sessionControlComponent = SessionControlComponent(
|
||||
view.homeLayout,
|
||||
this,
|
||||
bus,
|
||||
SessionControlState(listOf(), listOf(), mode)
|
||||
FenixViewModelProvider.create(
|
||||
this,
|
||||
SessionControlViewModel::class.java
|
||||
) {
|
||||
SessionControlViewModel(SessionControlState(listOf(), listOf(), mode))
|
||||
}
|
||||
)
|
||||
|
||||
view.homeLayout.applyConstraintSet {
|
||||
|
@ -450,6 +456,7 @@ class HomeFragment : Fragment(), CoroutineScope {
|
|||
|
||||
private fun emitSessionChanges() {
|
||||
val sessionManager = requireComponents.core.sessionManager
|
||||
|
||||
getManagedEmitter<SessionControlChange>().onNext(
|
||||
SessionControlChange.TabsChange(
|
||||
sessionManager.sessions
|
||||
|
|
|
@ -7,48 +7,35 @@ package org.mozilla.fenix.home.sessioncontrol
|
|||
import android.graphics.Bitmap
|
||||
import android.os.Parcelable
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.ViewModelProviders
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.Observer
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
import org.mozilla.fenix.mvi.ViewState
|
||||
import org.mozilla.fenix.mvi.Change
|
||||
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.UIComponentViewModel
|
||||
import org.mozilla.fenix.mvi.ViewState
|
||||
import org.mozilla.fenix.mvi.UIComponentViewModelBase
|
||||
import org.mozilla.fenix.mvi.UIComponentViewModelProvider
|
||||
|
||||
class SessionControlComponent(
|
||||
private val container: ViewGroup,
|
||||
owner: Fragment,
|
||||
bus: ActionBusFactory,
|
||||
override var initialState: SessionControlState = SessionControlState(emptyList(), emptyList(), Mode.Normal)
|
||||
viewModelProvider: UIComponentViewModelProvider<SessionControlState, SessionControlChange>
|
||||
) :
|
||||
UIComponent<SessionControlState, SessionControlAction, SessionControlChange>(
|
||||
owner,
|
||||
bus.getManagedEmitter(SessionControlAction::class.java),
|
||||
bus.getSafeManagedObservable(SessionControlChange::class.java)
|
||||
bus.getSafeManagedObservable(SessionControlChange::class.java),
|
||||
viewModelProvider
|
||||
) {
|
||||
|
||||
var stateObservable: Observable<SessionControlState>
|
||||
lateinit var viewModel: SessionControlViewModel
|
||||
|
||||
override fun initView() = SessionControlUIView(container, actionEmitter, changesObservable)
|
||||
|
||||
val view: RecyclerView
|
||||
get() = uiView.view as RecyclerView
|
||||
|
||||
override fun render(): Observable<SessionControlState> {
|
||||
viewModel = ViewModelProviders.of(owner, SessionControlViewModel.Factory(initialState))
|
||||
.get(SessionControlViewModel::class.java)
|
||||
return viewModel.render(changesObservable, uiView)
|
||||
}
|
||||
|
||||
init {
|
||||
stateObservable = render()
|
||||
bind()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -124,19 +111,11 @@ sealed class SessionControlChange : Change {
|
|||
}
|
||||
|
||||
class SessionControlViewModel(initialState: SessionControlState) :
|
||||
UIComponentViewModel<SessionControlState, SessionControlAction, SessionControlChange>(
|
||||
UIComponentViewModelBase<SessionControlState, SessionControlChange>(
|
||||
initialState,
|
||||
reducer
|
||||
) {
|
||||
|
||||
class Factory(
|
||||
private val initialState: SessionControlState
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T =
|
||||
SessionControlViewModel(initialState) as T
|
||||
}
|
||||
|
||||
companion object {
|
||||
val reducer: (SessionControlState, SessionControlChange) -> SessionControlState = { state, change ->
|
||||
when (change) {
|
||||
|
|
|
@ -5,46 +5,34 @@
|
|||
package org.mozilla.fenix.library.bookmarks
|
||||
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import io.reactivex.Observable
|
||||
import mozilla.components.concept.storage.BookmarkNode
|
||||
import org.mozilla.fenix.mvi.ViewState
|
||||
import org.mozilla.fenix.mvi.Change
|
||||
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.UIComponentViewModel
|
||||
import org.mozilla.fenix.mvi.UIComponentViewModelBase
|
||||
import org.mozilla.fenix.mvi.UIComponentViewModelProvider
|
||||
import org.mozilla.fenix.mvi.UIView
|
||||
import org.mozilla.fenix.mvi.ViewState
|
||||
import org.mozilla.fenix.test.Mockable
|
||||
|
||||
@Mockable
|
||||
class BookmarkComponent(
|
||||
private val container: ViewGroup,
|
||||
owner: Fragment,
|
||||
bus: ActionBusFactory,
|
||||
override var initialState: BookmarkState =
|
||||
BookmarkState(null, BookmarkState.Mode.Normal)
|
||||
viewModelProvider: UIComponentViewModelProvider<BookmarkState, BookmarkChange>
|
||||
) :
|
||||
UIComponent<BookmarkState, BookmarkAction, BookmarkChange>(
|
||||
owner,
|
||||
bus.getManagedEmitter(BookmarkAction::class.java),
|
||||
bus.getSafeManagedObservable(BookmarkChange::class.java)
|
||||
bus.getSafeManagedObservable(BookmarkChange::class.java),
|
||||
viewModelProvider
|
||||
) {
|
||||
override fun initView(): UIView<BookmarkState, BookmarkAction, BookmarkChange> =
|
||||
BookmarkUIView(container, actionEmitter, changesObservable)
|
||||
|
||||
override fun render(): Observable<BookmarkState> {
|
||||
return ViewModelProvider(
|
||||
owner,
|
||||
BookmarkViewModel.Factory(initialState)
|
||||
).get(BookmarkViewModel::class.java).render(changesObservable, uiView)
|
||||
}
|
||||
|
||||
init {
|
||||
render()
|
||||
bind()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -82,17 +70,11 @@ operator fun BookmarkNode.contains(item: BookmarkNode): Boolean {
|
|||
}
|
||||
|
||||
class BookmarkViewModel(initialState: BookmarkState) :
|
||||
UIComponentViewModel<BookmarkState, BookmarkAction, BookmarkChange>(initialState, reducer) {
|
||||
|
||||
class Factory(
|
||||
private val initialState: BookmarkState
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T =
|
||||
BookmarkViewModel(initialState) as T
|
||||
}
|
||||
UIComponentViewModelBase<BookmarkState, BookmarkChange>(initialState, reducer) {
|
||||
|
||||
companion object {
|
||||
fun create() = BookmarkViewModel(BookmarkState(null, BookmarkState.Mode.Normal))
|
||||
|
||||
val reducer: Reducer<BookmarkState, BookmarkChange> = { state, change ->
|
||||
when (change) {
|
||||
is BookmarkChange.Change -> {
|
||||
|
|
|
@ -37,6 +37,7 @@ import mozilla.components.concept.sync.Profile
|
|||
import mozilla.components.support.base.feature.BackHandler
|
||||
import org.mozilla.fenix.BrowserDirection
|
||||
import org.mozilla.fenix.BrowsingModeManager
|
||||
import org.mozilla.fenix.FenixViewModelProvider
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.components.Components
|
||||
|
@ -74,8 +75,25 @@ class BookmarkFragment : Fragment(), CoroutineScope, BackHandler, AccountObserve
|
|||
|
||||
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, this, ActionBusFactory.get(this))
|
||||
signInComponent = SignInComponent(view.bookmark_layout, this, ActionBusFactory.get(this))
|
||||
bookmarkComponent = BookmarkComponent(
|
||||
view.bookmark_layout,
|
||||
ActionBusFactory.get(this),
|
||||
FenixViewModelProvider.create(
|
||||
this,
|
||||
BookmarkViewModel::class.java,
|
||||
BookmarkViewModel.Companion::create
|
||||
)
|
||||
)
|
||||
signInComponent = SignInComponent(
|
||||
view.bookmark_layout,
|
||||
ActionBusFactory.get(this),
|
||||
FenixViewModelProvider.create(
|
||||
this,
|
||||
SignInViewModel::class.java
|
||||
) {
|
||||
SignInViewModel(SignInState(false))
|
||||
}
|
||||
)
|
||||
return view
|
||||
}
|
||||
|
||||
|
|
|
@ -5,41 +5,30 @@
|
|||
package org.mozilla.fenix.library.bookmarks
|
||||
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import io.reactivex.Observable
|
||||
import org.mozilla.fenix.mvi.ViewState
|
||||
import org.mozilla.fenix.mvi.Change
|
||||
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.UIComponentViewModel
|
||||
import org.mozilla.fenix.mvi.UIComponentViewModelBase
|
||||
import org.mozilla.fenix.mvi.UIComponentViewModelProvider
|
||||
import org.mozilla.fenix.mvi.UIView
|
||||
import org.mozilla.fenix.mvi.ViewState
|
||||
|
||||
class SignInComponent(
|
||||
private val container: ViewGroup,
|
||||
owner: Fragment,
|
||||
bus: ActionBusFactory,
|
||||
override var initialState: SignInState =
|
||||
SignInState(false)
|
||||
viewModelProvider: UIComponentViewModelProvider<SignInState, SignInChange>
|
||||
) : UIComponent<SignInState, SignInAction, SignInChange>(
|
||||
owner,
|
||||
bus.getManagedEmitter(SignInAction::class.java),
|
||||
bus.getSafeManagedObservable(SignInChange::class.java)
|
||||
bus.getSafeManagedObservable(SignInChange::class.java),
|
||||
viewModelProvider
|
||||
) {
|
||||
override fun initView(): UIView<SignInState, SignInAction, SignInChange> =
|
||||
SignInUIView(container, actionEmitter, changesObservable)
|
||||
|
||||
override fun render(): Observable<SignInState> =
|
||||
ViewModelProvider(
|
||||
owner,
|
||||
SignInViewModel.Factory(initialState)
|
||||
).get(SignInViewModel::class.java).render(changesObservable, uiView)
|
||||
|
||||
init {
|
||||
render()
|
||||
bind()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -54,19 +43,9 @@ sealed class SignInChange : Change {
|
|||
object SignedOut : SignInChange()
|
||||
}
|
||||
|
||||
class SignInViewModel(initialState: SignInState) :
|
||||
UIComponentViewModel<SignInState, SignInAction, SignInChange>(
|
||||
initialState, reducer
|
||||
) {
|
||||
|
||||
class Factory(
|
||||
private val initialState: SignInState
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T =
|
||||
SignInViewModel(initialState) as T
|
||||
}
|
||||
|
||||
class SignInViewModel(
|
||||
initialState: SignInState
|
||||
) : UIComponentViewModelBase<SignInState, SignInChange>(initialState, reducer) {
|
||||
companion object {
|
||||
val reducer = object : Reducer<SignInState, SignInChange> {
|
||||
override fun invoke(state: SignInState, change: SignInChange): SignInState {
|
||||
|
|
|
@ -17,6 +17,7 @@ import androidx.appcompat.app.AppCompatActivity
|
|||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModelProviders
|
||||
import androidx.navigation.Navigation
|
||||
import kotlinx.android.synthetic.main.fragment_bookmark.view.*
|
||||
import kotlinx.android.synthetic.main.fragment_select_bookmark_folder.*
|
||||
import kotlinx.android.synthetic.main.fragment_select_bookmark_folder.view.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
@ -31,6 +32,7 @@ import mozilla.components.concept.sync.AccountObserver
|
|||
import mozilla.components.concept.sync.OAuthAccount
|
||||
import mozilla.components.concept.sync.Profile
|
||||
import org.mozilla.fenix.BrowserDirection
|
||||
import org.mozilla.fenix.FenixViewModelProvider
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.ext.getColorFromAttr
|
||||
|
@ -39,6 +41,8 @@ import org.mozilla.fenix.library.bookmarks.BookmarksSharedViewModel
|
|||
import org.mozilla.fenix.library.bookmarks.SignInAction
|
||||
import org.mozilla.fenix.library.bookmarks.SignInChange
|
||||
import org.mozilla.fenix.library.bookmarks.SignInComponent
|
||||
import org.mozilla.fenix.library.bookmarks.SignInState
|
||||
import org.mozilla.fenix.library.bookmarks.SignInViewModel
|
||||
import org.mozilla.fenix.mvi.ActionBusFactory
|
||||
import org.mozilla.fenix.mvi.getAutoDisposeObservable
|
||||
import org.mozilla.fenix.mvi.getManagedEmitter
|
||||
|
@ -68,7 +72,16 @@ class SelectBookmarkFolderFragment : Fragment(), CoroutineScope, AccountObserver
|
|||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
val view = inflater.inflate(R.layout.fragment_select_bookmark_folder, container, false)
|
||||
signInComponent = SignInComponent(view.select_bookmark_layout, this, ActionBusFactory.get(this))
|
||||
signInComponent = SignInComponent(
|
||||
view.bookmark_layout,
|
||||
ActionBusFactory.get(this),
|
||||
FenixViewModelProvider.create(
|
||||
this,
|
||||
SignInViewModel::class.java
|
||||
) {
|
||||
SignInViewModel(SignInState(false))
|
||||
}
|
||||
)
|
||||
return view
|
||||
}
|
||||
|
||||
|
|
|
@ -4,16 +4,13 @@
|
|||
package org.mozilla.fenix.library.history
|
||||
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import io.reactivex.Observable
|
||||
import org.mozilla.fenix.mvi.ViewState
|
||||
import org.mozilla.fenix.mvi.Change
|
||||
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.UIComponentViewModel
|
||||
import org.mozilla.fenix.mvi.ViewState
|
||||
import org.mozilla.fenix.mvi.UIComponentViewModelBase
|
||||
import org.mozilla.fenix.mvi.UIComponentViewModelProvider
|
||||
import org.mozilla.fenix.test.Mockable
|
||||
|
||||
data class HistoryItem(val id: Int, val title: String, val url: String, val visitedAt: Long)
|
||||
|
@ -21,26 +18,19 @@ data class HistoryItem(val id: Int, val title: String, val url: String, val visi
|
|||
@Mockable
|
||||
class HistoryComponent(
|
||||
private val container: ViewGroup,
|
||||
owner: Fragment,
|
||||
bus: ActionBusFactory,
|
||||
override var initialState: HistoryState = HistoryState(emptyList(), HistoryState.Mode.Normal)
|
||||
viewModelProvider: UIComponentViewModelProvider<HistoryState, HistoryChange>
|
||||
) :
|
||||
UIComponent<HistoryState, HistoryAction, HistoryChange>(
|
||||
owner,
|
||||
bus.getManagedEmitter(HistoryAction::class.java),
|
||||
bus.getSafeManagedObservable(HistoryChange::class.java)
|
||||
bus.getSafeManagedObservable(HistoryChange::class.java),
|
||||
viewModelProvider
|
||||
) {
|
||||
|
||||
override fun initView() = HistoryUIView(container, actionEmitter, changesObservable)
|
||||
|
||||
override fun render(): Observable<HistoryState> =
|
||||
ViewModelProvider(
|
||||
owner,
|
||||
HistoryViewModel.Factory(initialState)
|
||||
).get(HistoryViewModel::class.java).render(changesObservable, uiView)
|
||||
|
||||
init {
|
||||
render()
|
||||
bind()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -74,18 +64,11 @@ sealed class HistoryChange : Change {
|
|||
data class RemoveItemForRemoval(val item: HistoryItem) : HistoryChange()
|
||||
}
|
||||
|
||||
class HistoryViewModel(initialState: HistoryState) :
|
||||
UIComponentViewModel<HistoryState, HistoryAction, HistoryChange>(initialState, reducer) {
|
||||
|
||||
class Factory(
|
||||
private val initialState: HistoryState
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T =
|
||||
HistoryViewModel(initialState) as T
|
||||
}
|
||||
|
||||
class HistoryViewModel(
|
||||
initialState: HistoryState
|
||||
) : UIComponentViewModelBase<HistoryState, HistoryChange>(initialState, reducer) {
|
||||
companion object {
|
||||
fun create() = HistoryViewModel(HistoryState(emptyList(), HistoryState.Mode.Normal))
|
||||
val reducer: (HistoryState, HistoryChange) -> HistoryState = { state, change ->
|
||||
when (change) {
|
||||
is HistoryChange.Change -> state.copy(mode = HistoryState.Mode.Normal, items = change.list)
|
||||
|
|
|
@ -29,6 +29,7 @@ import mozilla.components.concept.storage.VisitType
|
|||
import mozilla.components.support.base.feature.BackHandler
|
||||
import org.mozilla.fenix.BrowserDirection
|
||||
import org.mozilla.fenix.BrowsingModeManager
|
||||
import org.mozilla.fenix.FenixViewModelProvider
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.components.Components
|
||||
|
@ -59,7 +60,15 @@ class HistoryFragment : Fragment(), CoroutineScope, BackHandler {
|
|||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
val view = inflater.inflate(R.layout.fragment_history, container, false)
|
||||
historyComponent = HistoryComponent(view.history_layout, this, ActionBusFactory.get(this))
|
||||
historyComponent = HistoryComponent(
|
||||
view.history_layout,
|
||||
ActionBusFactory.get(this),
|
||||
FenixViewModelProvider.create(
|
||||
this,
|
||||
HistoryViewModel::class.java,
|
||||
HistoryViewModel.Companion::create
|
||||
)
|
||||
)
|
||||
return view
|
||||
}
|
||||
|
||||
|
|
|
@ -5,45 +5,30 @@
|
|||
package org.mozilla.fenix.quickactionsheet
|
||||
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import io.reactivex.Observable
|
||||
import org.mozilla.fenix.mvi.Action
|
||||
import org.mozilla.fenix.mvi.ActionBusFactory
|
||||
import org.mozilla.fenix.mvi.UIComponentViewModelProvider
|
||||
import org.mozilla.fenix.mvi.ViewState
|
||||
import org.mozilla.fenix.mvi.Change
|
||||
import org.mozilla.fenix.mvi.Reducer
|
||||
import org.mozilla.fenix.mvi.UIComponent
|
||||
import org.mozilla.fenix.mvi.UIComponentViewModel
|
||||
import org.mozilla.fenix.mvi.UIComponentViewModelBase
|
||||
import org.mozilla.fenix.mvi.UIView
|
||||
import org.mozilla.fenix.mvi.ViewState
|
||||
|
||||
class QuickActionComponent(
|
||||
private val container: ViewGroup,
|
||||
owner: Fragment,
|
||||
bus: ActionBusFactory,
|
||||
override var initialState: QuickActionState = QuickActionState(
|
||||
readable = false,
|
||||
bookmarked = false,
|
||||
readerActive = false,
|
||||
bounceNeeded = false
|
||||
)
|
||||
viewModelProvider: UIComponentViewModelProvider<QuickActionState, QuickActionChange>
|
||||
) : UIComponent<QuickActionState, QuickActionAction, QuickActionChange>(
|
||||
owner,
|
||||
bus.getManagedEmitter(QuickActionAction::class.java),
|
||||
bus.getSafeManagedObservable(QuickActionChange::class.java)
|
||||
bus.getSafeManagedObservable(QuickActionChange::class.java),
|
||||
viewModelProvider
|
||||
) {
|
||||
override fun initView(): UIView<QuickActionState, QuickActionAction, QuickActionChange> =
|
||||
QuickActionUIView(container, actionEmitter, changesObservable)
|
||||
|
||||
override fun render(): Observable<QuickActionState> =
|
||||
ViewModelProvider(
|
||||
owner,
|
||||
QuickActionViewModel.Factory(initialState)
|
||||
).get(QuickActionViewModel::class.java).render(changesObservable, uiView)
|
||||
|
||||
init {
|
||||
render()
|
||||
bind()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -71,20 +56,9 @@ sealed class QuickActionChange : Change {
|
|||
object BounceNeededChange : QuickActionChange()
|
||||
}
|
||||
|
||||
class QuickActionViewModel(initialState: QuickActionState) :
|
||||
UIComponentViewModel<QuickActionState, QuickActionAction, QuickActionChange>(
|
||||
initialState,
|
||||
reducer
|
||||
) {
|
||||
|
||||
class Factory(
|
||||
private val initialState: QuickActionState
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T =
|
||||
QuickActionViewModel(initialState) as T
|
||||
}
|
||||
|
||||
class QuickActionViewModel(
|
||||
initialState: QuickActionState
|
||||
) : UIComponentViewModelBase<QuickActionState, QuickActionChange>(initialState, reducer) {
|
||||
companion object {
|
||||
val reducer: Reducer<QuickActionState, QuickActionChange> = { state, change ->
|
||||
when (change) {
|
||||
|
|
|
@ -26,6 +26,7 @@ import mozilla.components.support.base.feature.ViewBoundFeatureWrapper
|
|||
import mozilla.components.support.ktx.android.content.isPermissionGranted
|
||||
import mozilla.components.support.ktx.kotlin.isUrl
|
||||
import org.mozilla.fenix.BrowserDirection
|
||||
import org.mozilla.fenix.FenixViewModelProvider
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.components.metrics.Event
|
||||
|
@ -34,6 +35,7 @@ import org.mozilla.fenix.components.toolbar.SearchChange
|
|||
import org.mozilla.fenix.components.toolbar.SearchState
|
||||
import org.mozilla.fenix.components.toolbar.ToolbarComponent
|
||||
import org.mozilla.fenix.components.toolbar.ToolbarUIView
|
||||
import org.mozilla.fenix.components.toolbar.ToolbarViewModel
|
||||
import org.mozilla.fenix.ext.getSpannable
|
||||
import org.mozilla.fenix.ext.requireComponents
|
||||
import org.mozilla.fenix.mvi.ActionBusFactory
|
||||
|
@ -42,7 +44,9 @@ import org.mozilla.fenix.mvi.getManagedEmitter
|
|||
import org.mozilla.fenix.search.awesomebar.AwesomeBarAction
|
||||
import org.mozilla.fenix.search.awesomebar.AwesomeBarChange
|
||||
import org.mozilla.fenix.search.awesomebar.AwesomeBarComponent
|
||||
import org.mozilla.fenix.search.awesomebar.AwesomeBarState
|
||||
import org.mozilla.fenix.search.awesomebar.AwesomeBarUIView
|
||||
import org.mozilla.fenix.search.awesomebar.AwesomeBarViewModel
|
||||
|
||||
@Suppress("TooManyFunctions")
|
||||
class SearchFragment : Fragment(), BackHandler {
|
||||
|
@ -67,15 +71,28 @@ class SearchFragment : Fragment(), BackHandler {
|
|||
|
||||
toolbarComponent = ToolbarComponent(
|
||||
view.toolbar_component_wrapper,
|
||||
this,
|
||||
ActionBusFactory.get(this),
|
||||
sessionId,
|
||||
isPrivate,
|
||||
SearchState(url, session?.searchTerms ?: "", isEditing = true),
|
||||
view.search_engine_icon
|
||||
view.search_engine_icon,
|
||||
FenixViewModelProvider.create(
|
||||
this,
|
||||
ToolbarViewModel::class.java
|
||||
) {
|
||||
ToolbarViewModel(SearchState(url, session?.searchTerms ?: "", isEditing = true))
|
||||
}
|
||||
)
|
||||
|
||||
awesomeBarComponent = AwesomeBarComponent(view.search_layout, this, ActionBusFactory.get(this))
|
||||
awesomeBarComponent = AwesomeBarComponent(
|
||||
view.search_layout,
|
||||
ActionBusFactory.get(this),
|
||||
FenixViewModelProvider.create(
|
||||
this,
|
||||
AwesomeBarViewModel::class.java
|
||||
) {
|
||||
AwesomeBarViewModel(AwesomeBarState("", false))
|
||||
}
|
||||
)
|
||||
ActionBusFactory.get(this).logMergedObservables()
|
||||
return view
|
||||
}
|
||||
|
|
|
@ -5,20 +5,15 @@ package org.mozilla.fenix.search.awesomebar
|
|||
file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.ViewModelProviders
|
||||
import io.reactivex.Observable
|
||||
import mozilla.components.browser.search.SearchEngine
|
||||
import org.mozilla.fenix.ext.logDebug
|
||||
import org.mozilla.fenix.mvi.ViewState
|
||||
import org.mozilla.fenix.mvi.Change
|
||||
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.UIComponentViewModel
|
||||
import org.mozilla.fenix.mvi.ViewState
|
||||
import org.mozilla.fenix.mvi.UIComponentViewModelBase
|
||||
import org.mozilla.fenix.mvi.UIComponentViewModelProvider
|
||||
|
||||
data class AwesomeBarState(
|
||||
val query: String,
|
||||
|
@ -40,42 +35,25 @@ sealed class AwesomeBarChange : Change {
|
|||
|
||||
class AwesomeBarComponent(
|
||||
private val container: ViewGroup,
|
||||
owner: Fragment,
|
||||
bus: ActionBusFactory,
|
||||
override var initialState: AwesomeBarState = AwesomeBarState("", false)
|
||||
viewModelProvider: UIComponentViewModelProvider<AwesomeBarState, AwesomeBarChange>
|
||||
) : UIComponent<AwesomeBarState, AwesomeBarAction, AwesomeBarChange>(
|
||||
owner,
|
||||
bus.getManagedEmitter(AwesomeBarAction::class.java),
|
||||
bus.getSafeManagedObservable(AwesomeBarChange::class.java)
|
||||
bus.getSafeManagedObservable(AwesomeBarChange::class.java),
|
||||
viewModelProvider
|
||||
) {
|
||||
override fun initView() = AwesomeBarUIView(container, actionEmitter, changesObservable)
|
||||
|
||||
override fun render(): Observable<AwesomeBarState> =
|
||||
ViewModelProviders.of(owner, AwesomeBarViewModel.Factory(initialState))
|
||||
.get(AwesomeBarViewModel::class.java).render(changesObservable, uiView)
|
||||
|
||||
init {
|
||||
render()
|
||||
bind()
|
||||
}
|
||||
}
|
||||
|
||||
class AwesomeBarViewModel(initialState: AwesomeBarState) :
|
||||
UIComponentViewModel<AwesomeBarState, AwesomeBarAction, AwesomeBarChange>(
|
||||
initialState,
|
||||
reducer
|
||||
) {
|
||||
|
||||
class Factory(
|
||||
private val initialState: AwesomeBarState
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T =
|
||||
AwesomeBarViewModel(initialState) as T
|
||||
}
|
||||
|
||||
class AwesomeBarViewModel(
|
||||
initialState: AwesomeBarState
|
||||
) : UIComponentViewModelBase<AwesomeBarState, AwesomeBarChange>(initialState, reducer) {
|
||||
companion object {
|
||||
val reducer: Reducer<AwesomeBarState, AwesomeBarChange> = { state, change ->
|
||||
logDebug("IN_REDUCER", change.toString())
|
||||
when (change) {
|
||||
is AwesomeBarChange.SearchShortcutEngineSelected ->
|
||||
state.copy(suggestionEngine = change.engine, showShortcutEnginePicker = false)
|
||||
|
|
|
@ -6,20 +6,17 @@ package org.mozilla.fenix.settings.quicksettings
|
|||
|
||||
import android.content.Context
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import io.reactivex.Observable
|
||||
import mozilla.components.feature.sitepermissions.SitePermissions
|
||||
import mozilla.components.support.ktx.kotlin.toUri
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.mvi.ViewState
|
||||
import org.mozilla.fenix.mvi.Change
|
||||
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.UIComponentViewModel
|
||||
import org.mozilla.fenix.mvi.UIComponentViewModelBase
|
||||
import org.mozilla.fenix.mvi.UIComponentViewModelProvider
|
||||
import org.mozilla.fenix.mvi.UIView
|
||||
import org.mozilla.fenix.mvi.ViewState
|
||||
import org.mozilla.fenix.settings.PhoneFeature
|
||||
import org.mozilla.fenix.settings.toStatus
|
||||
import org.mozilla.fenix.settings.toggle
|
||||
|
@ -27,25 +24,19 @@ import org.mozilla.fenix.utils.Settings
|
|||
|
||||
class QuickSettingsComponent(
|
||||
private val container: ViewGroup,
|
||||
owner: Fragment,
|
||||
bus: ActionBusFactory,
|
||||
override var initialState: QuickSettingsState
|
||||
viewModelProvider: UIComponentViewModelProvider<QuickSettingsState, QuickSettingsChange>
|
||||
) : UIComponent<QuickSettingsState, QuickSettingsAction, QuickSettingsChange>(
|
||||
owner,
|
||||
bus.getManagedEmitter(QuickSettingsAction::class.java),
|
||||
bus.getSafeManagedObservable(QuickSettingsChange::class.java)
|
||||
bus.getSafeManagedObservable(QuickSettingsChange::class.java),
|
||||
viewModelProvider
|
||||
) {
|
||||
override fun initView(): UIView<QuickSettingsState, QuickSettingsAction, QuickSettingsChange> {
|
||||
return QuickSettingsUIView(container, actionEmitter, changesObservable, container)
|
||||
}
|
||||
|
||||
override fun render(): Observable<QuickSettingsState> =
|
||||
ViewModelProvider(owner, QuickSettingsViewModel.Factory(initialState)).get(
|
||||
QuickSettingsViewModel::class.java
|
||||
).render(changesObservable, uiView)
|
||||
|
||||
init {
|
||||
render()
|
||||
bind()
|
||||
}
|
||||
|
||||
fun toggleSitePermission(
|
||||
|
@ -121,20 +112,9 @@ sealed class QuickSettingsChange : Change {
|
|||
data class Stored(val phoneFeature: PhoneFeature, val sitePermissions: SitePermissions?) : QuickSettingsChange()
|
||||
}
|
||||
|
||||
class QuickSettingsViewModel(initialState: QuickSettingsState) :
|
||||
UIComponentViewModel<QuickSettingsState, QuickSettingsAction, QuickSettingsChange>(
|
||||
initialState,
|
||||
reducer
|
||||
) {
|
||||
|
||||
class Factory(
|
||||
private val initialState: QuickSettingsState
|
||||
) : ViewModelProvider.Factory {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T =
|
||||
QuickSettingsViewModel(initialState) as T
|
||||
}
|
||||
|
||||
class QuickSettingsViewModel(
|
||||
initialState: QuickSettingsState
|
||||
) : UIComponentViewModelBase<QuickSettingsState, QuickSettingsChange>(initialState, reducer) {
|
||||
companion object {
|
||||
val reducer: (QuickSettingsState, QuickSettingsChange) -> QuickSettingsState = { state, change ->
|
||||
when (change) {
|
||||
|
|
|
@ -26,6 +26,7 @@ import kotlinx.coroutines.launch
|
|||
import mozilla.components.browser.session.Session
|
||||
import mozilla.components.feature.sitepermissions.SitePermissions
|
||||
import mozilla.components.support.ktx.kotlin.toUri
|
||||
import org.mozilla.fenix.FenixViewModelProvider
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.browser.BrowserFragment
|
||||
|
@ -74,15 +75,23 @@ class QuickSettingsSheetDialogFragment : AppCompatDialogFragment(), CoroutineSco
|
|||
val rootView = inflateRootView(container)
|
||||
requireComponents.core.sessionManager.findSessionById(sessionId)?.register(sessionObserver, view = rootView)
|
||||
quickSettingsComponent = QuickSettingsComponent(
|
||||
rootView as NestedScrollView, this, ActionBusFactory.get(this),
|
||||
QuickSettingsState(
|
||||
QuickSettingsState.Mode.Normal(
|
||||
url,
|
||||
isSecured,
|
||||
isTrackingProtectionOn,
|
||||
sitePermissions
|
||||
rootView as NestedScrollView,
|
||||
ActionBusFactory.get(this),
|
||||
FenixViewModelProvider.create(
|
||||
this,
|
||||
QuickSettingsViewModel::class.java
|
||||
) {
|
||||
QuickSettingsViewModel(
|
||||
QuickSettingsState(
|
||||
QuickSettingsState.Mode.Normal(
|
||||
url,
|
||||
isSecured,
|
||||
isTrackingProtectionOn,
|
||||
sitePermissions
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
return rootView
|
||||
}
|
||||
|
|
|
@ -4,10 +4,7 @@
|
|||
|
||||
package org.mozilla.fenix.library.bookmarks
|
||||
|
||||
import android.view.ViewGroup
|
||||
import io.mockk.MockKAnnotations
|
||||
import io.mockk.mockk
|
||||
import io.mockk.spyk
|
||||
import io.reactivex.Observer
|
||||
import io.reactivex.observers.TestObserver
|
||||
import mozilla.appservices.places.BookmarkRoot
|
||||
|
@ -16,13 +13,12 @@ import mozilla.components.concept.storage.BookmarkNodeType
|
|||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.TestUtils
|
||||
import org.mozilla.fenix.mvi.ActionBusFactory
|
||||
import org.mozilla.fenix.mvi.UIView
|
||||
import org.mozilla.fenix.TestUtils.bus
|
||||
import org.mozilla.fenix.mvi.getManagedEmitter
|
||||
|
||||
class BookmarkComponentTest {
|
||||
class BookmarkViewModelTest {
|
||||
|
||||
private lateinit var bookmarkComponent: TestBookmarkComponent
|
||||
private lateinit var bookmarkViewModel: BookmarkViewModel
|
||||
private lateinit var bookmarkObserver: TestObserver<BookmarkState>
|
||||
private lateinit var emitter: Observer<BookmarkChange>
|
||||
|
||||
|
@ -31,11 +27,10 @@ class BookmarkComponentTest {
|
|||
MockKAnnotations.init(this)
|
||||
TestUtils.setRxSchedulers()
|
||||
|
||||
bookmarkComponent = spyk(
|
||||
TestBookmarkComponent(mockk(), TestUtils.bus),
|
||||
recordPrivateCalls = true
|
||||
)
|
||||
bookmarkObserver = bookmarkComponent.render().test()
|
||||
bookmarkViewModel = BookmarkViewModel.create()
|
||||
bookmarkObserver = bookmarkViewModel.state.test()
|
||||
bus.getSafeManagedObservable(BookmarkChange::class.java)
|
||||
.subscribe(bookmarkViewModel.changes::onNext)
|
||||
emitter = TestUtils.owner.getManagedEmitter()
|
||||
}
|
||||
|
||||
|
@ -84,12 +79,4 @@ class BookmarkComponentTest {
|
|||
BookmarkState(tree - itemToSelect.guid, BookmarkState.Mode.Normal)
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
class TestBookmarkComponent(container: ViewGroup, bus: ActionBusFactory) :
|
||||
BookmarkComponent(container, mockk(relaxed = true), bus) {
|
||||
|
||||
override val uiView: UIView<BookmarkState, BookmarkAction, BookmarkChange>
|
||||
get() = mockk(relaxed = true)
|
||||
}
|
||||
}
|
|
@ -4,10 +4,7 @@
|
|||
|
||||
package org.mozilla.fenix.library.history
|
||||
|
||||
import android.view.ViewGroup
|
||||
import io.mockk.MockKAnnotations
|
||||
import io.mockk.mockk
|
||||
import io.mockk.spyk
|
||||
import io.reactivex.Observer
|
||||
import io.reactivex.observers.TestObserver
|
||||
import org.junit.Before
|
||||
|
@ -15,13 +12,11 @@ import org.junit.Test
|
|||
import org.mozilla.fenix.TestUtils.bus
|
||||
import org.mozilla.fenix.TestUtils.owner
|
||||
import org.mozilla.fenix.TestUtils.setRxSchedulers
|
||||
import org.mozilla.fenix.mvi.ActionBusFactory
|
||||
import org.mozilla.fenix.mvi.UIView
|
||||
import org.mozilla.fenix.mvi.getManagedEmitter
|
||||
|
||||
class HistoryComponentTest {
|
||||
class HistoryViewModelTest {
|
||||
|
||||
private lateinit var historyComponent: TestHistoryComponent
|
||||
private lateinit var historyViewModel: HistoryViewModel
|
||||
private lateinit var historyObserver: TestObserver<HistoryState>
|
||||
private lateinit var emitter: Observer<HistoryChange>
|
||||
|
||||
|
@ -30,11 +25,11 @@ class HistoryComponentTest {
|
|||
MockKAnnotations.init(this)
|
||||
setRxSchedulers()
|
||||
|
||||
historyComponent = spyk(
|
||||
TestHistoryComponent(mockk(), bus),
|
||||
recordPrivateCalls = true
|
||||
)
|
||||
historyObserver = historyComponent.render().test()
|
||||
historyViewModel = HistoryViewModel.create()
|
||||
historyObserver = historyViewModel.state.test()
|
||||
bus.getSafeManagedObservable(HistoryChange::class.java)
|
||||
.subscribe(historyViewModel.changes::onNext)
|
||||
|
||||
emitter = owner.getManagedEmitter()
|
||||
}
|
||||
|
||||
|
@ -97,12 +92,4 @@ class HistoryComponentTest {
|
|||
HistoryState(historyItems, HistoryState.Mode.Normal)
|
||||
)
|
||||
}
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
class TestHistoryComponent(container: ViewGroup, bus: ActionBusFactory) :
|
||||
HistoryComponent(container, mockk(relaxed = true), bus) {
|
||||
|
||||
override val uiView: UIView<HistoryState, HistoryAction, HistoryChange>
|
||||
get() = mockk(relaxed = true)
|
||||
}
|
||||
}
|
|
@ -4,60 +4,65 @@
|
|||
|
||||
package org.mozilla.fenix.mvi
|
||||
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.ViewModel
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.Observer
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.rxkotlin.withLatestFrom
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import io.reactivex.subjects.BehaviorSubject
|
||||
import io.reactivex.subjects.PublishSubject
|
||||
|
||||
interface UIComponentViewModel<S : ViewState, C : Change> {
|
||||
val changes: Observer<C>
|
||||
val state: Observable<S>
|
||||
}
|
||||
|
||||
interface UIComponentViewModelProvider<S : ViewState, C : Change> {
|
||||
fun fetchViewModel(): UIComponentViewModel<S, C>
|
||||
}
|
||||
|
||||
abstract class UIComponent<S : ViewState, A : Action, C : Change>(
|
||||
protected val owner: Fragment,
|
||||
protected val actionEmitter: Observer<A>,
|
||||
protected val changesObservable: Observable<C>
|
||||
protected val changesObservable: Observable<C>,
|
||||
private val viewModelProvider: UIComponentViewModelProvider<S, C>
|
||||
) {
|
||||
|
||||
abstract var initialState: S
|
||||
|
||||
open val uiView: UIView<S, A, C> by lazy { initView() }
|
||||
|
||||
abstract fun initView(): UIView<S, A, C>
|
||||
open fun getContainerId() = uiView.containerId
|
||||
abstract fun render(): Observable<S>
|
||||
|
||||
fun bind(): CompositeDisposable {
|
||||
val compositeDisposable = CompositeDisposable()
|
||||
val viewModel = viewModelProvider.fetchViewModel()
|
||||
|
||||
compositeDisposable.add(changesObservable.subscribe(viewModel.changes::onNext))
|
||||
compositeDisposable.add(viewModel.state.subscribe(uiView.updateView()))
|
||||
|
||||
return compositeDisposable
|
||||
}
|
||||
}
|
||||
|
||||
open class UIComponentViewModel<S : ViewState, A : Action, C : Change>(
|
||||
abstract class UIComponentViewModelBase<S : ViewState, C : Change>(
|
||||
initialState: S,
|
||||
private val reducer: Reducer<S, C>
|
||||
) : ViewModel() {
|
||||
reducer: Reducer<S, C>
|
||||
) : ViewModel(), UIComponentViewModel<S, C> {
|
||||
|
||||
private var currentState: S = initialState
|
||||
private var statesDisposable: Disposable? = null
|
||||
final override val changes: Observer<C>
|
||||
private var _state: BehaviorSubject<S> = BehaviorSubject.createDefault(initialState)
|
||||
override val state: Observable<S>
|
||||
get() = _state
|
||||
|
||||
/**
|
||||
* Render the ViewState to the View through the Reducer
|
||||
*/
|
||||
fun render(changesObservable: Observable<C>, uiView: UIView<S, A, C>): Observable<S> {
|
||||
val statesObservable = internalRender(changesObservable, reducer)
|
||||
statesDisposable = statesObservable
|
||||
.subscribe(uiView.updateView())
|
||||
return statesObservable
|
||||
}
|
||||
init {
|
||||
changes = PublishSubject.create<C>()
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
protected fun internalRender(changesObservable: Observable<C>, reducer: Reducer<S, C>): Observable<S> =
|
||||
changesObservable
|
||||
.scan(currentState, reducer)
|
||||
changes
|
||||
.withLatestFrom(_state)
|
||||
.map { reducer(it.second, it.first) }
|
||||
.distinctUntilChanged()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.replay(1)
|
||||
.autoConnect(0)
|
||||
.doOnNext { currentState = it }
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
statesDisposable?.dispose()
|
||||
.subscribe(_state)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue