In the packages/app-todo-list directory, we provide a simple To-do
List application running on an Interbit blockchain. The application can
add items to the to-do list, edit existing items, and toggle the
completed state of items.
This application was created using the Interbit Template app, and what follows is a description of the app and how to recreate it from the Template. The To-do List uses the same Interbit configuration as the Template. As such, to-do items are added to the user's private chain.
To get started, run the following commands in your console from the
interbit repo's root:
npm install
npm run build:modules
cd packages/app-todo-list
npm run interbit:startThe interbit:start command fires up an Interbit node which runs the
blockchain. The chain statuses and blocking actions are printed in the
console, and the message 'Updating index.html with chain data' indicates
that the blockchain node is ready. Leave this process running.
In another console run:
cd packages/app-todo-list
npm startThe start command runs the React application which has a UI to
interact with the blockchain. Open your browser to localhost:3055 to
view the To-do List app. There are 3 pages in the application, which are
accessible from the main navigation menu.
The To-do List page shows a friendly UI with a form to add items and a table displaying them. The table is initially empty, but once an item is added you can mark it as completed and edit the item's title and description.
The Private Chain page displays the private chain's state and also
provides a UI for adding and editing to-do items. Try adding a to-do
item on this page and you will see the item added to the todos array
on the left.
The Block Explorer page is an interactive tool for viewing the private chain's blocks, the chain state, actions that were dispatched to the chain, and block metadata.
You can optionally run
cd packages/app-todo-list
npm testin another console to run a test watcher. The To-do List app comes with all the tests from the Template app and tests for the to-do list private chain actions.
If you look in the packages/app-todo-list directory, you will see a
basic application built on Interbit. Refer to the Template
docs for detailed information on these
directories and files. This document only covers the changes we made to
the Template files to build our To-do List app. Specifically, we take a
closer look at the private chain actions in src/interbit/private, the
tests in src/tests/privateCovenant.test.js,
src/adapters/privateChainAdapter.js, and the React components in
src/components.
| Directory | Purpose |
|---|---|
public |
Website's public directory |
src |
Source code for the app |
src/adapters |
A set of adapters to fill UI details for the covenant forms |
src/components |
React components |
src/constants |
A file of constants that are used throughout the application (Ex. URLs) |
src/containers |
React components that are connected to Redux state |
src/css |
The css |
src/interbit |
All of the covenants needed for this application |
src/redux |
Reducers and any other Redux related files |
tests |
Jest tests |
App.js |
The main React component that loads the SPA |
exports.js |
A file to export anything that may be needed outside of this project (Ex. covenant action creators) |
index.js |
The file that creates the Redux store, attaches the Interbit middleware, and runs the SPA |
registerServiceWorker.js |
A file used by create-react-app to register a service worker |
interbit.config.js |
The Interbit configuration file |
interbit.prod.config.js |
The production environment interbit configuration file |
keyPair.js |
A file to import a public private key pair from environment variables when in production |
static.json |
Used to serve a single page application (SPA) in Heroku. This describes where to fallback if routes are not matched (Returning 404) |
In this section, we step through the code that is specific to the To-do List app. This app is a modified clone of the Template app, so we highlight any changes that were made to the Template files and any new files we've added.
We named our app app-todo-list and updated our public and private
covenant names correspondingly. See:
package.jsoninterbit.config.jsinterbit.prod.config.js
The public, private, and control chain configurations are exactly as they are in the Template app -- all we've done is rename things here. The Template app is configured so that users can dispatch actions to their private chain, so our To-do List app does the same. Users have their own private to-do lists. In the future, we may extend this example so that users can have shared lists.
Our To-do List app allows users to add items, edit items, and mark items
as complete in their to-do list. These actions are described in the
app's private covenant in src/interbit/private. We've removed the
actions included in the Template app and added the ADD_TODO,
EDIT_TODO, and TOGGLE_TODO actions.
Actions are dispatched to the chain during operation, and are passed to
the smart contract to change the
application state. Interbit follows the
Redux design pattern, so actions in the Interbit covenants are
essentially the same as actions in Redux. All the same, we'll go through
each of the files in src/interbit/private.
This is a constants file that names each of our actions, a.k.a. our action types.
const covenantName = 'app-todo-list-private'
const actionTypes = {
ADD_TODO: `${covenantName}/ADD_TODO`,
EDIT_TODO: `${covenantName}/EDIT_TODO`,
TOGGLE_TODO: `${covenantName}/TOGGLE_TODO`
}
module.exports = actionTypesWe define the action
creators here, each with
an action type (e.g. ADD_TODO) and a payload.
const actionTypes = require('./actionTypes')
const actionCreators = {
addTodo: (title, description) => ({
type: actionTypes.ADD_TODO,
payload: {
title,
description
}
})
// ...
}
module.exports = {
actionTypes,
actionCreators
}This file defines the initial private chain state and the smart
contract, which is implemented as a Redux reducer. The smart contract
defines how each action changes the application's state. It accepts
actions and a current state then generates a new state. In other words,
the smart contract does "the stuff" in an application and contains most
of the business logic. In our app, the ADD_TODO action adds an object
such as {id: 0, title: 'foo', description: 'bar', completed: false} to
the todos array in the private chain state.
Note that the state is immutable so we enforce this with the
seamless-immutable library.
const Immutable = require('seamless-immutable')
const {
cAuthConsumerCovenant,
mergeCovenants
} = require('interbit-covenant-tools')
const { actionTypes, actionCreators } = require('./actions')
const initialState = Immutable.from({
chainMetadata: { name: `To-do list application - User's private chain` },
todos: []
})
const reducer = (state = initialState, action) => {
if (action.type.endsWith('STROBE')) {
return state
}
console.log('REDUCING: ', action)
switch (action.type) {
case actionTypes.ADD_TODO: {
const { title, description } = action.payload
const todos = state.getIn(['todos'], Immutable.from([]))
const id = todos.length
const completed = false
return title
? state.set(
'todos',
todos.concat({ id, title, description, completed })
)
: state
}
// ...
default:
return state
}
}
const covenant = {
actionTypes,
actionCreators,
initialState,
reducer
}
module.exports = mergeCovenants([covenant, cAuthConsumerCovenant])Jest tests for private and control covenants are located in
src/tests/interbit. We've updated privateCovenant.test.js with tests
for the To-do List actions which have a watcher. Run npm test from the
app-todo-list directory to view the results of the tests as you make
changes to the code.
As described above, the Private Chain page displays the private chain's
state and has some rudimentary UI for adding and editing to-do items.
This is a handy development tool to interact with the private chain and
verify that the application state is updating correctly. We use the
<Covenant /> component from the interbit-ui-components package to
easily connect UI forms to the covenant actions.
The Template app comes with the scaffolding to connect the private
covenant actions to the <Covenant /> component. We modified
src/adapters/privateChainAdapter.js so that we can dispatch the
ADD_TODO and EDIT_TODO actions from the Private Chains page.
const covenant = require('../interbit/private')
const covenantName = 'Interbit To-do List Private Chain'
const addTodoActionLabel = 'Add a to-do item'
const addTodoTitleLabel = 'Enter a title *'
const addTodoDescriptionLabel = 'Enter a description'
const editTodoActionLabel = 'Edit a to-do item'
const editTodoIdLabel = 'Enter the ID of the to-do to edit'
const editTodoTitleLabel = 'Enter a new title *'
const editTodoDescriptionLabel = 'Enter a new description'
const actionCreators = {
addTodo: () => ({
type: addTodoActionLabel,
arguments: {
[addTodoTitleLabel]: '',
[addTodoDescriptionLabel]: ''
},
invoke: ({
[addTodoTitleLabel]: title,
[addTodoDescriptionLabel]: description
}) => covenant.actionCreators.addTodo(title, description)
}),
editTodo: () => ({
type: editTodoActionLabel,
arguments: {
[editTodoIdLabel]: '',
[editTodoTitleLabel]: '',
[editTodoDescriptionLabel]: ''
},
invoke: ({
[editTodoIdLabel]: id,
[editTodoTitleLabel]: title,
[editTodoDescriptionLabel]: description
}) => covenant.actionCreators.editTodo(id, title, description)
})
}
module.exports = {
covenantName,
actionCreators
}The to-do list app could still use some work. At the moment, there is no error checking, and the smart contract will accept any action with an appropriate type just fine, whether you supplied meaningful data or not. This is a very basic example of what Interbit can do. If you want to forge ahead, and write your own Interbit application, you can go ahead and do that now, or if you are not sure what you want to try next, you can try adding another action and case statement to this To-do List application.