Skip to content

Commit

Permalink
Modernize tutorial final code
Browse files Browse the repository at this point in the history
It's time to get the Tutorial caught up with undeprecated API. This PR modifies only the final tutorial module. Once we're happy with the code, I'll back port the changes and update the prose in follow ups.

- Use `AndroidScreen` and drop `ViewRegistry`
- Use `View.setBackHandler`
- Use `TextController`
- More consistent naming, code style for actions and event handlers in `Screen` renderings

I did not introduce `RenderContext.eventHandler {}`, that seems like it would just be confusing to a newcomer.

Also not introducing big new blocks of material, in particular not introducing `Overlay`. I do think we should do that, but for this release I just want to focus on getting the deprecated code deleted.
  • Loading branch information
rjrjr committed Oct 12, 2023
1 parent 0a14ba0 commit fe04b90
Show file tree
Hide file tree
Showing 29 changed files with 342 additions and 380 deletions.
2 changes: 1 addition & 1 deletion samples/tutorial/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ To help with the setup, we have created a few helper modules:

- `tutorial-views`: A set of 3 views for the 3 screens we will be building, `Welcome`, `TodoList`,
and `TodoEdit`.
- `tutorial-base`: This is the starting point to build out the tutorial. It contains layouts that host the views from `TutorialViews` to see how they display.
- `tutorial-base`: This is the starting point to build out the tutorial.
- `tutorial-final`: This is an example of the completed tutorial - could be used as a reference if
you get stuck.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import com.squareup.workflow1.StatefulWorkflow
import com.squareup.workflow1.action
import com.squareup.workflow1.renderChild
import com.squareup.workflow1.ui.WorkflowUiExperimentalApi
import com.squareup.workflow1.ui.backstack.BackStackScreen
import com.squareup.workflow1.ui.backstack.toBackStackScreen
import com.squareup.workflow1.ui.container.BackStackScreen
import com.squareup.workflow1.ui.container.toBackStackScreen
import workflow.tutorial.RootWorkflow.State
import workflow.tutorial.RootWorkflow.State.Todo
import workflow.tutorial.RootWorkflow.State.Welcome
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import androidx.lifecycle.viewModelScope
import com.squareup.workflow1.ui.ViewRegistry
import com.squareup.workflow1.ui.WorkflowLayout
import com.squareup.workflow1.ui.WorkflowUiExperimentalApi
import com.squareup.workflow1.ui.backstack.BackStackContainer
import com.squareup.workflow1.ui.container.BackStackContainer
import com.squareup.workflow1.ui.renderWorkflowIn
import kotlinx.coroutines.flow.StateFlow

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import com.squareup.workflow1.StatefulWorkflow
import com.squareup.workflow1.action
import com.squareup.workflow1.renderChild
import com.squareup.workflow1.ui.WorkflowUiExperimentalApi
import com.squareup.workflow1.ui.backstack.BackStackScreen
import com.squareup.workflow1.ui.backstack.toBackStackScreen
import com.squareup.workflow1.ui.container.BackStackScreen
import com.squareup.workflow1.ui.container.toBackStackScreen
import workflow.tutorial.RootWorkflow.State
import workflow.tutorial.RootWorkflow.State.Todo
import workflow.tutorial.RootWorkflow.State.Welcome
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import androidx.lifecycle.viewModelScope
import com.squareup.workflow1.ui.ViewRegistry
import com.squareup.workflow1.ui.WorkflowLayout
import com.squareup.workflow1.ui.WorkflowUiExperimentalApi
import com.squareup.workflow1.ui.backstack.BackStackContainer
import com.squareup.workflow1.ui.container.BackStackContainer
import com.squareup.workflow1.ui.renderWorkflowIn
import kotlinx.coroutines.flow.StateFlow

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import com.squareup.workflow1.StatefulWorkflow
import com.squareup.workflow1.action
import com.squareup.workflow1.renderChild
import com.squareup.workflow1.ui.WorkflowUiExperimentalApi
import com.squareup.workflow1.ui.backstack.BackStackScreen
import com.squareup.workflow1.ui.backstack.toBackStackScreen
import com.squareup.workflow1.ui.container.BackStackScreen
import com.squareup.workflow1.ui.container.toBackStackScreen
import workflow.tutorial.RootWorkflow.State
import workflow.tutorial.RootWorkflow.State.Todo
import workflow.tutorial.RootWorkflow.State.Welcome
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import androidx.lifecycle.viewModelScope
import com.squareup.workflow1.ui.ViewRegistry
import com.squareup.workflow1.ui.WorkflowLayout
import com.squareup.workflow1.ui.WorkflowUiExperimentalApi
import com.squareup.workflow1.ui.backstack.BackStackContainer
import com.squareup.workflow1.ui.container.BackStackContainer
import com.squareup.workflow1.ui.renderWorkflowIn
import kotlinx.coroutines.flow.StateFlow

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@ import com.squareup.workflow1.Snapshot
import com.squareup.workflow1.StatefulWorkflow
import com.squareup.workflow1.action
import com.squareup.workflow1.renderChild
import com.squareup.workflow1.ui.Screen
import com.squareup.workflow1.ui.WorkflowUiExperimentalApi
import com.squareup.workflow1.ui.backstack.BackStackScreen
import com.squareup.workflow1.ui.backstack.toBackStackScreen
import com.squareup.workflow1.ui.container.BackStackScreen
import com.squareup.workflow1.ui.container.toBackStackScreen
import workflow.tutorial.RootWorkflow.State
import workflow.tutorial.RootWorkflow.State.Todo
import workflow.tutorial.RootWorkflow.State.Welcome
import workflow.tutorial.TodoWorkflow.TodoProps

