Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Fragment] Replace bind with setOutput function. #319

Merged
merged 2 commits into from
Dec 12, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
@@ -1,45 +1,29 @@
package com.instacart.formula.android.compose

import android.os.SystemClock
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rxjava3.subscribeAsState
import androidx.compose.ui.platform.ComposeView
import com.instacart.formula.android.FeatureView
import com.instacart.formula.android.ViewFactory
import com.jakewharton.rxrelay3.BehaviorRelay


abstract class ComposeViewFactory<RenderModel> : ViewFactory<RenderModel> {

override fun create(inflater: LayoutInflater, container: ViewGroup?): FeatureView<RenderModel> {
val view = ComposeView(inflater.context)
var firstRender = true
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved in to formula fragment

val outputRelay = BehaviorRelay.create<RenderModel>()
view.setContent {
val model = outputRelay.subscribeAsState(null).value
if (model != null) {
Content(model)
}
}
return FeatureView(
view = view,
bind = { state ->
view.setContent {
val model = state.observable.subscribeAsState(null).value
if (model != null) {
val start = SystemClock.uptimeMillis()
Content(model)
val end = SystemClock.uptimeMillis()
state.environment.eventListener?.onRendered(
fragmentId = state.fragmentId,
durationInMillis = end - start,
)

if (firstRender) {
firstRender = false
state.environment.eventListener?.onFirstModelRendered(
fragmentId = state.fragmentId,
durationInMillis = end - state.initializedAtMillis,
)
}
}
}
null
}
setOutput = outputRelay::accept,
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,11 @@ import io.reactivex.rxjava3.core.Observable
* [FeatureView].
*
* @param view The root Android view.
* @param bind A bind function connects state observable to the view rendering.
* @param setOutput A function called to apply [RenderModel] to the view.
* @param lifecycleCallbacks Optional lifecycle callbacks if you need to know the Fragment state.
*/
class FeatureView<RenderModel>(
val view: View,
val bind: (State<RenderModel>) -> Cancelable?,
val setOutput: (RenderModel) -> Unit,
val lifecycleCallbacks: FragmentLifecycleCallback? = null,
) {
class State<RenderModel>(
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need this State class anymore

val initializedAtMillis: Long,
val fragmentId: FragmentId,
val environment: FragmentEnvironment,
val observable: Observable<RenderModel>,
)
}
)
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.instacart.formula.Cancelable
import com.instacart.formula.android.internal.FormulaFragmentDelegate
import com.instacart.formula.android.internal.getFormulaFragmentId
import com.jakewharton.rxrelay3.BehaviorRelay
import java.lang.Exception

class FormulaFragment : Fragment(), BaseFormulaFragment<Any> {
companion object {
Expand All @@ -31,13 +30,14 @@ class FormulaFragment : Fragment(), BaseFormulaFragment<Any> {
requireArguments().getParcelable<FragmentKey>(ARG_CONTRACT)!!
}

private var initializedAtMillis: Long? = SystemClock.uptimeMillis()

private var featureView: FeatureView<Any>? = null
private val stateRelay: BehaviorRelay<Any> = BehaviorRelay.create()
private var cancelable: Cancelable? = null
private var output: Any? = null

private var lifecycleCallback: FragmentLifecycleCallback? = null
private var initializedAtMillis: Long? = SystemClock.uptimeMillis()
private var firstRender = true

private val lifecycleCallback: FragmentLifecycleCallback?
get() = featureView?.lifecycleCallbacks

override fun onAttach(context: Context) {
super.onAttach(context)
Expand All @@ -47,6 +47,8 @@ class FormulaFragment : Fragment(), BaseFormulaFragment<Any> {
}

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
firstRender = true

val viewFactory = FormulaFragmentDelegate.viewFactory(this) ?: run {
// No view factory, no view
return null
Expand All @@ -59,17 +61,9 @@ class FormulaFragment : Fragment(), BaseFormulaFragment<Any> {

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
featureView?.let { value ->
val state = FeatureView.State(
initializedAtMillis = initializedAtMillis ?: SystemClock.uptimeMillis(),
fragmentId = getFormulaFragmentId(),
environment = FormulaFragmentDelegate.fragmentEnvironment(),
observable = stateRelay,
)
cancelable = value.bind(state)
this.lifecycleCallback = value.lifecycleCallbacks
lifecycleCallback?.onViewCreated(view, savedInstanceState)
}
tryToSetState()

lifecycleCallback?.onViewCreated(view, savedInstanceState)
}

override fun onActivityCreated(savedInstanceState: Bundle?) {
Expand Down Expand Up @@ -110,21 +104,18 @@ class FormulaFragment : Fragment(), BaseFormulaFragment<Any> {
override fun onDestroyView() {
initializedAtMillis = null

cancelable?.cancel()
cancelable = null

lifecycleCallback?.onDestroyView()
lifecycleCallback = null
super.onDestroyView()
featureView = null
}

override fun setState(state: Any) {
stateRelay.accept(state)
output = state
tryToSetState()
}

override fun currentState(): Any? {
return stateRelay.value
return output
}

override fun getFragmentKey(): FragmentKey {
Expand All @@ -134,4 +125,34 @@ class FormulaFragment : Fragment(), BaseFormulaFragment<Any> {
override fun toString(): String {
return "${key.tag} -> $key"
}

private fun tryToSetState() {
val output = output ?: return
val view = featureView ?: return

val fragmentId = getFormulaFragmentId()
val environment = FormulaFragmentDelegate.fragmentEnvironment()

try {
val start = SystemClock.uptimeMillis()
view.setOutput(output)
val end = SystemClock.uptimeMillis()

Laimiux marked this conversation as resolved.
Show resolved Hide resolved

environment.eventListener?.onRendered(
fragmentId = fragmentId,
durationInMillis = end - start,
)

if (firstRender) {
firstRender = false
environment.eventListener?.onFirstModelRendered(
fragmentId = fragmentId,
durationInMillis = end - (initializedAtMillis ?: SystemClock.uptimeMillis()),
)
}
} catch (exception: Exception) {
environment.onScreenError(key, exception)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package com.instacart.formula.android
import android.view.View
import com.instacart.formula.RenderView
import com.instacart.formula.Renderer
import com.instacart.formula.android.views.FeatureViewBindFunction

/**
* View instance contains an initialized Android [view] and provides factory
Expand Down Expand Up @@ -37,7 +36,7 @@ abstract class ViewInstance {
): FeatureView<RenderModel> {
return FeatureView(
view = view,
bind = FeatureViewBindFunction(renderer),
setOutput = renderer,
lifecycleCallbacks = lifecycleCallbacks
)
}
Expand Down

This file was deleted.

Loading