Live Visit Count for website or page. ActionCable, Turbo Broadcasts, Kredis

superails

Yaroslav Shmarov

Posted on February 15, 2023

Live Visit Count for website or page. ActionCable, Turbo Broadcasts, Kredis

It’s amazing that now you can add advanced javascript/websockets features like “Live visitor count” without writing a single extra line of javascript!

A “live visitor count” feature can give users a “you are not alone” feeling. For example, when watching a live stream on youtube:

youtube-watching-now-count.png

Another example - see other people that have the same google doc open:

google-docs-users-online.png

“live visitor count” can also boost urgency (booking website example):

booking-boost-urgency.png

By the end of this article you will have created:

  • live counter of all visitors on the app
  • live conter of all visitors on a specific page or “inside a Room” (like google doc or youtube live video)

turbo-broadcasts-action-cable.gif

Explanation of the above GIF:

  • I have 2 browsers open. Each browser = different session.
  • When I open the website in the second browser, the visitor count goes from 1 to 2.
  • If the user closes the browser tab, visitor count will decrease by 1.
  • When I open a specific Room page, the visitor count goes from 1 to 2 for this particular Room.
  • If a user leaves the Room (closes tab or redirects), visitor count will decrease by 1.

Technologies we will need to implement the “live visitor count”:

  • ActionCable - the core technology, that lets us to establish a websockets connection, and see if a user connected or disconnected.
  • turbo/streams_channel.rb - a way to link a turbo stream with an ActionCable channel.
  • gem Kredis - a Redis adapter for Rails, where we will store the visitor ids (session_id).

1. Count all current visitors on your website

In this example we will not implement user authentication. We will identify separate users by sessions. One browser = one session.

To make session_id available within an ActionCable Channel, you have to add it inside Connection. The Connection has access to request and cookie data.

# app/channels/application_cable/connection.rb
module ApplicationCable
  class Connection < ActionCable::Connection::Base
    identified_by :session_user
    def connect
      self.session_user = request.session.id
    end
  end
end
Enter fullscreen mode Exit fullscreen mode

Next, add a turbo_stream_from broadcast to your application layout.

Importantly, append the channel: VisitorChannel to connect to an ActionCable channel.

Add a placeholder div visitors-counter where we will update the number of current visitors.

# app/views/layouts/application.html.erb
<%= turbo_stream_from :visitors, channel: VisitorChannel %>

Current website visitors: 
<span id="visitors-counter"></span>
Enter fullscreen mode Exit fullscreen mode

Create an action cable channel. Don’t use the generator, because you will not need the other extra files and code that rails g channel visitor would give you. It has to inherit from Turbo::StreamsChannel. Add super. That way you will have streams_form automatically imported. You will have access to verified_stream_name_from_params that is :visitors for our current case.

In this case verified_stream_name_from_params = :visitors.

Create a redis unique list and add the session_user on subscribed, remove the user on unsubscribed.

Send a turbo stream broadcast to update this count in everyones views with Turbo::StreamsChannel.broadcast_update_to.

# app/channels/visitor_channel.rb
# class VisitorChannel < ApplicationCable::Channel
class VisitorChannel < Turbo::StreamsChannel
  def subscribed
    super
    visitors_online = Kredis.unique_list "visitors_online"
    visitors_online << session_user
    update_visitors_count(visitors_online)
  end

  def unsubscribed
    visitors_online = Kredis.unique_list "visitors_online"
    visitors_online.remove session_user
    update_visitors_count(visitors_online)
  end

  private

  def update_visitors_count(visitors_online)
    Turbo::StreamsChannel.broadcast_update_to(
      verified_stream_name_from_params,
      target: 'visitors-counter',
      html: visitors_online.elements.count
    )
  end
end
Enter fullscreen mode Exit fullscreen mode

Success! Now every visitor of your application will see the visitor count!

Try opening/closing pages in your app from 2 different browsers and see the visitor_count change.

2. Count current visitors on a specific page

Maybe a more useful feature would be to see the quantity of users on a specific page. For example, we have a scaffold of Room and each room has a #show page.

In this case we will pass an additional room_id param to the turbo_stream_from.

Make the target also unique based on the room_id:

# app/views/rooms/show.html.erb
<%= turbo_stream_from @room, channel: RoomChannel, params: { room_id: @room.id } %>
<%#= turbo_stream_from @room, channel: RoomChannel, data: { room_id: @room.id } %>

People inside this room: <span id="room-<%= params[:room_id]%>-counter"></span>
Enter fullscreen mode Exit fullscreen mode

Now we can access params[:room_id] from our room_channel.rb:

# app/channels/room_channel.rb
class RoomChannel < Turbo::StreamsChannel
  def subscribed
    super
    room_visitors = Kredis.unique_list "room_visitors"
    room_visitors << session_user
    target = "room-#{params[:room_id]}-counter"
    update_visitors_count(room_visitors, target)
  end

  def unsubscribed
    room_visitors = Kredis.unique_list "room_visitors"
    room_visitors.remove session_user
    target = "room-#{params[:room_id]}-counter"
    update_visitors_count(room_visitors, target)
  end

  private

  def update_visitors_count(room_visitors, target)
    Turbo::StreamsChannel.broadcast_update_to(
      verified_stream_name_from_params,
      target:,
      html: room_visitors.elements.count
    )
  end
end
Enter fullscreen mode Exit fullscreen mode

Success! Now when you open a specific room page, it will register a new connection. See the “People inside this room” counter get updated:

turbo-broadcasts-action-cable.gif

That’s it. See you in the next one!

💖 💪 🙅 🚩
superails
Yaroslav Shmarov

Posted on February 15, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related