### ----------------------------------------------------
### -- cro
### -- Licenses: Artistic-2.0
### -- Authors: Jonathan Worthington <jnthn@jnthn.net>
### -- File: docs/intro/spa-with-cro.md
### ----------------------------------------------------
### -- Chunk 1 of 2
# Building a Single Page Application with Cro

This tutorial walks through building a simple Single Page Application using
Cro as the backend. For the frontend, we'll use webpack, ES6, React and Redux.
No prior knowledge of these is required, although expect to need to do some
further reading if you want to use them well in your own applications.

The [code is available](https://github.com/croservices/sample-app-spa-react).
At various points during the tutorial, the present state is committed. The
repository history matches the tutorial exactly, so you can use it to get an
overview of the changes at each step, or to see what you missed if you're
trying to follow along by building this from scratch.

## What you'll need

* To have installed Cro (see [this guide](getstarted) for help with that)
* To have installed `npm` (the Node.js package manager, which we'll use to
  get packages we need to build the frontend); on Debian-based Linux distros
  that's just `sudo apt install npm`

## What we'll be building

So we're at a food festival. Or a beer festival. Or any event with a bunch of
stuff that we could try. But...what to try? If only there was some app where
people could leave their tips about what's hot and what's not, and we could
see them in real time! If there'd been such a thing at the last beer festival
I went to, I might have been spared that green tea beer...

So, we'll make a SPA that supports:

* Submitting new tips (a POST to the backend)
* Having the latest tips appear live (delivered over a web socket)
* Being able to agree or disagree with a tip (also a POST)
* Being able to see a list of the tips sorted most agreeable to most
  disagreeable (obtained by a `GET`)

## Stubbing the backend

Think of a creative name for the application. I'm calling mine "tipsy". Then
use `cro stub` to stub a HTTP application. To keep it simple, we'll skip HTTPS
(and thus HTTP/2.0), but will include web sockets support.

```
$ cro stub http tipsy tipsy
Stubbing a HTTP Service 'tipsy' in 'tipsy'...

First, please provide a little more information.

Secure (HTTPS) (yes/no) [no]: n
Support HTTP/1.1 (yes/no) [yes]: y
Support HTTP/2.0 (yes/no) [no]: n
Support Web Sockets (yes/no) [no]: y
```

This creates a directory `tipsy`. Now let's go into it and check the stubbed
backend runs, using `cro run`:

```
$ cro run
▶ Starting tipsy (tipsy)
🔌 Endpoint HTTP will be at http://localhost:20000/
📓 tipsy Listening at http://localhost:20000
```

We can check it using `curl` or by visiting it in the browser:

```
$ curl http://localhost:20000
<h1> tipsy </h1>
```

I like to regularly commit as I work. So, I'll create a git repository, add
a `.gitignore` file (to ignore Raku precompilation output), and commit the
stub.

```
$ git init .
Initialized empty Git repository in /home/jnthn/dev/cro/tipsy/.git/
jnthn@lviv:~/dev/cro/tipsy$ echo '.precomp/' > .gitignore
jnthn@lviv:~/dev/cro/tipsy$ git add .
jnthn@lviv:~/dev/cro/tipsy$ git commit -m "Stub tipsy backend"
[master (root-commit) ff1043a] Stub tipsy backend
 5 files changed, 99 insertions(+)
 create mode 100644 .cro.yml
 create mode 100644 .gitignore
 create mode 100644 META6.json
 create mode 100644 lib/Routes.rakumod
 create mode 100644 service.raku
```

## Serving a static page

We'll now tweak our Cro stub application to serve a HTML page. We'll
create a `static` directory, where static content to serve will go.

```
mkdir static
```

In there, we'll put an `index.html` with the following context:

```
<html>
  <head>
    <title>Tipsy</title>
  </head>
  <body>
    <h1>Tipsy</h1>
  </body>
</html>
```

Then, we'll edit `lib/Routes.rakumod` to get the `/` route to serve this file:

```
get -> {
    static 'static/index.html'
}
```

The `cro run` that we left running earlier should have automatically restarted
the service. Open the file in the browser to check that it's being served.

## Setting up the frontend build toolchain

Next up, we'll get set up for the frontend. First of all, we'll stub a
`package.json` file, which will contain our development and frontend
JavaScript dependencies. We use `npm init` and provide some answers:

```
$ npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg> --save` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
name: (tipsy) 
version: (1.0.0) 
description: Tipsy gives you tips at festivals
entry point: (index.js) 
test command: 
git repository: 
keywords: 
author: 
license: (ISC) 
About to write to /home/jnthn/dev/cro/tipsy/package.json:

{
  "name": "tipsy",
  "version": "1.0.0",
  "description": "Tipsy gives you tips at festivals",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC"
}


Is this ok? (yes) 
```

Next, let's install the webpack tool as a development dependency:

```
npm install --save-dev webpack webpack-cli
```

While that runs: what's webpack for? It helps out in various ways:

* It sets up compilation for us from modern JavaScript (with handy features
  like module imports, lambdas and `let` variable declarations) into a
  JavaScript version that works in web browsers
* It lets us use JavaScript modules, managed using the `npm` package manager,
  and have them concatenated into a single JavaScript file
* It can also help with CSS and image assets

Next up, we'll create a `webpack.config.js` in the root of the repository,
with the following contents:

```
const path = require('path');

module.exports = {
    entry: './frontend/index.js',
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'static/js')
    },
    mode: 'development'
};
```

This means that it will take `frontend/index.js` as the root of the JavaScript
frontend part of our application, follow all of its dependencies, and build
them into a bundle that will be written out to `static/js/bundle.js`. Next,
let's create those locations. First, let's stub the output location; the
`.gitignore` both ignores the generated output as well as making sure the
directory exists (since git won't track empty directories).

```
$ mkdir static/js
$ echo '*' > static/js/.gitignore
```

Next, a stub `frontend/index.js`:

```
$ mkdir frontend
$ echo 'document.write("Hello from JS")' > frontend/index.js
```

Now, we'd like a way to run webpack conveniently from our local installation of
it. One way is to edit `package.json` and add an entry to the `scripts`
section:

```
  "scripts": {
    "build": "webpack",
    "test": "echo \"Error: no test specified\" && exit 1"
  },
