Matthew Stevenson Matthew Stevenson - 15 days ago 5
CoffeeScript Question

Message username undefined for ActionCable chat app

I am following the Learn Enough Action Cable tutorial. I have gotten to the end of section 6, adding @mentions to create notifications to the mentioned user. One of the exercises is to append the sending user's name to the alert text. So far I am only getting "@undefined". I'm guessing data.mention.username is not the correct call to append. In the console to pull the username from a message I did User.find_by(id: Message.last.user_id).username, but I don't know how to translate that to working coffeescript.

room.coffee

App.room = App.cable.subscriptions.create "RoomChannel",
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) ->
alert("You have a new mention from @" + data.mention.username) if data.mention
if (data.message && !data.message.blank?)
$('#messages-table').append data.message
scroll_bottom()
$(document).on 'turbolinks:load', ->
submit_message()
scroll_bottom()
submit_message = () ->
$('#message_content').on 'keydown', (event) ->
if event.keyCode is 13 && !event.shiftKey
$('input').click()
event.target.value = ""
event.preventDefault()
scroll_bottom = () ->
$('#messages').scrollTop($('#messages')[0].scrollHeight)


messages_controller.rb

class MessagesController < ApplicationController
before_action :logged_in_user
before_action :get_messages

def index
end

def create
message = current_user.messages.build(message_params)
if message.save
ActionCable.server.broadcast 'room_channel',
message: render_message(message)
message.mentions.each do |mention|
ActionCable.server.broadcast "room_channel_user_#{mention.id}",
mention: true
end
end
end


private

def get_messages
@messages = Message.for_display
@message = current_user.messages.build
end

def message_params
params.require(:message).permit(:content)
end

def render_message(message)
render(partial: 'message', locals: { message: message })
end

end


message.rb

class Message < ApplicationRecord
belongs_to :user
validates :content, presence: true
scope :for_display, -> { order(:created_at).last(50) }

# Returns a list of users @mentioned in message content.
def mentions
content.scan(/@(#{User::NAME_REGEX})/).flatten.map do |username|
User.find_by(username: username)
end.compact
end
end


room_channel.rb

class RoomChannel < ApplicationCable::Channel
def subscribed
stream_from "room_channel"
stream_from "room_channel_user_#{message_user.id}"
end

def unsubscribed
# Any cleanup needed when channel is unsubscribed
end
end

Answer

Have to define the username within the create method of messages controller. So the create method in messages_controller.rb looks something like:

  def create
    message = current_user.messages.build(message_params)
    if message.save
      ActionCable.server.broadcast 'room_channel',
            message: render_message(message)
      message.mentions.each do |mention|
        ActionCable.server.broadcast "room_channel_user_#{mention.id}",
            mention:  true,
            origin: "@#{message.user.username}"
      end
    end
  end

And the room.coffee alert calls data.origin like so:

received: (data) ->
    alert("You have a new mention from " + data.origin) if data.mention
    if (data.message && !data.message.blank?)
      $('#messages-table').append data.message
      scroll_bottom()

Thanks to LIUSINING of LearnEnough Society for pointing me in the right direction.