Browse Source

Extract Main UI

pull/10/head
Arkadii Ivanov 4 years ago
parent
commit
c252fdc633
  1. 95
      examples/todoapp/common/main/src/commonMain/kotlin/example/todo/common/main/integration/TodoMainImpl.kt
  2. 104
      examples/todoapp/common/main/src/commonMain/kotlin/example/todo/common/main/ui/TodoMainUi.kt
  3. 17
      examples/todoapp/common/main/src/commonTest/kotlin/example/todo/common/main/integration/TodoMainTest.kt

95
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<TodoItem>) {
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)
}
}

104
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<Output>,
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<TodoItem>,
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 = "+")
}
}
}

17
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)
}

Loading…
Cancel
Save