```

This can now be run with `npm run build`:

```
$ npm run build

> tipsy@1.0.0 build /home/jnthn/dev/cro/tipsy
> webpack

Hash: 142d15221600d8f7960f
Version: webpack 3.6.0
Time: 125ms
    Asset    Size  Chunks             Chunk Names
bundle.js  2.5 kB       0  [emitted]  main
   [0] ./frontend/index.js 32 bytes {0} [built]
```

## Serving and using the bundle

Next up, edit `static/index.html`, and just before the closing `</body>` tag
add:

```
<script src="js/bundle.js"></script>
```

Finally, we need to serve the JavaScript. Edit `lib/Routes.rakumod` and add a new
route like this (which will serve anything under `static/js`, preparing us
for multiple bundles in the future should we need that):

```
get -> 'js', *@path {
    static 'static/js', @path
}
```

Refresh in the browser, and `Hello from JS` should appear on the page.

Last but not least, we need to ignore `node_modules`, and then can commit the
frontend stubbing:

```
$ echo 'node_modules/' >> .gitignore
$ git add .
$ git commit -m "Stub JavaScript application"
[master 199866c] Stub JavaScript application
 6 files changed, 40 insertions(+), 1 deletion(-)
 create mode 100644 frontend/index.js
 create mode 100644 package.json
 create mode 100644 static/index.html
 create mode 100644 webpack.config.js
```

## Starting to build the backend

To keep things simple, we'll build an in-memory model. There's all kinds of
ways we could factor it, but the key thing to keep in mind is that a web
application is a concurrent system, and in Cro requests are processed on a
thread pool. This means two requests may be processed at the same time!

To handle this, we'll use the `OO::Monitors` module. A monitor is like a class,
but it enforces mutual exclusion on its methods - that is, only one thread
may be inside the methods on a particular instance at a time. Thus, provided
we don't leak out our internal state (making defensive copies if we do have
to return parts of it), the state inside of the  monitor object is protected.

First, let's add `OO::Monitors` to our `META6.json` depends section, so that
part of the file looks like:

```
  "depends": [
    "Cro::HTTP",
    "Cro::WebSocket",
    "OO::Monitors"
  ],
