-
Notifications
You must be signed in to change notification settings - Fork 17
/
StackActions.kt
98 lines (80 loc) · 4.08 KB
/
StackActions.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
package com.github.terrakok.modo.stack
import com.github.terrakok.modo.NavigationAction
import com.github.terrakok.modo.NavigationContainer
import com.github.terrakok.modo.ReducerAction
import com.github.terrakok.modo.Screen
import com.github.terrakok.modo.ScreenKey
interface StackAction : NavigationAction<StackState>
fun interface StackReducerAction : StackAction, ReducerAction<StackState>
class SetStack(val state: StackState) : StackReducerAction {
@Suppress("SpreadOperator")
constructor(screen: Screen, vararg screens: Screen) : this(
StackState(listOf(screen, *screens))
)
override fun reduce(oldState: StackState): StackState =
state
}
class Forward(val screen: Screen, vararg val screens: Screen) : StackReducerAction {
@Suppress("SpreadOperator")
override fun reduce(oldState: StackState): StackState = StackState(
oldState.stack + listOf(screen, *screens)
)
}
class Replace(val screen: Screen, vararg val screens: Screen) : StackReducerAction {
@Suppress("SpreadOperator")
override fun reduce(oldState: StackState): StackState = StackState(
oldState.stack.dropLast(1) + listOf(screen, *screens)
)
}
class BackTo(val backToCondition: (pos: Int, screen: Screen) -> Boolean) : StackReducerAction {
constructor(screenKey: ScreenKey) : this(
{ _, screen ->
screen.screenKey == screenKey
}
)
constructor(screenBackTo: Screen) : this(
{ _, screen ->
screen == screenBackTo
}
)
override fun reduce(oldState: StackState): StackState {
val stack = oldState.stack
var foundPos = -1
for (i in stack.lastIndex downTo 0) {
if (backToCondition(i, stack[i])) {
foundPos = i
break
}
}
return if (foundPos != -1) StackState(oldState.stack.take(foundPos + 1)) else oldState
}
companion object {
inline operator fun <reified T : Screen> invoke(): StackReducerAction = BackTo { _, screen ->
screen is T
}
}
}
class RemoveScreens(val condition: (pos: Int, screen: Screen) -> Boolean) : StackReducerAction {
override fun reduce(oldState: StackState): StackState = StackState(
oldState.stack.filterIndexed { i, screen -> !condition(i, screen) }
)
}
class Back(private val screensToDrop: Int = 1) : StackReducerAction {
override fun reduce(oldState: StackState): StackState = StackState(
oldState.stack.dropLast(screensToDrop)
)
}
fun StackNavContainer.dispatch(action: (StackState) -> StackState) = dispatch(StackReducerAction(action))
fun NavigationContainer<StackState, StackAction>.forward(screen: Screen, vararg screens: Screen) = dispatch(Forward(screen, *screens))
fun NavigationContainer<StackState, StackAction>.replace(screen: Screen, vararg screens: Screen) = dispatch(Replace(screen, *screens))
fun NavigationContainer<StackState, StackAction>.setStack(screen: Screen, vararg screens: Screen) = dispatch(SetStack(screen, *screens))
fun NavigationContainer<StackState, StackAction>.setState(state: StackState) = dispatch(SetStack(state))
fun NavigationContainer<StackState, StackAction>.clearStack() = dispatch(SetStack(StackState()))
inline fun <reified T : Screen> NavigationContainer<StackState, StackAction>.backTo() = dispatch(BackTo<T>())
fun NavigationContainer<StackState, StackAction>.backTo(screen: Screen) = dispatch(BackTo(screen))
fun NavigationContainer<StackState, StackAction>.backTo(screenKey: ScreenKey) = dispatch(BackTo(screenKey))
fun NavigationContainer<StackState, StackAction>.backTo(pos: Int) = backTo { backToPos, _ -> pos == backToPos }
fun NavigationContainer<StackState, StackAction>.backTo(backToCondition: (pos: Int, screen: Screen) -> Boolean) = dispatch(BackTo(backToCondition))
fun NavigationContainer<StackState, StackAction>.backToRoot() = backTo(0)
fun NavigationContainer<StackState, StackAction>.removeScreens(condition: (pos: Int, screen: Screen) -> Boolean) = dispatch(RemoveScreens(condition))
fun NavigationContainer<StackState, StackAction>.back(screensToDrop: Int = 1) = dispatch(Back(screensToDrop))