PrimeTimeTran
PrimeTimeTran

Reputation: 2157

Rails ActionCable & Partials

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!

Upvotes: 0

Views: 900

Answers (3)

clara-lupa
clara-lupa

Reputation: 11

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.

Upvotes: 1

Daniel
Daniel

Reputation: 899

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

Upvotes: 0

Jagdeep Singh
Jagdeep Singh

Reputation: 4920

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.

Upvotes: 0

Related Questions