```

To make sure you really have that module available, ask zef to install it if
it's missing:

```
$ zef install --deps-only .
```

We'll put our business logic in a separate module, `lib/Tipsy.rakumod`, and write
some tests for it in `t/tipsy.t`. First, let's stub out the API for our
business/domain logic:

```
use OO::Monitors;

class Tip {
    has Int $.id;
    has Str $.tip;
    has Int $.agreed;
    has Int $.disagreed;
}

monitor Tipsy {
    method add-tip(Str $tip --> Nil) { ... }
    method agree(Int $tip-id --> Nil) { ... }
    method disagree(Int $tip-id --> Nil) { ... }
    method latest-tips(--> Supply) { ... }
    method top-tips(--> Supply) { ... }
}
```

Here, `Tip` is an immutable object representing a tip together with the number
of agrees and disagrees. The `Tipsy` class has a bunch of methods where we'll
implement the various operations. The first three are mutating operations. The
next, `latest-tips`, will be a `Supply` of Tip objects, emitting every time a
new tip is added. When it is first tapped, we will always emit the latest 50
tips. The `top-tips` method returns a Supply that will emit sorted lists of
the top 50 tips every time the rankings change. Don't try to remember all of
that, we'll come back to them one at a time.

Next up is to make this available to our routes. We could make the instance in
`Routes.rakumod`, but that will make it hard to test our routes in isolation of
the business logic. Instead, we'll make the sub in `Routes.rakumod` take an instance
of the business logic object as a parameter:

```
use Cro::HTTP::Router;
use Cro::HTTP::Router::WebSocket;
use Tipsy;

