Last active
December 25, 2019 01:06
-
-
Save ShikaSD/83101c10ede892c917be2b39c11b37e3 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.badoo.mobile.proposals.prop4 | |
import android.content.Context | |
import android.view.LayoutInflater | |
import android.view.View | |
import com.badoo.mobile.kotlin.gone | |
import com.badoo.mobile.kotlin.visible | |
import com.jakewharton.rxrelay2.PublishRelay | |
import io.reactivex.ObservableSource | |
import io.reactivex.functions.Consumer | |
/** | |
* Component model marker interface | |
*/ | |
interface ComponentModel | |
interface BindableComponent<M> { | |
/** | |
* exists | |
*/ | |
fun bind(model: M) | |
} | |
/** | |
* Cosmos compatible view interface | |
*/ | |
interface ComponentView<M : Any> : BindableComponent<M> { | |
/** | |
* replacing [#getAsView] | |
* exists | |
*/ | |
val androidView: View | |
val supportedModel: Class<M> // Maybe replace with boolean check | |
} | |
/** | |
* Allows model to define implementing view | |
* exists | |
*/ | |
interface ComponentViewProvider { | |
fun provideView(context: Context): ComponentView<*> | |
} | |
/** | |
* Keeps reference to latest view and manages bindings / visibility | |
* exists | |
*/ | |
class ComponentController : Consumer<ComponentModel> { | |
var view: ComponentView<*>? = null | |
private val context: Context = TODO() | |
/** | |
* New method | |
*/ | |
override fun accept(model: ComponentModel) { | |
val currentView = view as? ComponentView<Any> | |
val data = when (model) { | |
is CustomComponent<*, *> -> model.data | |
else -> model | |
} | |
if (currentView != null && currentView.supportedModel.isInstance(model)) { | |
currentView.bind(data) | |
return | |
} | |
if (model is ComponentViewProvider) { | |
view = model.provideView(context) | |
} else { | |
// ComponentsInflater.inflate(context, model) // fallback | |
} | |
} | |
/** | |
* exists | |
*/ | |
@Deprecated("Use accept") | |
fun populate(model: ComponentModel?) { | |
if (model != null) { | |
accept(model) | |
view?.androidView?.visible() | |
} else { | |
view?.androidView?.gone() | |
} | |
} | |
} | |
data class CustomComponent<V : ComponentView<M>, M : Any>( | |
val data: M, | |
val viewFactory: (Context) -> V | |
) : ComponentModel, ComponentViewProvider { | |
override fun provideView(context: Context) = viewFactory(context) | |
} | |
//// Example with cosmos model - view | |
class TextComponentModel | |
class ButtonComponentModel | |
data class CtaComponentModel( | |
val text: TextComponentModel, | |
val button: ButtonComponentModel, | |
val onClick: () -> Unit | |
): ComponentViewProvider, ComponentModel { | |
override fun provideView(context: Context): CtaBox = CtaBox(context) | |
} | |
class CtaBox(context: Context) : View(context), ComponentView<CtaComponentModel> { | |
// val button = ButtonView() | |
// val text = TextView() | |
var click = { } | |
override fun bind(model: CtaComponentModel) { | |
// button.bind(model.button) | |
// text.bind(model.text) | |
click = { | |
model.onClick() | |
} | |
} | |
override val androidView: View = this | |
override val supportedModel: Class<CtaComponentModel> = CtaComponentModel::class.java | |
} | |
// Example with NOT cosmos model - view | |
data class RandomModel( | |
val text: TextComponentModel, | |
val button: ButtonComponentModel | |
) | |
class RandomView(val view: View) : ComponentView<RandomModel> { | |
override val androidView: View = view | |
override val supportedModel: Class<RandomModel> = RandomModel::class.java | |
override fun bind(model: RandomModel) { | |
TODO("not implemented") | |
} | |
} | |
/** | |
* ******* Rib context ****** | |
* ***** Actual proposal **** | |
*/ | |
/** | |
* RibView marker | |
* exists | |
*/ | |
interface RibView { | |
val androidView: View | |
} | |
/** | |
* Actually renders models | |
* By default is delegated to component controller, but can be anything else | |
*/ | |
typealias Renderer = Consumer<ComponentModel> | |
abstract class LogicalView<Event, Model>( | |
protected val events: PublishRelay<Event> = PublishRelay.create(), | |
private val renderer: Renderer | |
): ObservableSource<Event> by events, Consumer<Model> { | |
override fun accept(model: Model) { | |
renderer.accept(createComponentModel(model)) | |
} | |
abstract fun createComponentModel(model: Model): ComponentModel // | |
} | |
// Random rib implementation | |
interface RandomRibView : ObservableSource<RandomRibView.Event>, Consumer<RandomRibView.VM> { | |
sealed class Event | |
class VM | |
} | |
class RandomRibViewImpl( | |
val view: View | |
) : LogicalView<RandomRibView.Event, RandomRibView.VM>( | |
renderer = ComponentController() | |
), RandomRibView { | |
override fun createComponentModel(model: RandomRibView.VM): ComponentModel = | |
CustomComponent( | |
RandomModel( | |
TextComponentModel(), | |
ButtonComponentModel() | |
) | |
) { RandomView(view) } | |
} | |
// Or delegate to other source | |
val output: Consumer<ComponentModel> = Consumer { } | |
class RandomLogicalViewImpl( | |
val view: View | |
) : LogicalView<RandomRibView.Event, RandomRibView.VM>( | |
renderer = output | |
) { // No ribView interface though | |
override fun createComponentModel(model: RandomRibView.VM): ComponentModel = | |
CustomComponent( | |
RandomModel( | |
TextComponentModel(), | |
ButtonComponentModel() | |
) | |
) { RandomView(LayoutInflater.from(it).inflate(0, null)) } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment