Skip to content

Commit

Permalink
Removing FlowFactory and Flow. (#380)
Browse files Browse the repository at this point in the history
* Removing FlowFactory and Flow.

* Refactoring FragmentFlowStore.
  • Loading branch information
Laimiux authored Sep 4, 2024
1 parent 1b03419 commit d60112a
Show file tree
Hide file tree
Showing 23 changed files with 206 additions and 782 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- Enable fine-grained control of dispatching via `Plugin.defaultDispatcher`, `RuntimeConfig.defaultDispatcher`, `Transition.ExecutionType` and `Effect.Type`.
- Remove `RxStream` type alias.
- Replace `implementation` function with a value
- Removed `FlowFactory` and `Flow`

## [0.7.1] - June 28, 2022
- **Breaking**: Rename `FragmentBindingBuilder` to `FragmentStoreBuilder`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.google.common.truth.Truth.assertThat
import com.instacart.formula.android.Feature
import com.instacart.formula.android.FeatureFactory
import com.instacart.formula.android.ViewFactory
import com.instacart.formula.android.events.ActivityResult
import com.instacart.formula.test.TestFragmentActivity
Expand All @@ -29,26 +30,30 @@ class FragmentAndroidEventTest {
initialContract = TestLifecycleKey()
},
contracts = {

bind<TestLifecycleKey> { _, _ ->
Feature(
state = activityResults().flatMap {
activityResults.add(it)
Observable.empty()
},
viewFactory = ViewFactory.fromLayout(R.layout.test_empty_layout) {
featureView { }
}
)
val featureFactory = object : FeatureFactory<Unit, TestLifecycleKey> {
override fun initialize(dependencies: Unit, key: TestLifecycleKey): Feature {
return Feature(
state = activityResults().flatMap {
activityResults.add(it)
Observable.empty()
},
viewFactory = ViewFactory.fromLayout(R.layout.test_empty_layout) {
featureView { }
}
)
}
}

bind(featureFactory)
}
)
}
}
},
cleanUp = {
activityResults.clear()
})
}
)

private val activityRule = ActivityScenarioRule(TestFragmentActivity::class.java)

Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,24 +1,18 @@
package com.instacart.formula.android

import com.instacart.formula.Evaluation
import com.instacart.formula.Formula
import com.instacart.formula.RuntimeConfig
import com.instacart.formula.Snapshot
import com.instacart.formula.android.internal.Binding
import com.instacart.formula.android.events.FragmentLifecycleEvent
import com.instacart.formula.android.internal.FeatureObservableAction
import com.instacart.formula.android.internal.FragmentFlowStoreFormula
import com.instacart.formula.android.utils.MainThreadDispatcher
import com.instacart.formula.rxjava3.RxAction
import com.instacart.formula.rxjava3.toObservable
import com.jakewharton.rxrelay3.PublishRelay
import io.reactivex.rxjava3.core.Observable

/**
* A FragmentFlowStore is responsible for managing the state of multiple [FragmentKey] instances.
*/
class FragmentFlowStore @PublishedApi internal constructor(
private val root: Binding<Unit>
) : Formula<FragmentEnvironment, FragmentFlowState, FragmentFlowState>() {
private val formula: FragmentFlowStoreFormula<*>,
) {
companion object {
inline fun init(
crossinline init: FragmentStoreBuilder<Unit>.() -> Unit
Expand All @@ -30,124 +24,24 @@ class FragmentFlowStore @PublishedApi internal constructor(
rootComponent: Component,
crossinline contracts: FragmentStoreBuilder<Component>.() -> Unit
): FragmentFlowStore {
val factory: (Unit) -> DisposableScope<Component> = {
DisposableScope(component = rootComponent, onDispose = {})
}

val bindings = FragmentStoreBuilder.build(contracts)
val root = Binding.composite(factory, bindings)
return FragmentFlowStore(root)
val formula = FragmentFlowStoreFormula(rootComponent, bindings)
return FragmentFlowStore(formula)
}
}


private val lifecycleEvents = PublishRelay.create<FragmentLifecycleEvent>()
private val visibleContractEvents = PublishRelay.create<FragmentId>()
private val hiddenContractEvents = PublishRelay.create<FragmentId>()

private val lifecycleEventStream = RxAction.fromObservable { lifecycleEvents }
private val visibleContractEventStream = RxAction.fromObservable { visibleContractEvents }
private val hiddenContractEventStream = RxAction.fromObservable { hiddenContractEvents }

internal fun onLifecycleEffect(event: FragmentLifecycleEvent) {
lifecycleEvents.accept(event)
formula.onLifecycleEffect(event)
}

internal fun onVisibilityChanged(contract: FragmentId, visible: Boolean) {
if (visible) {
visibleContractEvents.accept(contract)
} else {
hiddenContractEvents.accept(contract)
}
}

override fun initialState(input: FragmentEnvironment): FragmentFlowState = FragmentFlowState()

override fun Snapshot<FragmentEnvironment, FragmentFlowState>.evaluate(): Evaluation<FragmentFlowState> {
val rootInput = Binding.Input(
environment = input,
component = Unit,
activeFragments = state.activeIds,
onInitializeFeature = context.onEvent { event ->
val features = state.features.plus(event.id to event)
transition(state.copy(features = features))
}
)
root.bind(context, rootInput)

return Evaluation(
output = state,
actions = context.actions {
lifecycleEventStream.onEvent { event ->
val fragmentId = event.fragmentId
when (event) {
is FragmentLifecycleEvent.Removed -> {
val updated = state.copy(
activeIds = state.activeIds.minus(fragmentId),
states = state.states.minus(fragmentId),
features = state.features.minus(fragmentId)
)
transition(updated)
}
is FragmentLifecycleEvent.Added -> {
if (!state.activeIds.contains(fragmentId)) {
if (root.binds(fragmentId.key)) {
val updated = state.copy(activeIds = state.activeIds.plus(fragmentId))
transition(updated)
} else {
val updated = state.copy(
activeIds = state.activeIds.plus(fragmentId),
features = state.features.plus(fragmentId to FeatureEvent.MissingBinding(fragmentId))
)
transition(updated)
}
} else {
none()
}
}
}
}

visibleContractEventStream.onEvent {
if (state.visibleIds.contains(it)) {
// TODO: should we log this duplicate visibility event?
none()
} else {
transition(state.copy(visibleIds = state.visibleIds.plus(it)))
}
}

hiddenContractEventStream.onEvent {
transition(state.copy(visibleIds = state.visibleIds.minus(it)))
}

state.features.entries.forEach { entry ->
val fragmentId = entry.key
val feature = (entry.value as? FeatureEvent.Init)?.feature
if (feature != null) {
val action = FeatureObservableAction(
fragmentEnvironment = input,
fragmentId = fragmentId,
feature = feature,
)
action.onEvent {
if (state.activeIds.contains(fragmentId)) {
val keyState = FragmentState(fragmentId.key, it)
transition(state.copy(states = state.states.plus(fragmentId to keyState)))
} else {
none()
}
}
}
}
}
)
formula.onVisibilityChanged(contract, visible)
}

internal fun state(environment: FragmentEnvironment): Observable<FragmentFlowState> {
val config = RuntimeConfig(
defaultDispatcher = MainThreadDispatcher(),
)
return toObservable(environment, config)
return formula.toObservable(environment, config)
}
}
Loading

0 comments on commit d60112a

Please sign in to comment.