Skip to content
This repository has been archived by the owner on Feb 20, 2023. It is now read-only.

Commit

Permalink
Re-architect state handling code
Browse files Browse the repository at this point in the history
  • Loading branch information
colintheshots committed May 9, 2019
1 parent 75ccce2 commit 93ca6ff
Show file tree
Hide file tree
Showing 26 changed files with 557 additions and 206 deletions.
1 change: 1 addition & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,7 @@ dependencies {
implementation Deps.androidx_navigation_ui
implementation Deps.androidx_recyclerview
implementation Deps.androidx_lifecycle_viewmodel_ktx
implementation Deps.androidx_lifecycle_viewmodel_ss

implementation Deps.autodispose

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ class BrowserFragment : Fragment(), BackHandler, CoroutineScope,

toolbarComponent = ToolbarComponent(
view.browserLayout,
this,
ActionBusFactory.get(this), customTabSessionId,
(activity as HomeActivity).browsingModeManager.isPrivate,
SearchState("", getSessionById()?.searchTerms ?: "", isEditing = false),
Expand All @@ -156,6 +157,7 @@ class BrowserFragment : Fragment(), BackHandler, CoroutineScope,

QuickActionComponent(
view.nestedScrollQuickAction,
this,
ActionBusFactory.get(this),
QuickActionState(
readable = getSessionById()?.readerable ?: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +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.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

sealed class SaveCollectionStep {
Expand Down Expand Up @@ -51,34 +56,64 @@ sealed class CollectionCreationAction : Action {

class CollectionCreationComponent(
private val container: ViewGroup,
owner: Fragment,
bus: ActionBusFactory,
override var initialState: CollectionCreationState = CollectionCreationState()
) : UIComponent<CollectionCreationState, CollectionCreationAction, CollectionCreationChange>(
owner,
bus.getManagedEmitter(CollectionCreationAction::class.java),
bus.getSafeManagedObservable(CollectionCreationChange::class.java)
) {
override val reducer: Reducer<CollectionCreationState, CollectionCreationChange> =
{ state, change ->
when (change) {
is CollectionCreationChange.AddAllTabs -> state.copy(selectedTabs = state.tabs.toSet())
is CollectionCreationChange.TabListChange -> state.copy(tabs = change.tabs)
is CollectionCreationChange.TabAdded -> {
val selectedTabs = state.selectedTabs + setOf(change.tab)
state.copy(selectedTabs = selectedTabs)
}
is CollectionCreationChange.TabRemoved -> {
val selectedTabs = state.selectedTabs - setOf(change.tab)
state.copy(selectedTabs = selectedTabs)
}
is CollectionCreationChange.StepChanged -> {
state.copy(saveCollectionStep = change.saveCollectionStep)
}
}
}

override fun initView() = CollectionCreationUIView(container, actionEmitter, changesObservable)

override fun render() {
ViewModelProvider(owner, CollectionCreationViewModel.Factory(initialState, changesObservable)).get(
CollectionCreationViewModel::class.java
).render(uiView)
}

init {
render(reducer)
render()
}
}

class CollectionCreationViewModel(
initialState: CollectionCreationState,
changesObservable: Observable<CollectionCreationChange>
) :
UIComponentViewModel<CollectionCreationState, CollectionCreationAction, CollectionCreationChange>(
initialState,
changesObservable,
reducer
) {

class Factory(
private val initialState: CollectionCreationState,
private val changesObservable: Observable<CollectionCreationChange>
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>): T =
CollectionCreationViewModel(initialState, changesObservable) as T
}

companion object {
val reducer: Reducer<CollectionCreationState, CollectionCreationChange> =
{ state, change ->
when (change) {
is CollectionCreationChange.AddAllTabs -> state.copy(selectedTabs = state.tabs.toSet())
is CollectionCreationChange.TabListChange -> state.copy(tabs = change.tabs)
is CollectionCreationChange.TabAdded -> {
val selectedTabs = state.selectedTabs + setOf(change.tab)
state.copy(selectedTabs = selectedTabs)
}
is CollectionCreationChange.TabRemoved -> {
val selectedTabs = state.selectedTabs - setOf(change.tab)
state.copy(selectedTabs = selectedTabs)
}
is CollectionCreationChange.StepChanged -> {
state.copy(saveCollectionStep = change.saveCollectionStep)
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class CreateCollectionFragment : DialogFragment() {

collectionCreationComponent = CollectionCreationComponent(
view.create_collection_wrapper,
this,
ActionBusFactory.get(this),
CollectionCreationState(
tabs = tabs,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ 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
Expand All @@ -17,32 +22,26 @@ 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

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
) :
UIComponent<SearchState, SearchAction, SearchChange>(
owner,
bus.getManagedEmitter(SearchAction::class.java),
bus.getSafeManagedObservable(SearchChange::class.java)
) {

fun getView(): BrowserToolbar = uiView.toolbar

override val reducer: Reducer<SearchState, SearchChange> = { state, change ->
when (change) {
is SearchChange.ToolbarClearedFocus -> state.copy(focused = false)
is SearchChange.ToolbarRequestedFocus -> state.copy(focused = true)
is SearchChange.SearchShortcutEngineSelected ->
state.copy(engine = change.engine)
}
}

override fun initView() = ToolbarUIView(
sessionId,
isPrivate,
Expand All @@ -52,8 +51,13 @@ class ToolbarComponent(
engineIconView
)

override fun render() {
ViewModelProviders.of(owner, ToolbarViewModel.Factory(initialState, changesObservable))
.get(ToolbarViewModel::class.java).render(uiView)
}

init {
render(reducer)
render()
applyTheme()
}

Expand Down Expand Up @@ -95,3 +99,27 @@ sealed class SearchChange : Change {
object ToolbarClearedFocus : SearchChange()
data class SearchShortcutEngineSelected(val engine: SearchEngine) : SearchChange()
}

class ToolbarViewModel(initialState: SearchState, changesObservable: Observable<SearchChange>) :
UIComponentViewModel<SearchState, SearchAction, SearchChange>(initialState, changesObservable, 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, changesObservable) as T
}

companion object {
val reducer: Reducer<SearchState, SearchChange> = { state, change ->
when (change) {
is SearchChange.ToolbarClearedFocus -> state.copy(focused = false)
is SearchChange.ToolbarRequestedFocus -> state.copy(focused = true)
is SearchChange.SearchShortcutEngineSelected ->
state.copy(engine = change.engine)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class ToolbarUIView(
init {
val sessionManager = view.context.components.core.sessionManager
val session = sessionId?.let { sessionManager.findSessionById(it) }
?: sessionManager.selectedSession
?: sessionManager.selectedSession

view.apply {
elevation = resources.pxToDp(TOOLBAR_ELEVATION).toFloat()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,16 @@
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.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.test.Mockable

Expand All @@ -16,24 +22,25 @@ data class ExceptionsItem(val url: String)
@Mockable
class ExceptionsComponent(
private val container: ViewGroup,
owner: Fragment,
bus: ActionBusFactory,
override var initialState: ExceptionsState = ExceptionsState(emptyList())
) :
UIComponent<ExceptionsState, ExceptionsAction, ExceptionsChange>(
owner,
bus.getManagedEmitter(ExceptionsAction::class.java),
bus.getSafeManagedObservable(ExceptionsChange::class.java)
) {

override val reducer: (ExceptionsState, ExceptionsChange) -> ExceptionsState = { state, change ->
when (change) {
is ExceptionsChange.Change -> state.copy(items = change.list)
}
override fun render() {
ViewModelProviders.of(owner, ExceptionsViewModel.Factory(initialState, changesObservable))
.get(ExceptionsViewModel::class.java).render(uiView)
}

override fun initView() = ExceptionsUIView(container, actionEmitter, changesObservable)

init {
render(reducer)
render()
}
}

Expand All @@ -49,3 +56,28 @@ sealed class ExceptionsAction : Action {
sealed class ExceptionsChange : Change {
data class Change(val list: List<ExceptionsItem>) : ExceptionsChange()
}

class ExceptionsViewModel(initialState: ExceptionsState, changesObservable: Observable<ExceptionsChange>) :
UIComponentViewModel<ExceptionsState, ExceptionsAction, ExceptionsChange>(
initialState,
changesObservable,
reducer
) {

class Factory(
private val initialState: ExceptionsState,
private val changesObservable: Observable<ExceptionsChange>
) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel?> create(modelClass: Class<T>): T =
ExceptionsViewModel(initialState, changesObservable) as T
}

companion object {
val reducer: (ExceptionsState, ExceptionsChange) -> ExceptionsState = { state, change ->
when (change) {
is ExceptionsChange.Change -> state.copy(items = change.list)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ 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())
)
Expand Down
1 change: 1 addition & 0 deletions app/src/main/java/org/mozilla/fenix/home/HomeFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ class HomeFragment : Fragment(), CoroutineScope {

sessionControlComponent = SessionControlComponent(
view.homeLayout,
this,
bus,
SessionControlState(listOf(), listOf(), mode)
)
Expand Down
Loading

0 comments on commit 93ca6ff

Please sign in to comment.