Shiny chat in few lines of code
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
Intro
We live in a society heavily reliant upon communication, networking and information exchange. Think about all the chat apps that have recently popped out: WhatsApp, Messenger, Skype, Viber, Slack (not to mention Snapchat or Telegram). They have engrained themselves into our daily lives. You’d be hard pressed to find someone who doesn’t use at least one of these apps on a regular basis; with some using all of them! This leads to the obvious conclusion that written communication has become an integral part of our lives.
You may be thinking that, from a software developers perspective, creating communication apps is difficult, tedious and time consuming. I’m going to convince you otherwise.
We’re going to build a shiny chat app in no more than 15 minutes and less than 100 lines of R code!
App’s architecture
Let’s start with our chat app design. The focus is going to be basic functionality. All we need for a functioning chat app is a message window (1) to store previous comments, a text input (2) and a send button (3). Let’s also add another text input (4) to identify our users with some nicknames.
Having laid out out the desired functionality, the question is where to store our data? A text file could be fine, but periodical reading and writing would become unbearable for larger apps. Note that our problem is asynchronous – we want to send a text message only when the user presses a button. The message should then immediately appear on the other user’s screen. You may have heard of the RethinkDB open-source database, which is based on JSONs. Its asynchronous nature makes it perfect for realtime apps. Feel free to visit their documentation website if you want to learn more about RethinkDB.
For the purpose of this development let’s assume that our database will include records containing: user
, text
and time
fields.
Another more important problem is which framework should we choose to build the app? Although there are several obvious solutions, we are going to take a closer look at R and Shiny. With shiny.collections
package it will be as easy as making pie chart in ggplot
.
Can’t wait to see how that’s possible? Let’s move on to the code!
Gimme some code
Chat UI
At the very beginning, let’s create an R script chat.R
, which will contain our chat code. As I mentioned above, we will use shiny
and shiny.collections
packages. They should be attached first.
You can install the latest version of shiny.collections
via devtools
.
We might need some other libraries but can add them later.
As you probably know, each Shiny App consists of ui
, which contains the app’s layout and server
for the app’s logic.
First, we implement the ui
part of the app, following the mock-up above.
Create an empty server if you want to see the current state of your app:
and add shinyApp
creator at the end of the file.
After running it you should see something like this:
App’s logic
Great! Now that we have the chat design, we can start thinking of adding some functionality. We should ensure that our database is up and running before engaging the items we’ve created so far. If you have RethinkDB properly installed, using it is a piece of cake. Just type in your shell:
If the final line of your shell’s output looks similar to the one below, you are ready to use shiny.collections
.
Server ready, "user_QTG8WL_n0v" 67f1e11d-0acb-4fe9-ac1c-811b9bbecb66
The first step is to connect a database. Add the following line just before the server definition.
(I am using double colon notation here to avoid ambiguity about which functionality comes from standard shiny
and which from shiny.collections
package).
By default shiny.collections
connects to a database named “shiny”, or creates it if it’s not present.
The initial content in our server will be:
It makes a collection from a table named chat
. It will be empty until we fill it in.
It’s high time to focus on UI elements. Let’s work from top to bottom. The first component is the username field (4). It should definitely not be empty because we need to identify our users somehow. Let’s fill it with some random values from function get_random_username
to start.
(You can add all function definitons before ui
and server
in your script).
The following command added to server will update username_field
with the output of the function while we initialize the chat.
Now, we can add some action to the send button:
First thing to do is to form a data structure to send to our database an R list new_message
:
Then we use insert
function from shiny.collections
:
and clear text input.
Once everything is put together, it should look like this:
The data from database will be displayed in chatbox
(UI item (1)).
The observeEvent
function allows us to observe chat$collection
data and render div
with messages only when it changes. There are two conditions here: empty collection and collection with content. Test it with the following logical statement !is_empty(chat$collection)
. It employs is_empty
function from the purrr
package (so don’t forget to add library(purrr)
at the beginning of your R script). When it’s empty, your app should simply return a span with an “Empty chat” notification. If not, the chat should display messages with neat formatting. The render_msg_divs
function is responsible for that:
I must admit that there is some magic going on there. We use dplyr
and purrrlyr
to make the code tidy. Our goal is to create a div with messages listed one after another. Using div(class = "ui very relaxed list", ...)
is an easy way to achieve this.
As our collection
comes from unstructured noSQL database we first need to sort all items by time collection %>% arrange(time)
. Next, for each row we create div named “item” with “header” being username (field user from our chat$collection
) and “description” being content of the user’s message (field text
).
In order to apply it to every row, we use by_row
function from purrrlyr
package. The function returns a data.frame
with an additional column named .out
with results of the operation applied to respective row. With syntax %>% {.$.out}
we extract only results of that operation as a list.
To sum up, whole shiny server code should look like this:
We’re pretty much ready to run our chat but should consider introducing some amendments beforehand.
Improving the view
According to our mock-up with design, the box with messages should be fixed height rectangle but it’s currently just a list. We can fix this by adding this CSS code:
#chatbox {
padding: .5em;
border: 1px solid #777;
height: 300px;
overflow-y: scroll;
}
To define our customized style in our shiny app we need to add tags$head()
at the beginning of our fluidPage
definition in UI. Then we insert CSS code. This can be done in two ways: by using HTML
command from shiny
:
or adding a path to CSS file.
Commonly, for shiny apps, all external files are stored in a www/
folder.
We haven’t finished yet. Our chatbox
div has the proper size now, but it is unable to automatically scroll down after receiving a new message. Another issue is that the user is forced to press the send
button every time he or she finishes typing a new message. Most modern chat apps allow you to proceed after pressing ENTER. There’s no need for our app to be worse in that regard. We can easily introduce those two functionalities with the following JavaScript code (taken from ShinyChat example: look at Not less important section).
You can add that script to UI’s head with tags$script
command:
Launching the app
Awesome! You’re ready to launch the chat. If you followed the previous steps carefully you should see a view like this:
Now it’s your turn! Have fun and try to upgrade the chat by adding new functionalities or changing the style.
Not less important
You can find the entire code in chat.R
script published in shiny.collections repository in examples folder.
The main inspiration for this demo came from a ShinyChat example from Shiny Gallery. I decided to follow this idea to show that with shiny.collections
you can make it faster and easier (130 vs 60 of lines of pure R code).
R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.