0

I have a Ruby on Rails application for which I am implementing ActionCable. (For full disclosure, I'm an advanced beginner at RoR and a complete noob with ActionCable. I'm using this app to learn about it.) I'm trying to figure out if I can do something like the following:

Imagine your standard chat room (like in all the ActionCable tutorials), with the twist being that:

  1. users can edit their messages after they are sent, and
  2. some of the people in the room have special permissions (you can think of them as admin users). These admin users have the ability to edit messages sent by other people after they have been sent.

When rendering the page, I have a partial for each message that looks something like this:

# View:
<%= render :partial=>"message_line", :collection=>@messages, :locals=>{:current_user=>@user}%>
# _message_line.html.erb partial
<div><%= message_line %></div>
<div>
  <% if current_user.admin or current_user.id==message_line.user.id %>
    <%= Link to edit post... %>
  <% end %>
</div>

I have successfully set up the ActionCable such that when a user enters a message that message gets broadcast and displayed on the screens of all users in that room. But I can't figure out how tell, when receiving a message, if the user receiving it is an admin user and therefore should be shown the "link to edit post" link. The user that's invoking the controller action to push the message to everyone else is not the user receiving the message, and so therefore the controller doesn't know if the receiving user is an admin (especially given that there are multiple recipients).

As a concrete example, consider the following setup:
There are three users, UserA, UserB, UserC in the chat room. UserA is an admin, UserB and UserC are not.

Here's what should happen:

  • UserA enters a new message. It is broadcast to all 3 users and all 3 see it displayed on their screen. UserA sees the link to edit the message, UserB and UserC do not.
  • UserB enters a new message. It is broadcast to all 3 users and all 3 see it displayed on their screen. UserB and UserA sees the link to edit the message, UserC does not.

Thanks in advance for any help!

Q A
  • 101
  • 6

1 Answers1

0

Based off this answer it looks like your partial is being rendered before it is sent. This means current_user in your partial is the sending user, not the viewing user, like you might expect.

I'd suggest doing the same thing here. Have two different partials that you render and then use the viewing user's permissions to determine which to use.

# controller
data[:htmlAdmin] = ApplicationController.render partial: 'partial1', locals: { message: message_line, admin: true }
data[:htmlUser] = ApplicationController.render partial: 'partial2', locals: { message: message_line, admin: false }

# partial1
<div><%= message_line %></div>
<div>
  <%= Link to edit post... %>
</div>

# partial2
<div><%= message_line %></div>

# channel
received(data) {
  if current_user.is_admin?
    $("#messages_div").prepend(data[:htmlAdmin].html)
  else
    $("#messages_div").prepend(data[:htmlUser].html)
  end
}

Edit

If you use Devise, you can get current_user in ActionCable this way:

# app/channels/application_cable/connection.rb
module ApplicationCable
  class Connection < ActionCable::Connection::Base
    identified_by :current_user

    def connect
      self.current_user = find_verified_user
    end

    protected

    def find_verified_user
      if current_user = env["warden"].user
        current_user
      else
        reject_unauthorized_connection
      end
    end
  end
end
sam
  • 966
  • 6
  • 10
  • Thanks! This is a helpful start. And what you describe is exactly what's happening. One thing I can't figure out in your suggestion, though, is in the channel. Where does it get 'current_user' from? If I pass 'current_user' in then it will be passing the user of the *sender*. I feel like I'm missing something here.... How do I get the current user of the user calling the received() function? – Q A Feb 14 '21 at 03:29
  • Added in answer above. – sam Feb 14 '21 at 16:08
  • Thanks, I am doing something like that in my connection.rb file. (Not using Devise, but I still am getting a current_user out of it.) And room_channel.rb is getting current_user just fine and is able to see it. But the javascript file on the client side doesn't have access to the current_user object. So doing "if current_user.is_admin?" in the room_channel.coffee file kicks an error. I feel like there's some connection between those two that I'm missing here... ? – Q A Feb 15 '21 at 16:21