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

For #1994: Re-architect state handling code #2382

Merged
merged 2 commits into from
May 9, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- #1429 - Updated site permissions ui for MVP
- #1599 - Fixed a crash creating a bookmark for a custom tab
- #1414 - Fixed site permissions settings getting reset in Android 6.
- #1994 - Made app state persist better when rotating the screen

### Removed
1 change: 1 addition & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,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.androidx_core
implementation Deps.androidx_transition

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,63 @@ 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(): Observable<CollectionCreationState> =
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,12 @@ class ToolbarComponent(
engineIconView
)

override fun render(): Observable<SearchState> =
ViewModelProviders.of(owner, ToolbarViewModel.Factory(initialState, changesObservable))
.get(ToolbarViewModel::class.java).render(uiView)

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

Expand Down Expand Up @@ -95,3 +98,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,24 @@ 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(): Observable<ExceptionsState> =
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 +55,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