In this dojo, we are going to build a small Twitter client using the reactive loop pattern.
Our client will display a live feed of tweets with simple filtering abilities. It will also asynchronously highlight semantic entities (places, people, etc) and allow adding tweets to a “pinned” list.
First install the dependencies (you have NodeJS, right?):
$ npm install
Then start the local file server:
$ npm run start
Optionally, you can also run the watch
script separately to get
LiveReload:
$ npm run watch
You should be ready to go, just hit and get coding!
Open the app.js
file in your favourite editor.
Tips:
- The code is written in ES6, if anything is unclear check the docs or ask questions!
- Constants or functions whose name ends in
$
(e.g.tweets$
) denote observable streams, as a convention.
Let's start by displaying a live stream of tweets in the Timeline.
You can see in the view
function that we call the getTweetStream$
function to get a stream of tweets (Note: each element in the stream
is an Array of tweets, not a single tweet!). This stream is passed
to the columnsComponent
, which itself passes it to the
columnComponent
.
First, replace the dummy definition of tweetList$
with one that
renders the tweets$
(see TODO [#1a]
). This should make tweets
appear under your Timeline.
Then, refactor the tweetElement
function to use the more complete
markup (see TODO [#2b]
). This should make tweets look nicer as in
the screenshot above.
At the top of the page, a form (filtersComponent
) is displayed with
two input fields:
- A filter query
- An option to exclude @-replies
The components export their respective models as streams (query$
and
exclReplies$
). These are also references in the main view
function under constants of the same name.
Change the dummy definition of filteredTweets$
in the main view
function to apply the query$
and exclReplies$
filters (see TODO [#2]
). Once done, you should see the Timeline change when using the
filters.
We want to be able to pin any tweet from the Timeline by clicking it. Pinned tweets should appear in the Pinned column, most recently added first.
First, in the columnComponent
, let's update the view$
function to
push the tweet object of any tweet element clicked onto the
activate$
stream (see TODO [#3a]
). This is exposed as the
activate$
extent, which for the timeline is translated to the pin$
intent on the columnsComponent
.
Define pinnedTweets$
in terms of the pin$
intent (see TODO [#3b]
). Once completed, clicking a tweet in the Timeline column
should make it appear in the Pinned column.
Then, update the pinnedTweets$
definition to also include the
unpin$
intent (see TODO [#3c]
). Once completed, clicking a tweet
in the Pinned columns will make it disappear.
Real web hipsters are also semantic nerds (believe me). So let's highlight semantic entities in the tweets, using an external API to do the entity extraction.
The analyseEntities$
takes a string and returns a list of entities
asynchronously, as a stream. Call this function on each tweet before
rendering it (see TODO [#4a]
). Then pass the returned entities as
second parameter to the tweetElement
function and use them to
highlight the words in the tweet (see TODO [#4b]
).
Use the mousedown
, mousemove
and mouseup
events as streams to
allow dragging tweets from the Timeline to the Pinned column to pin
them.
I've not actually tried that one, so good luck :-)