0

Hi i have implemented action cable using rails 5. its working fine locally but its not working on heroku. on heroku whenever i send any message it sends four messages, two duplicates and 2 blank messages. here is my code :- conversation.js

 App.conversation = App.cable.subscriptions.create("ConversationChannel", {
      connected: function() {},
      disconnected: function() {},
      received: function(data) {
        var conversation = $('#conversations-list').find("[data-conversation-id='" + data['conversation_id'].$oid + "']");
        if (data['window'] !== undefined) {
          var conversation_visible = conversation.is(':visible');

          if (conversation_visible) {
            var messages_visible = (conversation).find('.panel-body').is(':visible');

            if (!messages_visible) {
              conversation.removeClass('panel-default').addClass('panel-success');
              conversation.find('.panel-body').toggle();
            }
            conversation.find('.messages-list').find('ul').append(data['message']);
          }
          else {
            $('#conversations-list').append(data['window']);
            conversation = $('#conversations-list').find("[data-conversation-id='" + data['conversation_id'].$oid + "']"); 
            conversation.find('.panel-body').toggle();
          }
        }
        else {
          conversation.find('ul').append(data['message']);
        }

        var messages_list = conversation.find('.messages-list');
        var height = messages_list[0].scrollHeight;
        messages_list.scrollTop(height);
      },
      speak: function(message) {
        return this.perform('speak', {
          message: message
        });
      },
    });

$(document).on('submit', '.new_message', function(e) {
  e.preventDefault();
  var values = $(this).serializeArray();
  App.conversation.speak(values);
  $(this).trigger('reset');
});

Application.js

// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, or any plugin's
// vendor/assets/javascripts directory can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file. JavaScript code in this file should be added after the last require_* statement.
//
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require rails-ujs
// require turbolinks
//= require jquery-3.2.1.min
// require_tree .

(function() {
  $(document).on('click', '.toggle-window', function(e) {
    e.preventDefault();
    var panel = $(this).parent().parent();
    var messages_list = panel.find('.messages-list');

    panel.find('.panel-body').toggle();
    panel.attr('class', 'panel panel-default');

    if (panel.find('.panel-body').is(':visible')) {
      var height = messages_list[0].scrollHeight;
      messages_list.scrollTop(height);
    }
  });
})();

Cable.js

//= require action_cable
    //= require_self
    //= require_tree ./channels

        (function() {
          this.App || (this.App = {});

          App.cable = ActionCable.createConsumer();

        }).call(this);

create.js

var conversations = $('#conversations-list');
var conversation = conversations.find("[data-conversation-id='" + "<%= @conversation.id %>" + "']");

        if (conversation.length !== 1) {
          conversations.append("<%= j(render 'conversations/conversation', conversation: @conversation, user: current_user) %>");
          conversation = conversations.find("[data-conversation-id='" + "<%= @conversation.id %>" + "']");
        }

        conversation.find('.panel-body').show();

        var messages_list = conversation.find('.messages-list');
        var height = messages_list[0].scrollHeight;
        messages_list.scrollTop(height);

Chat Screenshot enter image description here

Please let me know how i can fix it. i am using rails 5 with ruby-2.4.0. i am also using redis server for jobs.

awsm sid
  • 595
  • 11
  • 28
  • You need to add the error message a specific problem statement to the question. What did you expect to happen? What went wrong? What have you attempted to remedy the the problem? – max Nov 10 '17 at 12:21

1 Answers1

0

You set a Javascript event listener that has nothing to do with ActionCable.

Every time you trigger the submit bottom you call the App.conversation.speak() function that append the message on the page

$(document).on('submit', '.new_message', function(e) {
  e.preventDefault();
  var values = $(this).serializeArray();
  App.conversation.speak(values);
  $(this).trigger('reset');
});

this is your speak function

speak: function(message) {
   return this.perform('speak', {
     message: message
   });

I quote Defining The Channel's Subscriber

We add our new subscription to our consumer with App.cable.subscriptions.create. We give this function an argument of the name of the channel to which we want to subscribe, ConversationChannel.

When this subscriptions.create function is invoked, it will invoke the ConversationChannel#subscribed method, which is in fact a callback method.

So what is a callback method? I can't answer clearly this question.

This method is responsible for subscribing to and streaming messages that are broadcast to this channel.

app/channels/conversation_channel.rb

class ConversationChannel < ApplicationCable::Channel  
  def subscribed
    stream_from 'conversation'
  end
end  

that

ConversationChannel#subscribed streams from our messages broadcast, sending along any new messages as JSON to the client-side subscription function.

This is how I implement ActionCable, after the Message is saved to the db I trigger the following action in my MessagesController as in Sophie Debenedetto guide (I don't know if you save a Conversation to the DB or a Message)

app/controllers/messages_controller.rb

class MessagesController < ApplicationController

  def create
    message = Message.new(message_params)
    message.user = current_user
    if message.save
      ActionCable.server.broadcast 'messages',
        message: message.content,
        user: message.user.username
      head :ok
    end
  end

  ...
end

ActionCable.server.broadcast 'messages', sends a call to the received function inside App.cable.subscription and that function is responsible for updating the page with the new message.

This call will be performed only for the user that are subscribed to this event. The subscriptions are managed in the subscribed method of the ConversationChannel

App.conversation = App.cable.subscriptions.create('ConversationChannel', {      
  received: function(data) {
      $('#messages').append(this.speak(data));
      },
  speak: function(data) {
      return "<br><p> <strong>" + data.user + ": </strong>" + data.message + "</p>";
      };
  }
});

It passed the following data from your rails controller in the json format message: message.content, user: message.user.username

Some of this code is taken from my app https://sprachspiel.xyz that is an action cable app you can test, the app should be still working. This is the github repository

I believe you are calling your js function twice, or doing some workaround to make actioncable work that causes the div to be appended twice. I believe your are executing 2 different time js to run action cable.

Remember that action cable is a websocket meant to update that message on 2 different users/browsers

Community
  • 1
  • 1
Fabrizio Bertoglio
  • 5,890
  • 4
  • 16
  • 57