4

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 %>
Divyaraj Solanki
  • 351
  • 1
  • 10

2 Answers2

1

To get the typing indicator you need to use action cable and create a channel for it. You can use turbo stream to render the typing indicator. Example:

app/channels/typing_channel.rb

class TypingChannel < ApplicationCable::Channel
  def subscribed
    stream_from "typing_channel"
  end
end

app/javascript/channels/typing_channel.js

import consumer from "./consumer"

consumer.subscriptions.create("TypingChannel", {
  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) {
    // Called when there's incoming data on the websocket for this channel
  }
}); 

app/views/rooms/index.html.erb

<div id="typing">
  <%= turbo_stream_from "typing_channel" %>
</div>

app/views/rooms/_typing.html.erb

<p><%= message %></p>

app/controllers/rooms_controller.rb

class RoomsController < ApplicationController
  def rtm
    ActionCable.server.broadcast "typing_channel", { message: "helloo" }
  end
end

app/javascript/controllers/rooms_controller.js

import { Controller } from "stimulus"
import consumer from "../channels/consumer"

export default class extends Controller {
  static targets = [ "input" ]

  connect() {
    this.subscription = consumer.subscriptions.create("TypingChannel", {
      received: (data) => {
        this.renderTyping(data)
      }
    })
  }

  renderTyping(data) {
    const typing = document.getElementById("typing")
    typing.innerHTML = data.message
  }

  disconnect() {
    this.subscription.unsubscribe()
  }
}
 

Is not possible to use turbo stream with action cable. You need to use action cable to get the typing indicator. You can use turbo stream to render the typing indicator.

Tibic4
  • 3,709
  • 1
  • 13
0

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}"}

Divyaraj Solanki
  • 351
  • 1
  • 10