Search code examples
ruby-on-railsrubyactioncablehotwire-railsturbo

Typing indicator with action cable in rails- 7


I am unable to add typing indicator in my rails app with action cable I have created app in rails 7 and I user trubo stream tag and broadcast in it so I did't used channel for live chat , I tried to find tutorial and video but there is not any

I want to add typing indicator so I writtern js for the same on input it will called and it will go to controller On input I am calling controller "rtm"

room controller

  def rtm 
    @typing = "hhhhhhhhhhhhhhhhhhh"
    # ActionCable.server.broadcast "typing_channel",{ message: "helloo"}
    # @typings.broadcast_append_to "typing"
    Turbo::StreamsChannel.broadcast_append_to "typing", target: 'typing', partial: 'rooms/typing', locals: { message: "@typing" }
  end

here I have issue how can I broadcast the typing message to my room page

Room.rb

class Room < ApplicationRecord
    scope :public_rooms, -> { where(is_private: false) }
    has_many :messages
    after_create_commit {broadcast_append_to "rooms"}
end

message.rb

class Message < ApplicationRecord
  belongs_to :user
  belongs_to :room
  after_create_commit { broadcast_append_to self.room }
end

rooms/index

<script>
$(document).ready(function(){
  var tmo = null;
  $("#msg").on("input", function(){
    $.ajax({
      type: 'GET',
      url: '/rooms/rtm',
      data: {data: ''}
    });
    document.getElementById("typing").innerHTML = "Typing...";
    if (tmo) {
      clearTimeout(tmo);
    }
    tmo = setTimeout(function () {
      $.ajax({
        type: 'GET',
        url: '/rooms/rmm',
        data: {data: ''}
    });
      document.getElementById("typing").innerHTML = "";
    }, 1000);
  });
});
</script>

<div class="container">
  <h5> Hi <%= current_user&.firstname %> </h5>
  <%= debug(params) if Rails.env.development? %> 

  <br>  <h4> Rooms </h4>
  <%= render partial: 'layouts/new_room_form' %>
  <%= turbo_stream_from "rooms" %>
<div id="rooms">
  <%= render @rooms %>
</div>
</div>

<% if @single_room.present? %>
<%= link_to @single_room.name,@single_room, class: "btn btn-primary" %>

  <%= turbo_stream_from @single_room %>
  <div id="messages">
    <%= render @messages %>
  </div>

  <%= render partial: 'layouts/new_message_form' %>

  <%=  @typing %>
  <%= turbo_stream_from @typing %>

  <div id="typing">
  </div>

  <%= render partial: 'rooms/typing' %>
  <span id="typing"></span><br>

<% end %>

Solution

  • typing_channel.js

    import consumer from "channels/consumer"
    
    consumer.subscriptions.create("TypingChannel", {
      connected() {
        console.log("connected")
    
        // Called when the subscription is ready for use on the server
      },
    
      disconnected() {
        // Called when the subscription has been terminated by the server
      },
    
      received(data) {
        console.log(data)
        const box = document.getElementById('typing');
        if (box.textContent.includes(data.body)) {
        } else {
          $('#typing').append('<p>'+ data.body + data.message +'</p>');
        }
      }
    });
    

    I am using below js for indicator

    <script>
    $(document).ready(function(){
      var tmo = null;
      $("#chat").on("input", function(){
         $.ajax({
          type: 'GET',
          url: '/rooms/rtm',
        });
        if (tmo) {
          clearTimeout(tmo);
        }
        tmo = setTimeout(function () {
          $.ajax({
            type: 'GET',
            url: '/rooms/rmm',
        });
        }, 1000);
      });
    });
    </script>
    

    in controller

    ActionCable.server.broadcast "typing_channel", {message: 'Typing', body: "#{current_user.email}"}