1. Introduction
In the following tutorial, we'll learn how to use the sync plugin to create a simple chat-room application.
You could achieve a similar result using plain WebSockets (see the WebSocket tutorial).
But in this tutorial, we'll take a different approach, and use Crails' Channels.
Channels are based on WebSocket, and allow both the server and each client to broadcast messages to both the server and every other connected client. The read/write access of each channel can also be restricted depending on the user. Sounds perfect for a chat application ! Let's dive in !
1.1 Preparations
Create the chatnnel
application with the following command:
$ crails new -n chatnnel -p html cd chatnnel
Then add the sync
plugin to the application:
$ crails plugins sync install
2. Public chatroom
2.1 HTML
We'll start by creating a simple view to render our chatroom.
app/views/chatroom.html
std::string @room_id;
// END LINKING
<html>
<head>
<meta charset="utf-8" />
<title>Chatnell room - <%= room_id %></title>
</head>
<body data-room-id="">
<h1>Chatnnel room</h1>
<h2><%= room_id %></h2>
<textarea placeholder="Write your message..."></textarea>
<button id="send-button">Send</button>
<table>
<tbody id="message-container">
</tbody>
</table>
</body>
</html>
Pretty simple stuff: a textarea to input text, and a table to display the message received.
We also declared a room_id
shared property: that's because we are going to allow
the application to host as many rooms as the user may create.
2.2 JavaScript
We also need some JavaScript to connect to the channel and update the message list. This is achieved using WebSockets. Open our html view again and add the following javascript below the <table> element:
app/views/chatroom.html
<script>
const socket = new WebSocket("ws://localhost:3001/chatroom/<%= room_id %>/channel");
socket.onmessage = function(event) {
const list = document.querySelector("#message-container");
const row = document.createElement("tr");
const messageCell = document.createElement("td");
messageCell.textContent = event.data;
row.appendChild(messageCell);
list.prepend(row);
};
document.querySelector("#send-button").addEventListener("click", function() {
const input = document.querySelector("textarea");
const message = input.value;
socket.send(message);
input.value = "";
});
</script>
Simple stuff again: we connect a chatroom to the /chatroom/:room_id/channel
UID, which
we will bind later in our application's router. When the user press the button, we send the textarea
content to the WebSocket, and when the WebSocket sends us a message, we add a new row to #message-container
displaying the received message.
click
event handler: it will
be appended when it comes back through the WebSocket's message
event handler.
2.2 Controller
At this point, the chatroom client is almost ready: we just need a simple controller to render the HTML we created earlier. Generate a basic controller using the following command:
$ crails scaffold controller -n chatroom
Then declare a show
action method:
app/controllers/chatroom.hpp
#pragma once
#include "app/controllers/application.hpp"
class ChatroomController : public ApplicationController
{
public:
...
// Declare the show action:
void show();
};
And add the corresponding implementation in the controller source file:
app/controllers/chatroom.cpp
#include "chatroom.hpp"
#include <crails/params.hpp>
...
void ChatroomController::show()
{
render("chatroom", {
{"room_id", params["room_id"].as<std::string>()}
});
}
2.3 Router
Lastly, we need to two routes to our router: one to serve the chatroom html, the other to serve as the endpoint for the client's WebSockets:
app/routes.cpp
#include <crails/router.hpp>
#include <crails/sync/channel_actions.hpp> // header for channel routes
#include "controllers/chatroom.hpp" // our controller
void Crails::Router::initialize()
{
match_action("GET", "/chatroom/:room_id", ChatroomController, show);
match_sync_channel("/chatroom/:room_id/channel", Crails::Sync::ChannelClient);
}
And we're done ! Build and start the server, then go at http://localhost:3001/chatroom/test-room to try it out !
3. Idenitfy clients
In our current implementations, messages are broadcasted to each client, but there are no way to tell who emitted which messages. In this chapter, we'll update our chatroom application so that each client is identified with a username.
4. Private chatroom
All our chatrooms are publicly accessible. But what if you want your channels to be only accessible to some users ? Channels can be protected so that users cannot read or/and write, using passwords.