From c252fdc633205bab5276ebf7501c8ff5e4d29ca2 Mon Sep 17 00:00:00 2001 From: Arkadii Ivanov <> Date: Wed, 21 Oct 2020 22:50:21 +0100 Subject: [PATCH] Extract Main UI --- .../common/main/integration/TodoMainImpl.kt | 95 ++-------------- .../example/todo/common/main/ui/TodoMainUi.kt | 104 ++++++++++++++++++ .../common/main/integration/TodoMainTest.kt | 17 +-- 3 files changed, 123 insertions(+), 93 deletions(-) create mode 100644 examples/todoapp/common/main/src/commonMain/kotlin/example/todo/common/main/ui/TodoMainUi.kt diff --git a/examples/todoapp/common/main/src/commonMain/kotlin/example/todo/common/main/integration/TodoMainImpl.kt b/examples/todoapp/common/main/src/commonMain/kotlin/example/todo/common/main/integration/TodoMainImpl.kt index 96644b8f4f..5148f1ca5e 100644 --- a/examples/todoapp/common/main/src/commonMain/kotlin/example/todo/common/main/integration/TodoMainImpl.kt +++ b/examples/todoapp/common/main/src/commonMain/kotlin/example/todo/common/main/integration/TodoMainImpl.kt @@ -1,40 +1,17 @@ package example.todo.common.main.integration -import androidx.compose.foundation.Text -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.lazy.LazyColumnFor -import androidx.compose.material.Button -import androidx.compose.material.Checkbox -import androidx.compose.material.Divider -import androidx.compose.material.OutlinedTextField -import androidx.compose.material.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.input.key.ExperimentalKeyInput -import androidx.compose.ui.input.key.Key -import androidx.compose.ui.input.key.keyInputFilter -import androidx.compose.ui.text.AnnotatedString -import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.unit.dp import com.arkivanov.decompose.ComponentContext import example.todo.common.main.TodoMain import example.todo.common.main.TodoMain.Dependencies import example.todo.common.main.TodoMain.Output -import example.todo.common.main.store.TodoItem import example.todo.common.main.store.TodoMainStore.Intent import example.todo.common.main.store.TodoMainStore.State import example.todo.common.main.store.TodoMainStoreProvider +import example.todo.common.main.ui.TodoMainUi import example.todo.common.utils.composeState import example.todo.common.utils.getStore -import example.todo.common.utils.onKeyUp internal class TodoMainImpl( componentContext: ComponentContext, @@ -55,70 +32,18 @@ internal class TodoMainImpl( override fun invoke() { val state by store.composeState - Column { - TopAppBar(title = { Text(text = "Todo List") }) - - Box(Modifier.weight(1F)) { - TodoList(items = state.items) - } - - TodoInput(text = state.text) - } - } - - @Composable - private fun TodoList(items: List) { - LazyColumnFor(items = items) { item -> - Row(modifier = Modifier.clickable(onClick = { onItemClicked(id = item.id) }).padding(8.dp)) { - Text( - text = AnnotatedString(item.text), - modifier = Modifier.weight(1F), - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - - Spacer(modifier = Modifier.width(8.dp)) - - Checkbox( - checked = item.isDone, - onCheckedChange = { onDoneChanged(id = item.id, isDone = it) } - ) - } - - Divider() - } - } - - @OptIn(ExperimentalKeyInput::class) - @Composable - private fun TodoInput(text: String) { - Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(8.dp)) { - OutlinedTextField( - value = text, - modifier = Modifier.weight(weight = 1F).keyInputFilter(onKeyUp(Key.Enter, ::onAddClicked)), - onValueChange = ::onTextChanged, - label = { Text(text = "Add a todo") } - ) - - Button(modifier = Modifier.padding(start = 8.dp), onClick = ::onAddClicked) { - Text(text = "+") - } - } - } - - internal fun onItemClicked(id: Long) { - mainOutput.onNext(Output.Selected(id = id)) - } - - internal fun onDoneChanged(id: Long, isDone: Boolean) { - store.accept(Intent.SetItemDone(id = id, isDone = isDone)) + TodoMainUi( + state = state, + output = mainOutput, + intents = store::accept + ) } - internal fun onTextChanged(text: String) { - store.accept(Intent.SetText(text = text)) + internal fun onIntent(intent: Intent) { + store.accept(intent) } - internal fun onAddClicked() { - store.accept(Intent.AddItem) + internal fun onOutput(output: Output) { + mainOutput.onNext(output) } } diff --git a/examples/todoapp/common/main/src/commonMain/kotlin/example/todo/common/main/ui/TodoMainUi.kt b/examples/todoapp/common/main/src/commonMain/kotlin/example/todo/common/main/ui/TodoMainUi.kt new file mode 100644 index 0000000000..609937f3ee --- /dev/null +++ b/examples/todoapp/common/main/src/commonMain/kotlin/example/todo/common/main/ui/TodoMainUi.kt @@ -0,0 +1,104 @@ +package example.todo.common.main.ui + +import androidx.compose.foundation.Text +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.lazy.LazyColumnFor +import androidx.compose.material.Button +import androidx.compose.material.Checkbox +import androidx.compose.material.Divider +import androidx.compose.material.OutlinedTextField +import androidx.compose.material.TopAppBar +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.input.key.ExperimentalKeyInput +import androidx.compose.ui.input.key.Key +import androidx.compose.ui.input.key.keyInputFilter +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import com.badoo.reaktive.base.Consumer +import example.todo.common.main.TodoMain.Output +import example.todo.common.main.store.TodoItem +import example.todo.common.main.store.TodoMainStore.Intent +import example.todo.common.main.store.TodoMainStore.State +import example.todo.common.utils.onKeyUp + +@Composable +internal fun TodoMainUi( + state: State, + output: Consumer, + intents: (Intent) -> Unit +) { + Column { + TopAppBar(title = { Text(text = "Todo List") }) + + Box(Modifier.weight(1F)) { + TodoList( + items = state.items, + onItemClicked = { output.onNext(Output.Selected(id = it)) }, + onDoneChanged = { id, isDone -> intents(Intent.SetItemDone(id = id, isDone = isDone)) } + ) + } + + TodoInput( + text = state.text, + onAddClicked = { intents(Intent.AddItem) }, + onTextChanged = { intents(Intent.SetText(text = it)) } + ) + } +} + +@Composable +private fun TodoList( + items: List, + onItemClicked: (id: Long) -> Unit, + onDoneChanged: (id: Long, isDone: Boolean) -> Unit +) { + LazyColumnFor(items = items) { item -> + Row(modifier = Modifier.clickable(onClick = { onItemClicked(item.id) }).padding(8.dp)) { + Text( + text = AnnotatedString(item.text), + modifier = Modifier.weight(1F), + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + + Spacer(modifier = Modifier.width(8.dp)) + + Checkbox( + checked = item.isDone, + onCheckedChange = { onDoneChanged(item.id, it) } + ) + } + + Divider() + } +} + +@OptIn(ExperimentalKeyInput::class) +@Composable +private fun TodoInput( + text: String, + onTextChanged: (String) -> Unit, + onAddClicked: () -> Unit +) { + Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(8.dp)) { + OutlinedTextField( + value = text, + modifier = Modifier.weight(weight = 1F).keyInputFilter(onKeyUp(Key.Enter, onAddClicked)), + onValueChange = onTextChanged, + label = { Text(text = "Add a todo") } + ) + + Button(modifier = Modifier.padding(start = 8.dp), onClick = onAddClicked) { + Text(text = "+") + } + } +} diff --git a/examples/todoapp/common/main/src/commonTest/kotlin/example/todo/common/main/integration/TodoMainTest.kt b/examples/todoapp/common/main/src/commonTest/kotlin/example/todo/common/main/integration/TodoMainTest.kt index fd85b5e304..a0f3999c6e 100644 --- a/examples/todoapp/common/main/src/commonTest/kotlin/example/todo/common/main/integration/TodoMainTest.kt +++ b/examples/todoapp/common/main/src/commonTest/kotlin/example/todo/common/main/integration/TodoMainTest.kt @@ -15,6 +15,7 @@ import example.todo.common.database.TodoItemEntity import example.todo.common.main.TodoMain.Dependencies import example.todo.common.main.TodoMain.Output import example.todo.common.main.store.TodoItem +import example.todo.common.main.store.TodoMainStore.Intent import example.todo.database.TodoDatabase import kotlin.test.BeforeTest import kotlin.test.Test @@ -59,11 +60,11 @@ class TodoMainTest { } @Test - fun WHEN_item_clicked_THEN_Output_Selected_emitted() { + fun WHEN_item_selected_THEN_Output_Selected_emitted() { queries.add("Item1") val id = firstItem().id - impl.onItemClicked(id = id) + impl.onOutput(Output.Selected(id = id)) output.assertValue(Output.Selected(id = id)) } @@ -74,7 +75,7 @@ class TodoMainTest { val id = firstItem().id queries.setDone(id = id, isDone = false) - impl.onDoneChanged(id = id, isDone = true) + impl.onIntent(Intent.SetItemDone(id = id, isDone = true)) assertTrue(firstItem().isDone) } @@ -85,13 +86,13 @@ class TodoMainTest { val id = firstItem().id queries.setDone(id = id, isDone = true) - impl.onDoneChanged(id = id, isDone = false) + impl.onIntent(Intent.SetItemDone(id = id, isDone = false)) assertFalse(firstItem().isDone) } @Test - fun WHEN_item_text_changed_THEN_item_updated() { + fun WHEN_item_text_changed_in_database_THEN_item_updated() { queries.add("Item1") val id = firstItem().id @@ -102,16 +103,16 @@ class TodoMainTest { @Test fun WHEN_text_changed_THEN_text_updated() { - impl.onTextChanged(text = "Item text") + impl.onIntent(Intent.SetText(text = "Item text")) assertEquals("Item text", impl.state.text) } @Test fun GIVEN_text_entered_WHEN_add_clicked_THEN_item_added_in_database() { - impl.onTextChanged(text = "Item text") + impl.onIntent(Intent.SetText(text = "Item text")) - impl.onAddClicked() + impl.onIntent(Intent.AddItem) assertEquals("Item text", lastInsertItem().text) }