Implement realtime chat read receipts

Article


author small image Siva Kishore G
Posted : 26 Jan 2022
Modified : 06 Jun 2022
Expert Developers
Stack for implementing read receipts

Introduction

There are a number of techniques available to implement read receipts and out of which, today we will discuss how to do it with as little code as possible. For this, we will use socket io, dynamodb (you can go with your choice of DB, no restrictions) and a node JS server ofcourse!

The key ingredient is, how to detect if the user has seen the message. You can do this multiple ways

  1. Using element.getBoundingClientRect()
  2. Using window's onfocus & onblur methods
  3. Using Intersection Observer Learn More

We will be using the second method since the chat window scrolls automatically upon every message. So if you don't want to do that, use any of the above methods. Intersection observer is new and supported by most browsers. For simplicity sake we will go with #2

The technique is to listen to the onload method of image and while adding html's lazyload="true" to img tag. See below code. This way we don't need any additional JS dependencies for listening to scroll or element's visibility in viewport etc. Some web servers like nginx and express cache the images and we will use image versioning to overcome this.

<!-- Admin JS & client JS -->
<img loading="lazy" onload="readRcpt('${mid}', ${commentedOn})" src="/static_files/images/transparent.png?v=${mid}" class="messageStatus"/>


Confirgure Read Receipts architecture

Setup

See the above image to understand the flow. We will discuss #1, #3, #5 & #7 which involve setting up admin JS, chat server, client JS and database respectively.

Download necessary image files. I've used fontawesome svgs because I can easily add color to the svgs for example, to get double check green filled, I've will add fill="#48b31e" in the path tag.

  1. Transparent Pixel
  2. Single check
  3. Double check
  4. Double check (green filled)

SVG Attribution Required

If you are using fontawesome svgs, then make sure you give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.

Admin JS (#1)

We will use onblur & onfocus methods to identify if a user is on the page or not and save it in global variable userOnPage. We will use this global variable elsewhere along with an empty array readRcptsArray. In this array we will populate it with data objects for which we need to emit read-rcpt events.

In plain words, we will send read receipt acknowledgement immediately when the user is on the page and interacting. If not, we will save the tasks inside an array and trigger it when the user is again on the page.

// admin.js
var readRcptsArray = []
var userOnPage = true
window.onblur = function(){
  userOnPage = false
}

window.onfocus = function(){
  userOnPage = true
  if(readRcptsArray.length > 0){
    for(var r of readRcptsArray){
      socket.emit('read-rcpt',r)
    }
    readRcptsArray = []
  }
}
function readRcpt(id,commentedOn){
  var params = {
    roomid,
    id,
    commentedOn,
    adminSocket
  }
  if(userOnPage) return socket.emit("read-rcpt", params)
  readRcptsArray.push(params)
}

Chat Server (#3)

The socket io chat server will act as the middleman routing the incoming events to appropriate sockets. To do this correctly, you need to set up the server according to this.

Implement these in chat server

  1. Upon connection, get the user generated roomid & socket id and save it to the database
  2. When admin or client sends a message which includes certain ID (we will call this MID - short for message id), we will save it to the database
  3. Once the message is saved in database along with MID, the server emits an event with the particular MID & commentedOn params
  4. Listen to read-rcpt event, update the seen status in the database and notify the client / admin.

Database (#5)

We need two tables in the database. I'm using dynamoDB.

  1. chat_users
    1. roomid
    2. username
    3. connectedOn
    4. socketid
  2. chat_messages
    1. roomid
    2. commnetedOn
    3. msg
    4. seen_status
    5. mid
    6. clientType

Client JS (#7)

On the client side, instantialte socket IO & listen to notify-read-rcpt which has MID as the payload. This is typically the ID of the image you want to change. Use simple jquery to change the image. See below

socket.on("notify-read-rcpt",mid=>{
  $('#'+mid).attr("src",IMAGE_DOUBLE_CHECK_READ)
})

Performance

So, let's say we have an array of 10 or more in readRcptsArray, then we are sending the data to server 10 times back to back. This may take place in a matter of milliseconds and every request updates the database. The server is at load here right now. So, what we can do is create some sort of waiting mechanism.

For instance, when the first request completes writing to the database, emit an event upd-complete. The browser (client JS / admin JS) will listen to this event and remove that particular item from the array. Now check if the array's length is more than 0, if it is, redo the process, if not simply return;.

Conclusion

This is as simple as it gets. Now that you have understood the basic architecture and the flow, implement the same admin JS code on client side and client JS code on admin. This way both the admin and the client receive read receipts.



Post a comment

I promise, I keep it clean *

Comments

Alert SVG Cookie Consent

This website uses cookies and similar technologies, to enhance your browsing experience and provide personalized recommendations. By continuing to use our website, you agree to our Privacy policy.