Last active
April 2, 2019 12:09
-
-
Save Mishkun/40322b70f64654e36890cc678eb32e88 to your computer and use it in GitHub Desktop.
This file contains hidden or 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
/** | |
* @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 | |
} | |
} | |
} | |
} |
This file contains hidden or 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
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