Skip to content

Instantly share code, notes, and snippets.

@ShikaSD
Last active December 25, 2019 01:06
Show Gist options
  • Save ShikaSD/83101c10ede892c917be2b39c11b37e3 to your computer and use it in GitHub Desktop.
Save ShikaSD/83101c10ede892c917be2b39c11b37e3 to your computer and use it in GitHub Desktop.
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