@OptIn(WorkflowUiExperimentalApi::class)
object RootWorkflow : StatefulWorkflow<Unit, State, Nothing, BackStackScreen<Any>>() {
object RootWorkflow : StatefulWorkflow<Unit, State, Nothing, BackStackScreen<Screen>>() {

sealed class State {
object Welcome : State()
Expand All @@ -30,10 +31,10 @@ object RootWorkflow : StatefulWorkflow<Unit, State, Nothing, BackStackScreen<Any
renderProps: Unit,
renderState: State,
context: RenderContext
): BackStackScreen<Any> {
): BackStackScreen<Screen> {

// Our list of back stack items. Will always include the "WelcomeScreen".
val backstackScreens = mutableListOf<Any>()
val backstackScreens = mutableListOf<Screen>()

// Render a child workflow of type WelcomeWorkflow. When renderChild is called, the
// infrastructure will create a child workflow with state if one is not already running.
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package workflow.tutorial

import com.squareup.workflow1.ui.ScreenViewRunner
import com.squareup.workflow1.ui.ViewEnvironment
import com.squareup.workflow1.ui.WorkflowUiExperimentalApi
import com.squareup.workflow1.ui.control
import com.squareup.workflow1.ui.setBackHandler
import workflow.tutorial.views.databinding.TodoEditViewBinding

@OptIn(WorkflowUiExperimentalApi::class)
class TodoEditRunner(
private val binding: TodoEditViewBinding
) : ScreenViewRunner<TodoEditScreen> {

override fun showRendering(
rendering: TodoEditScreen,
environment: ViewEnvironment
) {
binding.root.setBackHandler(rendering.onBackClick)
binding.save.setOnClickListener { rendering.onSaveClick() }
rendering.title.control(binding.todoTitle)
rendering.note.control(binding.todoNote)
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
package workflow.tutorial

import com.squareup.workflow1.ui.AndroidScreen
import com.squareup.workflow1.ui.ScreenViewFactory
import com.squareup.workflow1.ui.TextController
import com.squareup.workflow1.ui.WorkflowUiExperimentalApi
import workflow.tutorial.views.databinding.TodoEditViewBinding

@OptIn(WorkflowUiExperimentalApi::class)
data class TodoEditScreen(
/** The title of this todo item. */
val title: String,
val title: TextController,
/** The contents, or "note" of the todo. */
val note: String,

/** Callbacks for when the title or note changes. */
val onTitleChanged: (String) -> Unit,
val onNoteChanged: (String) -> Unit,
val note: TextController,

val discardChanges: () -> Unit,
val saveChanges: () -> Unit
)
val onBackClick: () -> Unit,
val onSaveClick: () -> Unit
) : AndroidScreen<TodoEditScreen> {
override val viewFactory =
ScreenViewFactory.fromViewBinding(TodoEditViewBinding::inflate, ::TodoEditRunner)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ package workflow.tutorial
import com.squareup.workflow1.Snapshot
import com.squareup.workflow1.StatefulWorkflow
import com.squareup.workflow1.action
import com.squareup.workflow1.ui.WorkflowUiExperimentalApi
import workflow.tutorial.TodoEditWorkflow.Output
import workflow.tutorial.TodoEditWorkflow.Output.Discard
import workflow.tutorial.TodoEditWorkflow.Output.Save
import workflow.tutorial.TodoEditWorkflow.EditProps
import workflow.tutorial.TodoEditWorkflow.State

@OptIn(WorkflowUiExperimentalApi::class)
object TodoEditWorkflow : StatefulWorkflow<EditProps, State, Output, TodoEditScreen>() {

data class EditProps(
Expand Down Expand Up @@ -54,33 +56,20 @@ object TodoEditWorkflow : StatefulWorkflow<EditProps, State, Output, TodoEditScr
return TodoEditScreen(
title = renderState.todo.title,
note = renderState.todo.note,
onTitleChanged = { context.actionSink.send(onTitleChanged(it)) },
onNoteChanged = { context.actionSink.send(onNoteChanged(it)) },
saveChanges = { context.actionSink.send(onSave()) },
discardChanges = { context.actionSink.send(onDiscard()) }
onSaveClick = { context.actionSink.send(postSave) },
onBackClick = { context.actionSink.send(postDiscard) }
)
}

override fun snapshotState(state: State): Snapshot? = null

internal fun onTitleChanged(title: String) = action {
state = state.withTitle(title)
}

internal fun onNoteChanged(note: String) = action {
state = state.withNote(note)
}

private fun onDiscard() = action {
private val postDiscard = action {
// Emit the Discard output when the discard action is received.
setOutput(Discard)
}

internal fun onSave() = action {
internal val postSave = action {
// Emit the Save output with the current todo state when the save action is received.
setOutput(Save(state.todo))
}

private fun State.withTitle(title: String) = copy(todo = todo.copy(title = title))
private fun State.withNote(note: String) = copy(todo = todo.copy(note = note))
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
package workflow.tutorial

import com.squareup.workflow1.ui.AndroidScreen
import com.squareup.workflow1.ui.ScreenViewFactory
import com.squareup.workflow1.ui.WorkflowUiExperimentalApi
import workflow.tutorial.views.databinding.TodoListViewBinding

/**
* This should contain all data to display in the UI.
*
* It should also contain callbacks for any UI events, for example:
* `val onButtonTapped: () -> Unit`.
*/
@OptIn(WorkflowUiExperimentalApi::class)
data class TodoListScreen(
val username: String,
val todoTitles: List<String>,
val onTodoSelected: (Int) -> Unit,
val onBack: () -> Unit
)
val onBackClick: () -> Unit,
val onAddClick: () -> Unit
): AndroidScreen<TodoListScreen> {
override val viewFactory =
ScreenViewFactory.fromViewBinding(TodoListViewBinding::inflate, ::TodoListScreenRunner)
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
package workflow.tutorial

import androidx.recyclerview.widget.LinearLayoutManager
import com.squareup.workflow1.ui.LayoutRunner
import com.squareup.workflow1.ui.LayoutRunner.Companion.bind
import com.squareup.workflow1.ui.ScreenViewRunner
import com.squareup.workflow1.ui.ViewEnvironment
import com.squareup.workflow1.ui.ViewFactory
import com.squareup.workflow1.ui.WorkflowUiExperimentalApi
import com.squareup.workflow1.ui.backPressedHandler
import com.squareup.workflow1.ui.setBackHandler
import workflow.tutorial.views.TodoListAdapter
import workflow.tutorial.views.databinding.TodoListViewBinding

@OptIn(WorkflowUiExperimentalApi::class)
class TodoListLayoutRunner(
class TodoListScreenRunner(
private val todoListBinding: TodoListViewBinding
) : LayoutRunner<TodoListScreen> {
) : ScreenViewRunner<TodoListScreen> {

private val adapter = TodoListAdapter()

Expand All @@ -24,9 +22,10 @@ class TodoListLayoutRunner(

override fun showRendering(
rendering: TodoListScreen,
viewEnvironment: ViewEnvironment
environment: ViewEnvironment
) {
todoListBinding.root.backPressedHandler = rendering.onBack
todoListBinding.root.setBackHandler(rendering.onBackClick)
todoListBinding.add.setOnClickListener { rendering.onAddClick() }

with(todoListBinding.todoListWelcome) {
text = resources.getString(R.string.todo_list_welcome, rendering.username)
Expand All @@ -36,8 +35,4 @@ class TodoListLayoutRunner(
adapter.onTodoSelected = rendering.onTodoSelected
adapter.notifyDataSetChanged()
}

companion object : ViewFactory<TodoListScreen> by bind(
TodoListViewBinding::inflate, ::TodoListLayoutRunner
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,15 @@ object TodoListWorkflow : StatelessWorkflow<ListProps, Output, TodoListScreen>()
): TodoListScreen {
val titles = renderProps.todos.map { it.title }
return TodoListScreen(
username = renderProps.username,
todoTitles = titles,
onTodoSelected = { context.actionSink.send(selectTodo(it)) },
onBack = { context.actionSink.send(onBack()) }
username = renderProps.username,
todoTitles = titles.map { it.textValue },
onTodoSelected = { context.actionSink.send(selectTodo(it)) },
onBackClick = { context.actionSink.send(postGoBack) },
onAddClick = { context.actionSink.send(postNewTodo) }
)
}

private fun onBack() = action {
private val postGoBack = action {
// When an onBack action is received, emit a Back output.
setOutput(Back)
}
Expand All @@ -46,7 +47,7 @@ object TodoListWorkflow : StatelessWorkflow<ListProps, Output, TodoListScreen>()
setOutput(SelectTodo(index))
}

private fun new() = action {
private val postNewTodo = action {
// Tell our parent a new todo item should be created.
setOutput(NewTodo)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
package workflow.tutorial

import com.squareup.workflow1.ui.TextController
import com.squareup.workflow1.ui.WorkflowUiExperimentalApi

@OptIn(WorkflowUiExperimentalApi::class)
data class TodoModel(
val title: String,
val note: String
)
val title: TextController,
val note: TextController
) {
constructor(
title: String,
note: String
) : this(TextController(title), TextController(note))
}
Loading

0 comments on commit fe04b90

Please sign in to comment.