0

Action Cable is configured to correctly to subscribe, broadcast, etc.

However, I'm getting the behavior of user avatars rendering on the wrong side due to a Rails partial.

# conversations/_message.html.erb

<% if current_user.id == message.user_id %> 
  <Display Avatar Right>
  </Display Avatar Right>
<% else %>
  <Display Avatar Left>
  </Display Avatar Left>
<% end %>

When a message belongs to the current user, the avatar needs to be on the right, when not, the avatar needs to be on the left.

In the create_message method of my MessagesChannel, this is the method that is called when a user creates a new message. The issue is, when a message is created, current_user is the user who created the message. This will render the correct HTML for This user ALONE. Other users/subscribers to the channel will be broadcasted the SAME HTML... this will result in the bug seen in the first screen shot.

def create_message(message_params)
  message = Message.new context_type: 'Conversation'
  message[:body] = message_params['data']['body']
  message[:context_id] = message_params['data']['conversation_id']
  message[:user_id] = message_params['data']['user_id']
  data = {}
  if message.save
    data[:html] = ApplicationController.render partial: 'conversations/message', locals: { message: message, current_user: current_user }
  else
    p message.errors.full_messages.to_sentence
    data[:html] = "Error: #{message.errors.full_messages.to_sentence}"
  end

  ActionCable.server.broadcast('room', data)
end

The bug is, in

# conversations/_message.html.erb

if current_user.id == message.user_id will always return true and the avatar displays on the right even for other users(users who are subscribed to this channel.)

Another thing is, I don't want to touch the behavior in the partial because for the user who sent the message, the html sent back from the MessagesChannel is correct(it renders the avatar on the right.)

Here are two screenshots to illustrate

Incorrectly displays for other subscribers screen.

enter image description here

Correctly displays for message sender screen.

enter image description here

Anyone know a workaround where I can still use this _message partial? Thanks!

PrimeTimeTran
  • 1,807
  • 2
  • 17
  • 29
  • You might need to add the related model and controller code too. – Jagdeep Singh Jan 03 '18 at 12:49
  • 1
    I could but I know that's not the issue. The issue is this, when the html is rendered in `MessagesChannel` action, current_user will always be the user who created the message, thus when the partial is rendered with `ApplicationController.render ...` the partial always branches the same way. I thought about adding model code but it's sorta complicated. `Message` belongs to a `Conversation`. `Conversation` can have many `Users`, through `UserConversations`. – PrimeTimeTran Jan 03 '18 at 12:51

3 Answers3

1

hej, your partial is being rendered before being broadcasted. the broadcast_to method transmits a string which is the rendered html of the partial. as a result, when you render your partial, the current_user is the one who sends the message and therefore it is rendered the way the sender sees the partial. a solution could be to pass a boolean variable "sender" to your partial, i.e.

<% if sender %> 
  <Display Avatar Right>
  </Display Avatar Right>
<% else %>
  <Display Avatar Left>
  </Display Avatar Left>
<% end %>

then you transmit both the sender and the receiver partials in the data object and the sender Id, e.g.

data[:htmlSender] = ApplicationController.render partial: 'conversations/message', locals: { message: message, sender: true }

data[:htmlReceiver] = ApplicationController.render partial: 'conversations/message', locals: { message: message, sender: false, } data[:senderId] = current_user.id.to_s

then you make a conditional in the received(data) callback where you compare the senderId from your data Object with the current_user id and insert either data.htmlSender or data.htmlReceiver.

clara-lupa
  • 11
  • 1
  • 1
    Thanks! This is helpful for a similar problem I have. What I can't figure out is, where do I get the "current_user" id from within the received(data) function in the channel javascript? If I can figure out how to get that, then I know how to do the logic to compare things as you suggest. But I feel like I'm missing something in there. How does the received() function know who the current_user is? – Q A Feb 14 '21 at 03:34
0

You have typed <% if current_user.id = message.user_id %>.

Replace = with ==.

= will return the value of message.user_id which is truth in your case.

Jagdeep Singh
  • 4,880
  • 2
  • 17
  • 22
  • That was a typo in my question, my bad. It is correctly typed in the `_message.html.erb` partial. `if current_user.id == message.user_id` – PrimeTimeTran Jan 03 '18 at 12:46
0

current_user.id = message.user_id this is an assignment. The control flow will always go into if block. Using == should be right.

Daniel
  • 869
  • 2
  • 7
  • 19