So that the messages appear on the right if you are the sender
, and on the left if you are the receiver
, both with their own background-color
.
For that, we will need to add a few things in a number of files, namely to pass infos to our Stimulus chatroom_subscription_controller
Let's start by passing the current_user id
as a value
to Stimulus from our show
page.
In app/views/chatrooms/show.html.erb
<div class="container chatroom"
data-controller="chatroom-subscription"
data-chatroom-subscription-chatroom-id-value="<%= @chatroom.id %>"
data-chatroom-subscription-current-user-id-value="<%= current_user.id %>">
Next, we'll wrap each message inside two div
that will serve to position the message left or right, using Bootstrap classes, and to style it differently depending on the current_user
being the message's sender
or receiver
.
<% @chatroom.messages.each do |message| %>
<div class="message-row d-flex <%= message.sender?(current_user) ? 'justify-content-end' : 'justify-content-start' %>">
<div class="<%= message.sender?(current_user) ? 'sender-style' : 'receiver-style' %>">
<%= render "messages/message", message: message %>
</div>
</div>
<% end %>
Notice we use a message#sender?
instance method in the display logic, to determine if the current_user
is the message's sender
.
We need to define this method in app/models/message.rb
def sender?(a_user)
user.id == a_user.id
end
Notice also that we are using three classes: .message-row
, .sender-style
and .receiver-style
, which have not yet been defined.
Let's create a new file and define those classes.
touch app/assets/stylesheets/components/_message.scss
In the newly created _message.scss
// To style the wrapping div
.message-row {
margin-bottom: 10px;
padding: 8px;
}
// To style the message itself
.sender-style, .receiver-style {
width: 70%;
max-width: 400px;
border-radius: 4px;
padding: 8px;
min-height: 80px;
}
.sender-style {
background-color: lightsalmon;
}
.receiver-style {
background-color: lightcoral;
}
import
this new component in _index.scss
Which means we'll need to tackle the same logic in our Stimulus chatroom_subscription_controller
. In order to do so, we will need access to the message's user_id
(aka: sender_id
). Let's pass it through the cable as part of the broadcasted data
In app/controllers/messages_controller.rb
# [...]
if @message.save
ChatroomChannel.broadcast_to(
@chatroom,
message: render_to_string(partial: "message", locals: { message: @message }),
sender_id: @message.user.id
)
# [...]
From the chatrooms/show.html.erb
we are now passing the current_user.id
as a value
to our controller, so let's define it.
In app/javascript/controllers/chatroom_subscription_controller.js
export default class extends Controller {
static values = { chatroomId: Number, currentUserId: Number }
// [...]
For the next part, we'll be working in the#insertMessageAndScrollDown(data)
function
First, let's build the logic to know if the message was sent by the current_user
, using the sender_id
from the data
, and the currentUserIdValue
#insertMessageAndScrollDown(data) {
// Logic to know if the sender is the current_user
const currentUserIsSender = this.currentUserIdValue === data.sender_id
// [...]
}
We will now able to figure out what style
Class the message should have, and where its wrapping div
should be positioning it on the page.
Let's create a new #buildMessageElement()
function to build our complete message with its two wrapping div
, passing it the currentUserIsSender
Boolean
, and the message
String
coming from the data
#buildMessageElement(currentUserIsSender, message) {
return `
<div class="message-row d-flex ${this.#justifyClass(currentUserIsSender)}">
<div class="${this.#userStyleClass(currentUserIsSender)}">
${message}
</div>
</div>
`
}
From this function, we are calling two other functions which will also use the currentUserIsSender
Boolean
to return us the relevant classes
to position and style our message. Let's define those.
#justifyClass(currentUserIsSender) {
return currentUserIsSender ? "justify-content-end" : "justify-content-start"
}
#userStyleClass(currentUserIsSender) {
return currentUserIsSender ? "sender-style" : "receiver-style"
}
And finally, let's call our #buildMessageElement(currentUserIsSender, message)
function, and see the magic happen ✨
#insertMessageAndScrollDown(data) {
// Logic to know if the sender is the current_user
const currentUserIsSender = this.currentUserIdValue === data.sender_id
// Creating the whole message from the `data.message` String
const messageElement = this.#buildMessageElement(currentUserIsSender, data.message)
// Inserting the `message` in the DOM
this.messagesTarget.insertAdjacentHTML("beforeend", messageElement)
this.messagesTarget.scrollTo(0, this.messagesTarget.scrollHeight)
}