Building a real time chat system, having trouble with "chat bubble" styling

I’m building a chat system in my app and using Devise for auth, but I’m running into an issue with the styling of the messages after a user posts one.
What I want is for it to be the same as any messaging app where the current_users sent messages appear on the right and the other messages on the left.

My message partial:

<%= turbo_frame_tag dom_id(message) do %>
    <div class="<%= message.user == current_user ? "m-right" : "m-left" %>">
        ....
    </div>
<% end %>

When I have the current_user condition in the div class, it breaks my turbo_stream functionality and throws this error,

[ActiveJob] [Turbo::Streams::ActionBroadcastJob] [46dd61a9-f770-4a86-ab93-8f2b29777b91] Error performing Turbo::Streams::ActionBroadcastJob (Job ID: 46dd61a9-f770-4a86-ab93-8f2b29777b91) from Async(default) in 7.23ms: ActionView::Template::Error (Devise could not find the Warden::Proxy instance on your request environment.

After searching for this error, I came across a few threads that led me to making current_user into a user variable on my _message partial.

<%= render 'message', message: message, user: current_user %>

and changed my broadcast in my model to be

after_create_commit { broadcast_append_later_to self.conversation, target: "conversation_#{self.conversation.id}", partial: 'messages/message', locals: { message: self, user: curent_user } }

This throws a NameError

NameError - undefined local variable or method `current_user’

If I try and move my broadcast to my controller instead

    def create
		@message = @conversation.messages.build(message_params)
		if @message.save
			@message.broadcast_append_later_to @conversation, target: "conversation_#{@conversation.id}", partial: 'messages/message', locals: { message: @message, user: current_user }
			respond_to do |format|
				format.turbo_stream {}
				format.html { redirect_to @conversation }
			end
		end
	end

The broadcast works correctly, but it sets current_user to the user that sent the message (User A) so when User B receives the message, it is styled incorrectly because user in

<%= message.user == user ? "m-left" : "m-right" %>

gets set by the controller to the person (current_user) that posted the message

Has anyone gotten the functionality Im looking for to work correctly?

Thanks.

Thanks for the suggestion, but unfortunately its still not working. Maybe I’ve defined my Current class wrong

models/current.rb

class Current < ActiveSupport::CurrentAttributes
    attribute :user
end

application_controller.rb

class ApplicationController < ActionController::Base
	before_action :set_current_user
    
	private

	def set_current_user
		Current.user = current_user		
	end
end

When I set this this way, initially loading the conversation page all of its messages load fine and are styled correctly, but when I go to add a new message, I get an error

ActionView::Template::Error (undefined method id’ for nil:NilClass):`

because the Current.user is for some reason undefined when the partial is rendered via turbo broadcast.

a solution is to have a custom css per user defined in the layout, like:

<style>
  .message {
    text-align: left;
  }
  .message[data-user-id="<%= current_user.id %>"] {
    text-align: right;
  }
</style>

then in your partial, the rendering is the same for all users:

<%= turbo_frame_tag dom_id(message) do %>
    <div class="message" data-user-id="<%=message.user.id%>">
        ....
    </div>
<% end %>

I aim to have really-really simple partials to be used by turbo stream.

1 Like

This works! I haven’t used inline CSS for so long I forgot it was option lol.

Hi,

Another solution is:

1 ) having a meta which reference the current user id:

<meta name="current-user-id" content="6ddeb2a2-2dd9-4cd2-94af-a6d8c12992ab">

2 a) each html message has a stimulus js controller:

<div class="message" data-controller="message" data-message-author-id-value="6ddeb2a2-2dd9-4cd2-94af-a6d8c12992ab"  hidden>
...
</div>

2 b) the message js controller could be write like:

import { Controller } from "stimulus";

export default class extends Controller {
  static values = {
    authorId: String
  }

  connect() {
    if (this.currentUserId === this.authorId)
      this.element.classList.add("message--righted")

    this.element.hidden = false
  }

  get authorId() {
    return this.authorIdValue
  }

  get currentUserId() {
    return document.querySelector("[name='current-user-id']").content
  }
}

3 ) a Bem css structure

.message {
  background: #f7f7f7;
  display:block;
  padding-right: 25%;
  width: 100%;
}

.message--righted {
  background: #00a8ff;
  color: white;
  padding-left: 25%;
  padding-right: 0;
}

Now, a html message come with a turbo_stream, turbo append it to the dom, the message is hidden, the js controller connect, it check some constraints and decide to add a special css class, to the html message div, or not, and after this operation, it show the message to the user.

Voilà.

1 Like