Skip to content

Commit 173cc54

Browse files
committed
Explore FragmentStore changes.
1 parent 8a2eac2 commit 173cc54

File tree

4 files changed

+105
-142
lines changed

4 files changed

+105
-142
lines changed

formula-android/src/main/java/com/instacart/formula/android/FragmentStore.kt

Lines changed: 104 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
package com.instacart.formula.android
22

3-
import com.instacart.formula.RuntimeConfig
43
import com.instacart.formula.android.events.FragmentLifecycleEvent
54
import com.instacart.formula.android.internal.FeatureComponent
65
import com.instacart.formula.android.internal.Features
7-
import com.instacart.formula.android.internal.FragmentStoreFormula
86
import com.instacart.formula.android.utils.MainThreadDispatcher
9-
import com.instacart.formula.rxjava3.toObservable
7+
import com.jakewharton.rxrelay3.PublishRelay
108
import io.reactivex.rxjava3.core.Observable
9+
import io.reactivex.rxjava3.disposables.Disposable
1110

1211
/**
1312
* A FragmentStore is responsible for managing the state of multiple [FragmentKey] instances.
1413
*/
1514
class FragmentStore @PublishedApi internal constructor(
16-
private val formula: FragmentStoreFormula,
15+
private val featureComponent: FeatureComponent<*>,
1716
) {
1817
companion object {
1918
val EMPTY = init { }
@@ -37,23 +36,116 @@ class FragmentStore @PublishedApi internal constructor(
3736
features: Features<Component>
3837
): FragmentStore {
3938
val featureComponent = FeatureComponent(component, features.bindings)
40-
val formula = FragmentStoreFormula(featureComponent)
41-
return FragmentStore(formula)
39+
return FragmentStore(featureComponent)
4240
}
4341
}
4442

43+
@Volatile private var disposed = false
44+
private val dispatcher = MainThreadDispatcher()
45+
private val updateRelay = PublishRelay.create<Unit>()
46+
private var state = FragmentState()
47+
private var runningFeatures = mutableMapOf<FragmentId, Disposable>()
48+
49+
private lateinit var fragmentEnvironment: FragmentEnvironment
50+
4551
internal fun onLifecycleEffect(event: FragmentLifecycleEvent) {
46-
formula.onLifecycleEffect(event)
52+
val fragmentId = event.fragmentId
53+
when (event) {
54+
is FragmentLifecycleEvent.Added -> handleNewFragment(fragmentId)
55+
is FragmentLifecycleEvent.Removed -> handleRemoveFragment(fragmentId)
56+
}
4757
}
4858

4959
internal fun onVisibilityChanged(fragmentId: FragmentId, visible: Boolean) {
50-
formula.onVisibilityChanged(fragmentId, visible)
60+
dispatcher.dispatch {
61+
val visibleIds = if (visible) {
62+
state.visibleIds.plus(fragmentId)
63+
} else {
64+
state.visibleIds.minus(fragmentId)
65+
}
66+
state = state.copy(visibleIds = visibleIds)
67+
updateRelay.accept(Unit)
68+
}
5169
}
5270

71+
// TODO: should not be an observable.
5372
internal fun state(environment: FragmentEnvironment): Observable<FragmentState> {
54-
val config = RuntimeConfig(
55-
defaultDispatcher = MainThreadDispatcher(),
56-
)
57-
return formula.toObservable(environment, config)
73+
// TODO: should be set differently
74+
fragmentEnvironment = environment
75+
return updateRelay.startWithItem(Unit).map { state }.distinctUntilChanged()
76+
}
77+
78+
fun dispose() {
79+
disposed = true
80+
81+
dispatcher.dispatch {
82+
for (running in runningFeatures) {
83+
running.value.dispose()
84+
}
85+
86+
runningFeatures.clear()
87+
}
88+
}
89+
90+
private fun handleNewFragment(fragmentId: FragmentId) {
91+
dispatcher.dispatch {
92+
if (disposed) return@dispatch
93+
if (state.activeIds.contains(fragmentId)) return@dispatch
94+
95+
val featureEvent = featureComponent.init(fragmentEnvironment, fragmentId)
96+
state = state.copy(
97+
activeIds = state.activeIds.plus(fragmentId),
98+
features = state.features.plus(featureEvent.id to featureEvent)
99+
)
100+
101+
runFeature(fragmentEnvironment, featureEvent)
102+
103+
updateRelay.accept(Unit)
104+
}
105+
}
106+
107+
private fun handleRemoveFragment(fragmentId: FragmentId) {
108+
dispatcher.dispatch {
109+
if (disposed) return@dispatch
110+
111+
runningFeatures.remove(fragmentId)?.dispose()
112+
113+
if (state.activeIds.contains(fragmentId)) {
114+
state = state.copy(
115+
activeIds = state.activeIds.minus(fragmentId),
116+
features = state.features.minus(fragmentId),
117+
outputs = state.outputs.minus(fragmentId),
118+
)
119+
updateRelay.accept(Unit)
120+
}
121+
}
122+
}
123+
124+
private fun runFeature(environment: FragmentEnvironment, event: FeatureEvent) {
125+
val fragmentId = event.id
126+
val feature = (event as? FeatureEvent.Init)?.feature
127+
if (feature != null) {
128+
val observable = feature.stateObservable.onErrorResumeNext {
129+
environment.onScreenError(fragmentId.key, it)
130+
Observable.empty()
131+
}
132+
133+
runningFeatures[fragmentId] = observable.subscribe {
134+
publishUpdate(fragmentId, it)
135+
}
136+
}
137+
}
138+
139+
private fun publishUpdate(fragmentId: FragmentId, output: Any) {
140+
dispatcher.dispatch {
141+
if (!state.activeIds.contains(fragmentId)) {
142+
return@dispatch
143+
}
144+
145+
val keyState = FragmentOutput(fragmentId.key, output)
146+
state = state.copy(outputs = state.outputs.plus(fragmentId to keyState))
147+
148+
updateRelay.accept(Unit)
149+
}
58150
}
59151
}

formula-android/src/main/java/com/instacart/formula/android/internal/ActivityManager.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ internal class ActivityManager<Activity : FragmentActivity>(
9393

9494
fun dispose() {
9595
stateSubscription.dispose()
96+
store.fragmentStore.dispose()
9697
store.onCleared?.invoke()
9798
}
9899

formula-android/src/main/java/com/instacart/formula/android/internal/FeatureObservableAction.kt

Lines changed: 0 additions & 28 deletions
This file was deleted.

formula-android/src/main/java/com/instacart/formula/android/internal/FragmentStoreFormula.kt

Lines changed: 0 additions & 102 deletions
This file was deleted.

0 commit comments

Comments
 (0)