sub routes(Tipsy $tipsy) is export {
    route {
        get -> {
            static 'static/index.html'
        }
    ...
}
```

And then set it up in the `service.raku` entry point, by:

1. Using the module
2. Making an instance of it

The `service.raku` file will end up looking like this:

```
use Cro::HTTP::Log::File;
use Cro::HTTP::Server;
use Routes;
use Tipsy;

my $tipsy = Tipsy.new;
my $application = routes($tipsy);

my Cro::Service $http = Cro::HTTP::Server.new(
    http => <1.1>,
    host => %*ENV<TIPSY_HOST> ||
        die("Missing TIPSY_HOST in environment"),
    port => %*ENV<TIPSY_PORT> ||
        die("Missing TIPSY_PORT in environment"),
    :$application,
    after => [
        Cro::HTTP::Log::File.new(logs => $*OUT, errors => $*ERR)
    ]
);
$http.start;
say "Listening at http://%*ENV<TIPSY_HOST>:%*ENV<TIPSY_PORT>";
react {
    whenever signal(SIGINT) {
        say "Shutting down...";
        $http.stop;
        done;
    }
}
```

## Adding a tip and latest tips

Next, let's write tests for adding a tip and seeing it show up in the latest
tips. Here goes with `t/tipsy.t`:

```
use Tipsy;
use Test;

my $tipsy = Tipsy.new;
lives-ok { $tipsy.add-tip('The lamb kebabs are good!') },
    'Can add a tip';
lives-ok { $tipsy.add-tip('Not so keen on the fish burrito!') },
    'Can add another tip';
given $tipsy.latest-tips.head(2).list -> @tips {
    is @tips[0].tip, 'Not so keen on the fish burrito!',
        'Correct first tip retrieved on initial tap of latest-tips';
    is @tips[1].tip, 'The lamb kebabs are good!',
        'Correct second tip retrieved on initial tap of latest-tips';
}

react {
    whenever $tipsy.latest-tips.skip(2).head {
        is .tip, 'Try the vanilla stout for sure',
            'Get new tips emitted live';
    }
    $tipsy.add-tip('Try the vanilla stout for sure');
}

done-testing;
```

The first part tests that we can add two tips, and that if we tap the `Supply`
of latest tips then we are given those two straight away. The second part is a
bit more involved: it checks that if we tap the latest tips `Supply`, and then a
new tip is added, then we will also be told about this new tip.

Now to make them pass! Here's the implementation in the `monitor`:

```
monitor Tipsy {
    has Int $!next-id = 1;
    has Tip %!tips-by-id{Int};
    has Supplier $!latest-tips = Supplier.new;

    method add-tip(Str $tip --> Nil) {
        my $id = $!next-id++;
        my $new-tip = Tip.new(:$id, :$tip);
        %!tips-by-id{$id} = $new-tip;
        start $!latest-tips.emit($new-tip);
    }

    method latest-tips(--> Supply) {
        my @latest-existing = %!tips-by-id.values.sort(-*.id).head(50);
        supply {
            whenever $!latest-tips {
                .emit;
            }
            .emit for @latest-existing;
        }
    }
    
    # The other unimplemented methods go here
}
```

We keep a counter of IDs to give each tip its own unique ID. We have a hash
mapping IDs to `Tip` objects. We then have a `Supplier` that we will use to
notify any interested parties when there are new tips.

The `add-tip` method's first 3 lines are straightforward, the final one is a
little curious: why the `start`? The answer is that, with supplies, the sender
pays for the cost of distributing the message, but we don't want to tie up the
monitor - which does mutual exclusion - with all of that work. So, we throw in
a `start` to dispatch the notifications asynchronously.

The `latest-tips` method also needs a little care. Remember that anything we
return from a `monitor` is not protected by the mutual exclusion. Thus, we
should make a copy of the latest existing tips outside of the `supply` block,
while the monitor is protecting `%!tips-by-id`. Then, when the returned
`supply` block is tapped, we subscribe to the latest tips, and then emit each
of the latest existing ones.

And with these, our tests pass. Progress!

```
$ git add .
$ git commit -m "Implement first part of business logic"
[master 6e352c6] Implement first part of business logic
 6 files changed, 70 insertions(+), 4 deletions(-)
 create mode 100644 lib/Tipsy.rakumod
 create mode 100644 t/tipsy.t
```

## Setting up React

Now it's time to get back to the frontend. We'll use React for that. React
gives us a virtual DOM, which we can rebuild every time something changes. It
then diffs it against the current real DOM, and applies the changes. This lets
us work in a more functional style. React components are written using JSX,
an XML-like syntax embedded in JavaScript. First we need to set up compiling
that. Here are the new development dependencies:

```
$ npm install --save-dev babel-loader core-js @babel/core@^7 @babel/preset-react
```

Next, we need to create a `.babelrc` file, saying to use this react preset
(Babel is the thing used to turn modern JavaScript into browser-compatible
JavaScript). It should simply contain:

```
{
  "presets" : ["@babel/preset-react"]
}
```

Finally, the `webpack.config.js` needs updates to use this. After the
changes, it should look as follows:

```
const path = require('path');

module.exports = {
    entry: './frontend/index.js',
    output: {
        filename: 'bundle.js',
        path: path.resolve(__dirname, 'static/js')
    },
    mode: 'development',
    module : {
        rules : [
            {
                test: /\.js/,
                include: path.resolve(__dirname, 'frontend'),
                loader: 'babel-loader'
            }
        ]
    }
};
```

Try an `npm run build`. It should survive, though we aren't yet using any of
the new React support yet.

Next the build toolchain is set up, it's time to install the React modules we
will use in our frontend. Here goes:

```
$ npm install --save react react-dom
```

And with those, we can edit our `index.js` file to look like this:

```
import React from 'react';
import {render} from 'react-dom';

var App = () => <p>Hello React!</p>;
render(<App />, document.getElementById('app'));
```

And add a `div` tag with the ID `app` to our `index.html`:

```
<html>
  <head>
    <title>Tipsy</title>
  </head>
  <body>
    <h1>Tipsy</h1>
    <div id="app"></div>
    <script src="js/bundle.js"></script>
  </body>
</html>
```

Run `npm run build` again, refresh, and it should say `Hello React!`.

```
$ git add .
$ git commit -m "Setup react"
[master 0ffa260] Setup react
 5 files changed, 27 insertions(+), 1 deletion(-)
 create mode 100644 .babelrc
```

## Adding some components

Next, let's sketch out the UI a little more. Replace the existing `App`
component with this:

```
var SubmitTip = () => (
    <div>
        <h2>Got a tip?</h2>
        <div>
            <textarea rows="2" cols="100" maxLength="200" />
        </div>
        <input type="button" value="Add Tip" />
    </div>
);

var LatestTips = () => (
    <div>
        <h2>Latest Tips</h2>
        TODO
    </div>
);

var App = () => (
    <div>
        <SubmitTip />
        <LatestTips />
    </div>
);
```

Another `npm run build`, refresh, and it should show something that looks a
lot like one would expect from the UI. But how do we get data to populate the
UI? And how do we do stuff when the button is clicked? For that, we'll bring
in the final piece of the client side puzzle: Redux.

## Redux

There's [a good tutorial](http://redux.js.org/) on Redux on its site, and I
won't try and repeat it all here, but I'll try to summarize what Redux does.
React gives us a way to render a virtual DOM each time something changes. To
make that useful, we need some kind of object containing the current state
that should be rendered onto the UI. Redux is a container for that state, and
gets us to organize changes to it using reducers. That is to say, we never
really update state, we just produce a new one derived from the current one,
plus an action.

First, let's add the `redux` and related dependencies.

```
$ npm install --save redux redux-thunk react-redux
```

Next up, we need to define some actions. Actions correspond to state changes
on the page. We'll create just two for now:

* `CHANGE_TIP_TEXT` - when the text of the tip the user is typing changes
* `ADD_TIP` - when the Add Tip button is pressed

In a `frontend/actions.js` file, we do this:

```
export const CHANGE_TIP_TEXT = 'CHANGE_TIP_TEXT';
export const ADD_TIP = 'ADD_TIP';

export function changeTipText(text) {
    return { type: CHANGE_TIP_TEXT, text };
}
export function addTip() {
    return { type: ADD_TIP };
}
```

The constants are names of actions, and the functions are known as "action
creators": they create an object with a `type` property and, optionally, some
data.

Next, we need a reducer. Reducers take current state, and calculate and return
a new state. They never mutate the state and never do any side-effects (such
as network I/O). They are pure calculation. Our state will, for now, be very
simple: just the content of the text box.

Writing reducers is much more convenient when we have the upcoming spread
operator in JavaScript; it's a prefix `...`, and works much like Raku's
prefix `|` operator for flattening. Since we already have a build toolchain,
we can add it by installing another syntax transform:

```
npm install --save-dev babel-plugin-transform-object-rest-spread
```

And adding it to our `.babelrc`:

```
{
    "presets" : ["es2015","react"],
    "plugins" : ["transform-object-rest-spread"]
}
```

With that done, here's our reducer, placed in `frontend/reducer.js`:

```
import * as ActionTypes from './actions';

const initialState = {
    tipText: ''
};
export function tipsyReducer(state = initialState, action) {
    switch (action.type) {
        case ActionTypes.CHANGE_TIP_TEXT:
            return { ...state, tipText: action.text };
        case ActionTypes.ADD_TIP:
            return { ...state, tipText: '' };
        default:
            return state;
    }
}
```

Now it's time to wire it up to React. The overall flow will be:

1. Something happens on the UI (text changed, add tip button pressed)
2. An action object is created
3. It's passed into the reducer, which produces a new state
4. The new state is turned into properties, which are then used to build up a
   React virtual DOM
5. React takes care of applying any updates to the real DOM, thus reflecting
   our changes

Back in `frontend/index.js`, we'll import our actions and reducer, together
with some bits from the `redux` and `react-redux` libraries:

```
import { createStore } from 'redux';
import { Provider, connect } from 'react-redux';
import * as Actions from './actions';
import { tipsyReducer } from './reducer';
```

Next up, we'll create a Redux store, which is the thing that holds the latest
version of the state produced by the reducer. We create it simply by passing
our reducer to `createStore`:

```
let store = createStore(tipsyReducer);
````

Next up, we need to map the state from the store into properties that will be
available in our React components, and also produce some functions that will
also be made available in those properties, and will dispatch actions.

```
function mapProps(state) {
    return state;
}
function mapDispatch(dispatch) {
    return {
        onChangeTipText: text => dispatch(Actions.changeTipText(text)),
        onAddTip: text => dispatch(Actions.addTip())
    };
}
```

With those ready, we can complete the integration of Redux with React, which
looks like this:

```
let ConnectedApp = connect(mapProps, mapDispatch)(App);
render(
    <Provider store={store}>
        <ConnectedApp />
    </Provider>,
    document.getElementById('app'));
```

The `connect` function makes the properties from the state in the store
available to the `App` React component, and wrapping it in `Provider` makes
the state from the `store` available in order for that to happen.

Last but not least, we can update our components:

```
var SubmitTip = props => (
    <div>
        <h2>Got a tip?</h2>
        <div>
            <textarea rows="2" cols="100" maxLength="200"
                value={props.tipText}
                onChange={e => props.onChangeTipText(e.target.value)} />
        </div>
        <input type="button" value="Add Tip" onClick={props.onAddTip} />
    </div>
);

var App = props => (
    <div>
        <SubmitTip tipText={props.tipText}
            onChangeTipText={props.onChangeTipText}
            onAddTip={props.onAddTip} />
        <LatestTips />
    </div>
);
```

After `npm run build`, and a refresh in the browser, we should notice that
hitting the Add Tip button after typing will clear the text box. Our actions
and reducer are running. Phew!

For completeness, here is the entire `frontend/index.js` at this stage:

```
import React from 'react';
import { render } from 'react-dom';
import { createStore } from 'redux';
import { Provider, connect } from 'react-redux';
import * as Actions from './actions';
import { tipsyReducer } from './reducer';

var SubmitTip = props => (
    <div>
        <h2>Got a tip?</h2>
        <div>
            <textarea rows="2" cols="100" maxLength="200"
                value={props.tipText}
                onChange={e => props.onChangeTipText(e.target.value)} />
        </div>
        <input type="button" value="Add Tip" onClick={props.onAddTip} />
    </div>
);

var LatestTips = () => (
    <div>
        <h2>Latest Tips</h2>
        TODO
    </div>
);

var App = props => (
    <div>
        <SubmitTip tipText={props.tipText}
            onChangeTipText={props.onChangeTipText}
            onAddTip={props.onAddTip} />
        <LatestTips />
    </div>
);

function mapProps(state) {
    return state;
}
function mapDispatch(dispatch) {
    return {
        onChangeTipText: text => dispatch(Actions.changeTipText(text)),
        onAddTip: text => dispatch(Actions.addTip())
    };
}

let store = createStore(tipsyReducer);
let ConnectedApp = connect(mapProps, mapDispatch)(App);
render(
    <Provider store={store}>
        <ConnectedApp />
    </Provider>,
    document.getElementById('app'));
```

It's commit time again.

```
$ git add .
$ git commit -m "Wire up Redux"
[master 3478433] Wire up Redux
 5 files changed, 83 insertions(+), 7 deletions(-)
 create mode 100644 frontend/actions.js
 rewrite frontend/index.js (81%)
 create mode 100644 frontend/reducer.js
```

## POSTing to the backend

Finally, it's time to get adding a tip in the frontend calling the backend!
We'll need to do two things:

* Write a POST handler in the Cro backend
* Find a way to have our Redux action result in a POST

First for the Cro part. In `lib/Routes.rakumod` we add the following route
implementation:

```
post -> 'tips' {
    request-body -> (:$text) {
        $tipsy.add-tip($text);
        response.status = 204;
    }
}
```

Next up, the JavaScript part. The question is where to put the network bit?
Our reducer should be pure. The answer is the `redux-thunk` module that we
installed earlier, but didn't yet use. This allows us to write action creators
that return a function. This function will be passed the dispatcher, and it
can call it to dispatch actions. Typically, we will dispatch one action when an
asynchronous operation starts, which we can use to indicate that on the UI,
and a further one once it has completed, so we can again indicate that the
operation completed in the UI.

First, let's setup `redux-thunk`, which is a piece of middleware. Back in
`frontend/index.js`, add:

```
import thunkMiddleware from 'redux-thunk';
```

And then change:

```
import { createStore } from 'redux';
```

To also import `applyMiddleware`:

```
import { createStore, applyMiddleware } from 'redux';
```

Finally, change:

```
let store = createStore(tipsyReducer);
```

To apply the middleware also:

```
let store = createStore(tipsyReducer, applyMiddleware(thunkMiddleware));
```

Next, in `frontend/actions.js`, we'll refactor the add tip action creator
function to be a thunk action:

```
export function addTip() {
    return dispatch => {
        dispatch({ type: ADD_TIP });
    };
}
```

Note that this doesn't yet change anything; build it and the behavior should
be just the same. Now that we've done this, however, we can see that it will
be possible to do this dispatch in a callback after the network operation.

There's of course dozens of good ways to deal with asynchronous operations
in JavaScript, and if you are seriously building single page applications I
strongly recommend looking into using a promise library. There's also Redux
middleware that will integrate with that. For now, we'll do the simplest
possible thing: use jQuery and a callback. First, let's add jQuery as a
dependency:

```
$ npm install --save jquery
```

Then we'll import it in `frontend/actions.js`:

```
import $ from 'jquery';
```

And do the POST to the backend:

```
export function addTip() {
    return (dispatch, getState) => {
        $.ajax({
            url: '/tips',
            type: 'POST',
            contentType: 'application/json',
            data: JSON.stringify({ text: getState().tipText }),
            success: () => dispatch({ type: ADD_TIP })
        });
    };
}
```

Reload it, maybe open your browser's development tools (F12 usually), flip to
the Network tab, and click Add Tip. All being well, you'll observe the request
was made and it produced a 204 response. Alternatively, you may like to stop
the `cro run` and instead do `cro trace`; type a message, click Add Tip again,
and you should see the request body dumped in the trace output.

```
...
65 63 74 69 6f 6e 3a 20 6b 65 65 70 2d 61 6c 69  ection: keep-ali
76 65 0d 0a 0d 0a 7b 22 74 65 78 74 22 3a 22 74  ve....{"text":"t
72 79 20 74 68 65 20 62 65 65 66 22 7d           ry the beef"}
```

## The latest tips websocket

There's a variety of ways that we could write up the websocket, but we'll use
`redux-websocket-action`. This allows us to send Redux actions over the
websocket to the client, which is neat (although, unfortunately, does mean we
are coupling our backend to the use of this library in the frontend, which is
worth some questioning).

First, let's install the library in the frontend:

```
$ npm install --save redux-websocket-action
```

Over in `index.js`, we import it:

```
import WSAction from 'redux-websocket-action';
```

And set it up, like this:

```
let host = window.location.host;
let wsAction = new WSAction(store, 'ws://' + host + '/latest-tips', {
    retryCount:3,
    reconnectInterval: 3
});
wsAction.start();
```

Over in `frontend/actions.js`, we'll add one more action type (but no action
creation function, as it will originate on the server):

```
export const LATEST_TIP = 'LATEST_TIP';
```

Then we'll update our reducer, to prepend each incoming tip to a list of
latest tips, so `frontend/reducer.js` ends up as:

```
import * as ActionTypes from './actions';

const initialState = {
    tipText: '',
    latestTips: []
};
export function tipsyReducer(state = initialState, action) {
    switch (action.type) {
        case ActionTypes.CHANGE_TIP_TEXT:
            return { ...state, tipText: action.text };
        case ActionTypes.ADD_TIP:
            return { ...state, tipText: '' };
        case ActionTypes.LATEST_TIP: {
            let tip = { id: action.id, text: action.text };
            return {
                ...state,
                latestTips: [tip, ...state.latestTips]
            };
        }
        default:
            return state;
    }
}
```

And then update the React components in `frontend/index.js` to render the
latest tips:

```
var LatestTips = props => (
    <div>
        <h2>Latest Tips</h2>
        <ul>
        {props.tips.map(t => <li key={t.id}>{t.text}</li>)}
        </ul>
    </div>
);

var App = props => (
    <div>
        <SubmitTip tipText={props.tipText}
            onChangeTipText={props.onChangeTipText}
            onAddTip={props.onAddTip} />
        <LatestTips tips={props.latestTips} />
    </div>
);
```

`App` is updated simply to pass the tips to the `LatestTips` component. And
with that, the client-side additions are done.

Now for the backend, which is just a single addition in `Routes.rakumod`. We clear
out the websocket stub that was generated for us, and replace it with code to
take the `latest-tips` Supply from the `Tipsy` business logic object, turn the
events into appropriate JSON, and emit them:

```
get -> 'latest-tips' {
    web-socket ->