Skip to content

Instantly share code, notes, and snippets.

@RationalRank
Last active November 23, 2022 02:24
Show Gist options
  • Save RationalRank/c7e493ac04aa0567c6f1cd65f709335b to your computer and use it in GitHub Desktop.
Save RationalRank/c7e493ac04aa0567c6f1cd65f709335b to your computer and use it in GitHub Desktop.
Appyx NodeConnector - Using kotlin flow
class NodeConnector<Input, Output> : Connectable<Input, Output> {
private val inputChannel: Channel<Input> = Channel(Channel.BUFFERED)
private val outputChannel: Channel<Output> = Channel(Channel.BUFFERED)
override val input: Flow<Input> = inputChannel.receiveAsFlow()
override val output: Flow<Output> = outputChannel.receiveAsFlow()
override fun send(value: Output) {
outputChannel.trySend(value)
}
override fun accept(value: Input) {
inputChannel.trySend(value)
}
}
interface Connectable<Input, Output> : NodeLifecycleAware {
val input: Flow<Input>
val output: Flow<Output>
// Send an output
fun send(value: Output)
// Accept an input
fun accept(value: Input)
}
inline fun <reified Input, reified Output> NodeConnector<Input, Output>.forEachOutput(
lifecycleOwner: LifecycleOwner,
crossinline onOutputReceived: (output: Output) -> Unit
) {
output
.onEach { onOutputReceived(it) }
.launchIn(lifecycleOwner.lifecycleScope)
}
inline fun <reified Input, reified Output> NodeConnector<Input, Output>.forEachInput(
lifecycleOwner: LifecycleOwner,
crossinline onInputReceived: (output: Input) -> Unit
) {
input
.onEach { onInputReceived(it) }
.launchIn(lifecycleOwner.lifecycleScope)
}
class ParentNode(
buildContext: BuildContext,
private val listBackStack: BackStack<Routing>
) : ParentNode<ParentNode.Routing>(listBackStack, buildContext) {
sealed interface Routing : Parcelable {
@Parcelize
object List : Routing
@Parcelize
object Detail : Routing
}
private val listConnection: NodeConnector<ListNode.Inputs, ListNode.Outputs> = NodeConnector()
private val detailConnection: NodeConnector<DetailNode.Inputs, DetailNode.Outputs> = NodeConnector()
init {
listConnection.forEachOutput(this) { output ->
if (output is ListNode.Outputs.ItemSelected) {
listBackStack.singleTop(Routing.Detail)
detailConnection.accept(DetailNode.Inputs.ViewItem(output.itemUid, output.name))
}
}
}
}
class DetailNode(
buildContext: BuildContext,
) : Node(buildContext) {
sealed class Inputs {
data class ViewItem(val itemUid: String, val name: String) : Inputs()
}
init {
nodeConnector.forEachInput(this@DetailNode) { input ->
if (input is Inputs.ViewItem) {
viewModel.updateItem(input.itemUid, input.name)
}
}
}
@Composable
override fun View(modifier: Modifier) {
DetailScreen(modifier, viewModel)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment