Skip to content

Instantly share code, notes, and snippets.

@Mishkun
Last active April 2, 2019 12:09
Show Gist options
  • Save Mishkun/40322b70f64654e36890cc678eb32e88 to your computer and use it in GitHub Desktop.
Save Mishkun/40322b70f64654e36890cc678eb32e88 to your computer and use it in GitHub Desktop.
/**
* @author themishkun on 24/07/2018.
*/
typealias ViewAction<View> = (View) -> Unit
@DslMarker
annotation class ViewStateDslMarker
/**
* Class representing the storage of actions for view to be executed. It also provides a DSL to quickly
* describe how these actions are stored, are they persist between view changes or not.
* The DSL is super easy:
* <state | oneShot> ( <interface_method_reference> ) [ <parameters, separated by comma, if present> ]
*
* So, given interface
* ```
* interface TestView {
* fun test()
* fun testParams(p1: A, p2: B, p3: C)
* }
* ```
* We can describe view state as
* ```
* class ViewState : TestView {
* val dsl = VewStateDsl<TestView>()
* override fun test() = dsl.state[TestView::test]
* override fun testParams(p1: A, p2: B, p3: C) = dsl.oneShot[TestView::testParams](p1, p2, p3)
* restore() = view?.let(dsl::restore)
* }
* ```
* Note that we have convenience integration with the BaseViewState, so we don't need to call restore
* and take care of dsl property
* ```
* class ViewState : BaseViewState<TestView>, TestView {
* override fun test() = state[TestView::test]
* override fun testParams(p1: A, p2: B, p3: C) = oneShot[TestView::testParams](p1, p2, p3)
* }
* ```
*/
@ViewStateDslMarker
@Suppress("MethodOverloading")
class ViewStateDsl<View> {
private val states: MutableMap<Any, ViewAction<View>> = mutableMapOf()
private val oneShots: MutableMap<Any, ViewAction<View>> = mutableMapOf()
/**
* View reference to properly act on
*/
var view: View? = null
/**
* When called this variant of dsl, the method referenced would be called on every restore with
* the latest given parameters
*/
val state = StateDsl()
/**
* When called this variant of dsl, the method referenced would be called only once:
* a) immediately if view is present
* b) on next restore()
*/
val oneShot = OneShotDsl()
/**
* Call this after attaching view to restore states and fire pending oneShots
*/
fun restore() {
view?.let { view ->
states.forEach { (_, action) -> action(view) }
oneShots.forEach { (_, action) -> action(view) }
oneShots.clear()
}
}
@ViewStateDslMarker
inner class StateDsl {
operator fun get(method: View.() -> Unit) {
saveStateAndAct(method) { it.method() }
}
operator fun <A> get(method: View.(A) -> Unit): (A) -> Unit = { param1 ->
saveStateAndAct(method) { it.method(param1) }
}
operator fun <A, B> get(method: View.(A, B) -> Unit): (A, B) -> Unit = { param1, param2 ->
saveStateAndAct(method) { it.method(param1, param2) }
}
operator fun <A, B, C> get(method: View.(A, B, C) -> Unit): (A, B, C) -> Unit = { param1, param2, param3 ->
saveStateAndAct(method) { it.method(param1, param2, param3) }
}
operator fun <A, B, C, D> get(method: View.(A, B, C, D) -> Unit): (A, B, C, D) -> Unit =
{ param1, param2, param3, param4 -> saveStateAndAct(method) { it.method(param1, param2, param3, param4) }
}
private fun saveStateAndAct(method: Any, action: ViewAction<View>) {
this@ViewStateDsl.view?.let(action)
this@ViewStateDsl.states[method] = action
}
}
@ViewStateDslMarker
inner class OneShotDsl {
operator fun get(method: View.() -> Unit) {
fireOneShotOrSave(method) { it.method() }
}
operator fun <A> get(method: View.(A) -> Unit): (A) -> Unit = { param1 ->
fireOneShotOrSave(method) { it.method(param1) }
}
operator fun <A, B> get(method: View.(A, B) -> Unit): (A, B) -> Unit = { param1, param2 ->
fireOneShotOrSave(method) { it.method(param1, param2) }
}
operator fun <A, B, C> get(method: View.(A, B, C) -> Unit): (A, B, C) -> Unit = { param1, param2, param3 ->
fireOneShotOrSave(method) { it.method(param1, param2, param3) }
}
private fun fireOneShotOrSave(method: Any, action: ViewAction<View>) {
val view = this@ViewStateDsl.view
if (view != null) {
action(view)
} else {
this@ViewStateDsl.oneShots[method] = action
}
}
}
}
interface TestView {
fun test()
fun testParams(p1: A, p2: B, p3: C)
}
/*
* Mosby-like ViewState
*/
class ViewState : TestView {
val dsl = VewStateDsl<TestView>()
override fun test() = dsl.state[TestView::test]
override fun testParams(p1: A, p2: B, p3: C) = dsl.oneShot[TestView::testParams](p1, p2, p3)
restore() = view?.let(dsl::restore)
}
/*
* Can be used raw without any Mosby viewstate
*/
class SomePresenter {
val dsl = ViewStateDsl()
onBind(view: TestView){
dsl.view = view
dsl.restore()
}
onUnbind() {
dsl.view = null
}
onDataLoaded() {
dsl.oneShot[TestView::test]
dsl.state[TestView::testParams](param1, param2, param3)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment