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.

1 Like

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.

2 Likes

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