Skip to content

Commit

Permalink
Rename FragmentStoreBuilder to FeaturesBuilder.
Browse files Browse the repository at this point in the history
  • Loading branch information
Laimiux committed Sep 4, 2024
1 parent d60112a commit 49b6596
Show file tree
Hide file tree
Showing 16 changed files with 125 additions and 135 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
- 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`
- **Breaking**: Removed `FlowFactory` and `Flow`
- **Breaking**: Replace `FragmentStoreBuilder` with `FeaturesBuilder`

## [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 @@ -6,6 +6,7 @@ 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.FragmentFlowStore
import com.instacart.formula.android.ViewFactory
import com.instacart.formula.android.events.ActivityResult
import com.instacart.formula.test.TestFragmentActivity
Expand All @@ -29,7 +30,7 @@ class FragmentAndroidEventTest {
configureActivity = {
initialContract = TestLifecycleKey()
},
contracts = {
fragmentStore = FragmentFlowStore.init {
val featureFactory = object : FeatureFactory<Unit, TestLifecycleKey> {
override fun initialize(dependencies: Unit, key: TestLifecycleKey): Feature {
return Feature(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import com.google.common.truth.Truth.assertThat
import com.instacart.formula.android.FragmentFlowState
import com.instacart.formula.android.FragmentKey
import com.instacart.formula.android.BackCallback
import com.instacart.formula.android.FragmentFlowStore
import com.instacart.formula.test.TestKey
import com.instacart.formula.test.TestKeyWithId
import com.instacart.formula.test.TestFragmentActivity
Expand Down Expand Up @@ -49,7 +50,7 @@ class FragmentFlowRenderViewTest {

updateThreads.add(Thread.currentThread())
},
contracts = {
fragmentStore = FragmentFlowStore.init {
bind(TestFeatureFactory<TestKey> { stateChanges(it) })
bind(TestFeatureFactory<TestKeyWithId> { stateChanges(it) })
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import com.google.common.truth.Truth.assertThat
import com.instacart.formula.android.FormulaFragment
import com.instacart.formula.android.ActivityStoreContext
import com.instacart.formula.android.FeatureFactory
import com.instacart.formula.android.FragmentFlowStore
import com.instacart.formula.android.FragmentKey
import com.instacart.formula.test.TestKey
import com.instacart.formula.test.TestKeyWithId
Expand Down Expand Up @@ -36,7 +37,7 @@ class FragmentLifecycleStateTest {
configureActivity = {
initialContract = TestKey()
},
contracts = {
fragmentStore = FragmentFlowStore.init {
bind(featureFactory<TestKey>(this@activity))
bind(featureFactory<TestKeyWithId>(this@activity))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ 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.FragmentFlowStore
import com.instacart.formula.android.ViewFactory
import com.instacart.formula.test.TestFragmentActivity
import com.instacart.formula.test.TestFragmentLifecycleCallback
Expand Down Expand Up @@ -36,7 +37,7 @@ class FragmentLifecycleTest {
contract = TestLifecycleKey()
initialContract = contract
},
contracts = {
fragmentStore = FragmentFlowStore.init {
val featureFactory = object : FeatureFactory<Unit, TestLifecycleKey> {
override fun initialize(dependencies: Unit, key: TestLifecycleKey): Feature {
return Feature(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import io.reactivex.rxjava3.disposables.Disposable
* navigation destination [com.instacart.formula.fragment.FragmentKey] to its state
* management stream.
*
* @param contracts Fragment state management defined for this [Activity].
* @param fragmentStore Fragment state management defined for this [Activity].
* @param streams This provides ability to configure arbitrary RxJava streams that survive
* configuration changes. Check [com.instacart.formula.android.StreamConfigurator] for utility methods.
* @param configureActivity This is invoked as part of [com.instacart.formula.FormulaAndroid.onPreCreate]. You can
Expand All @@ -18,7 +18,7 @@ import io.reactivex.rxjava3.disposables.Disposable
* @param onFragmentLifecycleEvent This is callback for when a fragment is added or removed.
*/
class ActivityStore<Activity : FragmentActivity>(
val contracts: FragmentFlowStore,
val fragmentStore: FragmentFlowStore,
val streams: (StreamConfigurator<Activity>.() -> Disposable)? = null,
val configureActivity: ((Activity) -> Unit)? = null,
val onRenderFragmentState: ((Activity, FragmentFlowState) -> Unit)? = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,58 +79,21 @@ abstract class ActivityStoreContext<out Activity : FragmentActivity> {
* @param onFragmentLifecycleEvent This is called after each [FragmentLifecycleEvent].
* @param streams This provides ability to configure arbitrary RxJava streams that survive
* configuration changes. Check [StreamConfigurator] for utility methods.
* @param contracts [FragmentFlowStore] used to provide state management for individual screens.
* @param fragmentStore [FragmentFlowStore] used to provide state management for individual screens.
*/
fun <ActivityT : FragmentActivity> store(
configureActivity: (ActivityT.() -> Unit)? = null,
onRenderFragmentState: ((ActivityT, FragmentFlowState) -> Unit)? = null,
onFragmentLifecycleEvent: ((FragmentLifecycleEvent) -> Unit)? = null,
streams: (StreamConfigurator<ActivityT>.() -> Disposable)? = null,
contracts: FragmentFlowStore
fragmentStore: FragmentFlowStore = FragmentFlowStore.EMPTY,
): ActivityStore<ActivityT> {
return ActivityStore(
contracts = contracts,
fragmentStore = fragmentStore,
configureActivity = configureActivity,
onFragmentLifecycleEvent = onFragmentLifecycleEvent,
onRenderFragmentState = onRenderFragmentState,
streams = streams
)
}

/**
* Creates an [ActivityStore].
*
* @param configureActivity This is called when activity is created before view inflation. You can use this to
* configure / inject the activity.
* @param onRenderFragmentState This is called after [FragmentFlowState] is applied to UI.
* @param onFragmentLifecycleEvent This is called after each [FragmentLifecycleEvent].
* @param streams This provides ability to configure arbitrary RxJava streams that survive
* configuration changes. Check [StreamConfigurator] for utility methods.
* @param contracts Builder method that configures [FragmentFlowStore] used to provide state management for individual screens.
*/
inline fun <ActivityT : FragmentActivity> store(
noinline configureActivity: (ActivityT.() -> Unit)? = null,
noinline onRenderFragmentState: ((ActivityT, FragmentFlowState) -> Unit)? = null,
noinline onFragmentLifecycleEvent: ((FragmentLifecycleEvent) -> Unit)? = null,
noinline streams: (StreamConfigurator<ActivityT>.() -> Disposable)? = null,
crossinline contracts: FragmentStoreBuilder<Unit>.() -> Unit = {}
): ActivityStore<ActivityT> {
return store(
configureActivity = configureActivity,
onRenderFragmentState = onRenderFragmentState,
onFragmentLifecycleEvent = onFragmentLifecycleEvent,
streams = streams,
contracts = contracts(Unit, contracts)
)
}

/**
* Convenience method to to create a [FragmentFlowStore] with a [Component] instance.
*/
inline fun <Component> contracts(
rootComponent: Component,
crossinline contracts: FragmentStoreBuilder<Component>.() -> Unit
): FragmentFlowStore {
return FragmentFlowStore.init(rootComponent, contracts)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package com.instacart.formula.android

import com.instacart.formula.android.internal.FeatureBinding
import com.instacart.formula.android.internal.Features
import com.instacart.formula.android.internal.MappedFeatureFactory
import java.lang.IllegalStateException
import kotlin.reflect.KClass

/**
* Helps to build a [Features] list that binds various fragment keys to their respective
* feature factories. Each feature factory has a dependency type that needs to either match
* [Dependencies] type defined here or map this root dependency type to the custom type.
*/
class FeaturesBuilder<Dependencies> {
companion object {
inline fun <Dependencies> build(
init: FeaturesBuilder<Dependencies>.() -> Unit
): Features<Dependencies> {
return FeaturesBuilder<Dependencies>().apply(init).build()
}
}

private val types = mutableSetOf<Class<*>>()
private val bindings: MutableList<FeatureBinding<Dependencies, *>> = mutableListOf()

/**
* Binds a [feature factory][FeatureFactory] for a specific [key][type].
*
* @param type The class which describes the [key][Key].
* @param featureFactory Feature factory that provides state observable and view rendering logic.
*/
fun <Key : FragmentKey> bind(
type : KClass<Key>,
featureFactory: FeatureFactory<Dependencies, Key>,
) = apply {
val binding = FeatureBinding(type.java, featureFactory)
bind(type.java, binding)
}

/**
* Binds a feature factory for a [Key].
*
* @param featureFactory Feature factory that provides state observable and view rendering logic.
*/
inline fun <reified Key: FragmentKey> bind(
featureFactory: FeatureFactory<Dependencies, Key>
) = apply {
bind(Key::class, featureFactory)
}

/**
* A convenience inline function that binds a feature factory for a specific [key][Key].
*
* @param featureFactory Feature factory that provides state observable and view rendering logic.
* @param toDependencies Maps [Dependencies] to feature factory [dependencies][CustomDependencyType].
*/
inline fun <CustomDependencyType, reified Key: FragmentKey> bind(
featureFactory: FeatureFactory<CustomDependencyType, Key>,
noinline toDependencies: (Dependencies) -> CustomDependencyType
) = apply {
val mapped = MappedFeatureFactory(
delegate = featureFactory,
toDependencies = toDependencies,
)
bind(Key::class, mapped)
}

@PublishedApi
internal fun build(): Features<Dependencies> {
return Features(bindings)
}

private fun bind(type: Class<*>, binding: FeatureBinding<Dependencies, *>) = apply {
if (types.contains(type)) {
throw IllegalStateException("Binding for $type already exists")
}
types += type
bindings += binding
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.instacart.formula.android

import com.instacart.formula.RuntimeConfig
import com.instacart.formula.android.events.FragmentLifecycleEvent
import com.instacart.formula.android.internal.Features
import com.instacart.formula.android.internal.FragmentFlowStoreFormula
import com.instacart.formula.android.utils.MainThreadDispatcher
import com.instacart.formula.rxjava3.toObservable
Expand All @@ -14,6 +15,8 @@ class FragmentFlowStore @PublishedApi internal constructor(
private val formula: FragmentFlowStoreFormula<*>,
) {
companion object {
val EMPTY = init { }

inline fun init(
crossinline init: FragmentStoreBuilder<Unit>.() -> Unit
): FragmentFlowStore {
Expand All @@ -22,10 +25,17 @@ class FragmentFlowStore @PublishedApi internal constructor(

inline fun <Component> init(
rootComponent: Component,
crossinline contracts: FragmentStoreBuilder<Component>.() -> Unit
crossinline init: FragmentStoreBuilder<Component>.() -> Unit
): FragmentFlowStore {
val features = FragmentStoreBuilder.build(init)
return init(rootComponent, features)
}

fun <Component> init(
component: Component,
features: Features<Component>
): FragmentFlowStore {
val bindings = FragmentStoreBuilder.build(contracts)
val formula = FragmentFlowStoreFormula(rootComponent, bindings)
val formula = FragmentFlowStoreFormula(component, features.bindings)
return FragmentFlowStore(formula)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,79 +1,4 @@
package com.instacart.formula.android

import com.instacart.formula.android.internal.FeatureBinding
import com.instacart.formula.android.internal.MappedFeatureFactory
import java.lang.IllegalStateException
import kotlin.reflect.KClass

/**
* A class used by [FragmentFlowStore] to register [fragment keys][FragmentKey] and their
* feature factories.
*/
class FragmentStoreBuilder<Component> {
companion object {
@PublishedApi
internal inline fun <Component> build(
init: FragmentStoreBuilder<Component>.() -> Unit
): List<FeatureBinding<Component, *>> {
return FragmentStoreBuilder<Component>().apply(init).build()
}
}

private val types = mutableSetOf<Class<*>>()
private val bindings: MutableList<FeatureBinding<Component, *>> = mutableListOf()

/**
* Binds a [feature factory][FeatureFactory] for a specific [key][type].
*
* @param type The class which describes the [key][Key].
* @param featureFactory Feature factory that provides state observable and view rendering logic.
*/
fun <Key : FragmentKey> bind(
type : KClass<Key>,
featureFactory: FeatureFactory<Component, Key>,
) = apply {
val binding = FeatureBinding(type.java, featureFactory)
bind(type.java, binding)
}

/**
* Binds a feature factory for a [Key].
*
* @param featureFactory Feature factory that provides state observable and view rendering logic.
*/
inline fun <reified Key: FragmentKey> bind(
featureFactory: FeatureFactory<Component, Key>
) = apply {
bind(Key::class, featureFactory)
}

/**
* A convenience inline function that binds a feature factory for a specific [key][Key].
*
* @param featureFactory Feature factory that provides state observable and view rendering logic.
* @param toDependencies Maps [Component] to feature factory [dependencies][Dependencies].
*/
inline fun <Dependencies, reified Key: FragmentKey> bind(
featureFactory: FeatureFactory<Dependencies, Key>,
noinline toDependencies: (Component) -> Dependencies
) = apply {
val mapped = MappedFeatureFactory(
delegate = featureFactory,
toDependencies = toDependencies,
)
bind(Key::class, mapped)
}

@PublishedApi
internal fun build(): List<FeatureBinding<Component, *>> {
return bindings
}

private fun bind(type: Class<*>, binding: FeatureBinding<Component, *>) = apply {
if (types.contains(type)) {
throw IllegalStateException("Binding for $type already exists")
}
types += type
bindings += binding
}
}
@Deprecated("Use FeaturesBuilder directly")
typealias FragmentStoreBuilder<Component> = FeaturesBuilder<Component>
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ internal class ActivityManager<Activity : FragmentActivity>(
activity = activity,
fragmentEnvironment = environment,
onLifecycleEvent = {
store.contracts.onLifecycleEffect(it)
store.fragmentStore.onLifecycleEffect(it)
store.onFragmentLifecycleEvent?.invoke(it)
},
onLifecycleState = delegate::updateFragmentLifecycleState,
onFragmentViewStateChanged = store.contracts::onVisibilityChanged
onFragmentViewStateChanged = store.fragmentStore::onVisibilityChanged
)
}

Expand Down Expand Up @@ -108,7 +108,7 @@ internal class ActivityManager<Activity : FragmentActivity>(

private fun subscribeToFragmentStateChanges(): Disposable {
return store
.contracts
.fragmentStore
.state(environment)
.subscribe(delegate.fragmentFlowStateRelay::accept)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import com.instacart.formula.android.FragmentKey
/**
* Defines how a specific key should be bound to its [FeatureFactory]
*/
class FeatureBinding<in Component, Key : FragmentKey>(
class FeatureBinding<in Dependencies, Key : FragmentKey>(
val type: Class<Key>,
val feature: FeatureFactory<Component, Key>,
val feature: FeatureFactory<Dependencies, Key>,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.instacart.formula.android.internal

class Features<in Dependencies>(
val bindings: List<FeatureBinding<Dependencies, *>>,
)
Loading

0 comments on commit 49b6596

Please sign in to comment.