Start a Basic Chat
Make sure that you have followed the steps for creating the users/channels in the previous section. We will be using this data now to integrate your app with the Mitter.io platform
Setting up the Mitter.io SDK
Before you consume any Mitter.io APIs, you need to setup the Mitter object. In your index.js
file, add the following lines:
For the <mitter-api-url>
, use the following value:
If you're using the cloud hosted solution, you can omit the
mitterApiBaseUrl
key in the config or explicitly set it tohttps://api.mitter.io
If you're running it as a docker container set it to
http://localhost:<port>
where the port is port forwarded by docker for11902
. To find out which port it is, run the following commanddocker port $(docker ps --filter expose=11901-11903/tcp --format "")
For the <distributor-url>
, use the following value:
If you're using the cloud hosted solution, you can omit the
weaverUrl
key in the config or explicitly set it tohttps://weaver.mitter.io
If you're running it as a docker container set it to
http://localhost:<port>
where the port is port forwarded by docker for11903
. To find out which port it is, run the following commanddocker port $(docker ps --filter expose=11901-11903/tcp --format "")
Your application ID can be fetched from the Mitter.io Dashboard. Once you have this set up, you now need to pass in the user authorization. You can pick the correct user authorization for the user authorization map we had created earlier.
We will also need to pass the mitter
object we created to the App
component so that it can fetch the user data and render it. At the end of this, your index.js
file should look something like:
Getting the list of participated channels
We now want to get the list of channels for a user and then render them in our app. To do so, we'll have to make a couple of changes in our application. First, we'll have to move the channel messages object that we created to a variable that can be changed and propagated to the ChannelComponent
. We'll move it to the state for the App
component. Also, we'll pass on the Mitter object to the the ChannelComponent
as we will need to send messages later on.
Do note that we have even modified the render()
function to now pass the channelMessages
prop from App
via a state rather than the hard-coded variable. If you reload your application, you'll see a blank page. In your App
component, we will now fetch a list of channels for the user. We will do this in the componentDidMount()
method:
NOTE Do note that for a messaging-based application, the architecture of this application is not a recommended one. Ideally, you should be using a state management system like
redux
orflux
, but for the sake of simplicity we are not using this in the application so that we can focus on introducing Mitter.io concepts. There is also a packagemitter-redux
currently in alpha that builds atop redux and handles all intricacies of state management which should be used in production apps.
In the code above, we are transforming a response we get from Mitter.io, of the form:
to something of the form:
Which is basically a map of channel IDs to an empty array of messages. We will be adding messages to this object as we get them from the Mitter.io pipeline.
Reload the page and you should see the channels listed for the current selected user.
Change the url from http://localhost:3000/user/@john
to http://localhost:3000/user/@candice
and you should now see only one channel (#roadtrip
) as opposed to two channels earlier.
Listening to messages and populating them in a channel
Now, we will move on to setup the next phase of the project, where we listen to incoming events (in this case specifically, messages) and render them in our application. To listen to pipeline payloads (that's what Mitter calls events sent on different front-end mechanisms), you need to subscribe to them. So, add the following lines in the componentDidMount()
function (also pay attention to the additional import isNewMessagePayload
at the top of the file):
Adding this new message to our state is quite straightforward now. This is how the newMessage
method should now look:
A quick description of what's going on here (follow the numbered labels in the code):
We extract the channel ID from the payload. This is the channel that the message was sent in.
We are checking if the message already exists for the same ID in our
prevState
. While you may not encounter this frequently, Mitter.io might occasionally send duplicate messages on a payload. This usually happens when Mitter.io cannot confidently determine that a message delivery has occurred, but it might still have propagated. Also, the current implementation performs an entire iteration of the messages in a channel, which might not be very efficient. As an exercise to the reader, modify this to a store backed by a hashing algorithm.We now
concat
this message on to the list of messages for the given channel.
NOTE There are certain caveats with this approach, notably that you might get receive payloads for messages for which you do not have a channels object yet. This could happen if a user was added to a channel after the participated channels were fetched. While such a situation will not arise in our setup, production apps need to always be resilient to partial state and must reconstruct the state in whatever form they can from the available events.
This is pretty much it! However, these changes will not result in you seeing anything, because no messages are being sent. In the next section lets wire it up to send messages.
Sending messages
To send messages, we'll have to wire up the Send
button in our ChannelComponent
. We'll add a few methods, namely sendMessage()
and updateTypedMessage
to ChannelComponent
. Also, we'll set up the handlers on the input fields as we usually do for any React App. The input components will now look like this:
We'll modify our state to accommodate changes for the input field and also make the appropriate function binds so that we can use them as callbacks:
And the functions to now send the messages:
The updateTypedMessage
is your standard message to store the state of an input field, and have a way to control it. Let us look into what we are doing in the sendMessage
function. Pay attention to the numbered labels in the code:
When we send a message, we would like to clear the input field so that the user can type their next message
We would also like to re-focus the
messageInput
field (this property is set in the ref callback of the<input>
field)We now use the message client to send a message. This message contains the basic minimum fields required to send a message. While the
senderId
,textPayload
have been discussed before,timelineEvents
are something new. Let's discuss them for a while.
A TimelineEvent
is used to record events that occur for a given entity. Mitter.io supports timeline events for Channels
and Messages
. For example, this is what is used to store and transmit read/delivered receipts. You are free to use any type of timeline events and interpret them as you wish, with the exception that they may not start with mitter.
or io.mitter.
. Also, any message that is sent must have a mitter.mtet.SentTime
timeline event attached to it. The server then attaches another timeline event recording the server receive time, synchronized to the servers clock.
Once you've done this, open up two browser windows and go to http://localhost:3000/user/@john
and http://localhost:3000/user/@candice
. Try exchanging a few messages between them and you'll notice that you have a working chat app!
You might be wondering how your own messages got rendered. This is because every message to a channel is sent out to all participants of the channel and hence every user always gets an echo back of their own message. Do note that on slower networks there will be a significant delay in this occurring, so you might want to populate your message state when the user hits Send
and then let the network call take its time.
Let's now add a slightly more complex behavior in our application. In the next section, we will explore ACLs and see how we can use them to implement selective deliveries.
Last updated