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
endNotice 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)
}