Browse Source

Extract Main UI

pull/10/head
Arkadii Ivanov 4 years ago
parent
commit
c252fdc633
  1. 93
      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

93
examples/todoapp/common/main/src/commonMain/kotlin/example/todo/common/main/integration/TodoMainImpl.kt

@ -1,40 +1,17 @@
package example.todo.common.main.integration 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.Composable
import androidx.compose.runtime.getValue 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 com.arkivanov.decompose.ComponentContext
import example.todo.common.main.TodoMain import example.todo.common.main.TodoMain
import example.todo.common.main.TodoMain.Dependencies import example.todo.common.main.TodoMain.Dependencies
import example.todo.common.main.TodoMain.Output 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.Intent
import example.todo.common.main.store.TodoMainStore.State import example.todo.common.main.store.TodoMainStore.State
import example.todo.common.main.store.TodoMainStoreProvider 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.composeState
import example.todo.common.utils.getStore import example.todo.common.utils.getStore
import example.todo.common.utils.onKeyUp
internal class TodoMainImpl( internal class TodoMainImpl(
componentContext: ComponentContext, componentContext: ComponentContext,
@ -55,70 +32,18 @@ internal class TodoMainImpl(
override fun invoke() { override fun invoke() {
val state by store.composeState val state by store.composeState
Column { TodoMainUi(
TopAppBar(title = { Text(text = "Todo List") }) state = state,
output = mainOutput,
Box(Modifier.weight(1F)) { intents = store::accept
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))
} }
internal fun onTextChanged(text: String) { internal fun onIntent(intent: Intent) {
store.accept(Intent.SetText(text = text)) store.accept(intent)
} }
internal fun onAddClicked() { internal fun onOutput(output: Output) {
store.accept(Intent.AddItem) 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.Dependencies
import example.todo.common.main.TodoMain.Output import example.todo.common.main.TodoMain.Output
import example.todo.common.main.store.TodoItem import example.todo.common.main.store.TodoItem
import example.todo.common.main.store.TodoMainStore.Intent
import example.todo.database.TodoDatabase import example.todo.database.TodoDatabase
import kotlin.test.BeforeTest import kotlin.test.BeforeTest
import kotlin.test.Test import kotlin.test.Test
@ -59,11 +60,11 @@ class TodoMainTest {
} }
@Test @Test
fun WHEN_item_clicked_THEN_Output_Selected_emitted() { fun WHEN_item_selected_THEN_Output_Selected_emitted() {
queries.add("Item1") queries.add("Item1")
val id = firstItem().id val id = firstItem().id
impl.onItemClicked(id = id) impl.onOutput(Output.Selected(id = id))
output.assertValue(Output.Selected(id = id)) output.assertValue(Output.Selected(id = id))
} }
@ -74,7 +75,7 @@ class TodoMainTest {
val id = firstItem().id val id = firstItem().id
queries.setDone(id = id, isDone = false) queries.setDone(id = id, isDone = false)
impl.onDoneChanged(id = id, isDone = true) impl.onIntent(Intent.SetItemDone(id = id, isDone = true))
assertTrue(firstItem().isDone) assertTrue(firstItem().isDone)
} }
@ -85,13 +86,13 @@ class TodoMainTest {
val id = firstItem().id val id = firstItem().id
queries.setDone(id = id, isDone = true) queries.setDone(id = id, isDone = true)
impl.onDoneChanged(id = id, isDone = false) impl.onIntent(Intent.SetItemDone(id = id, isDone = false))
assertFalse(firstItem().isDone) assertFalse(firstItem().isDone)
} }
@Test @Test
fun WHEN_item_text_changed_THEN_item_updated() { fun WHEN_item_text_changed_in_database_THEN_item_updated() {
queries.add("Item1") queries.add("Item1")
val id = firstItem().id val id = firstItem().id
@ -102,16 +103,16 @@ class TodoMainTest {
@Test @Test
fun WHEN_text_changed_THEN_text_updated() { 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) assertEquals("Item text", impl.state.text)
} }
@Test @Test
fun GIVEN_text_entered_WHEN_add_clicked_THEN_item_added_in_database() { 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) assertEquals("Item text", lastInsertItem().text)
} }

Loading…
Cancel
Save