I am trying to learn turbo with rails and have come to a problem that I can't seem to figure out. I have a structure where every Applicant has Messages. Two users can send messages to each other through an Applicant. This stream works now.
However, I have tried to limit the delete to only the user that posted the message. This works when the messages are initially loaded, or the page is refreshed. But when the messages are streamed, the delete option does not appear.
It does not seem to recognise what current_user (devise) is on the stream. I have tried adding current_user as a local in the controller and view, but that doesn't work. I can't see a way to add current_user in the model.
One of the issues, I would imagine, is that I feel like my create method for messages and the broadcast after save method are doing the same thing.
Can anybody identify where I can pass the current_user into the turbo stream so the delete button appears? Failing that, any issues with my methodology would also help me in tracking the issue down
Here is the show action in the Applicant controller (what runs when the page initially loads):
class ApplicantController < ApplicationController
before_action :authenticate_user!
def show
@applicant = Applicant.find_by_hashid(params[:hashid])
max_messages = 200
@messages = @applicant.messages.order(created_at: :asc).last(max_messages)
current_user == @applicant.project.user ? @is_owner = true : @is_owner = false
@message = Message.new
respond_to do |format|
format.html
format.turbo_stream
end
end
end
Here is the create action in the controller for messages (what gets run when a new message is created):
class MessageController < ApplicationController
before_action :authenticate_user!
def create
@message = Message.new(message_params)
respond_to do |format|
if @message.save
format.turbo_stream { render turbo_stream: turbo_stream.append("applicant_messages_#{@message.applicant_id}", partial: "messages/message", locals: { message: @message }), notice: 'Message was successfully created.' }
format.json { render :show, status: :created, message: @message }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: @message.errors, status: :unprocessable_entity }
end
end
end
end
Here is the model for messages:
class Message < ApplicationRecord
after_save_commit -> { broadcast_append_later_to "applicant_messages_#{self.applicant_id}", partial: "messages/message", locals: { message: self }, target: "messages" }
after_destroy_commit -> { broadcast_remove_to "applicant_messages_#{self.applicant_id}", target: "message_#{self.id}" }
end
Here is the show.html.erb where the messages appear:
<div class="container">
<div>
<%= turbo_stream_from "applicant_messages_#{@applicant.id}" %>
<div id="messages">
<%= render @messages %>
</div>
</div>
</div>
<div class="container">
<%= form_for @message, url: message_create_path(@applicant.hashid),data: { controller: "message ", action: "turbo:submit-end->message#reset"}, method: :post, :html => {:id => "message_form"}, remote: true do |f| %>
<%= f.hidden_field :is_owner, :value => @is_owner %>
<%= f.hidden_field :applicant_id, :value => @applicant.id %>
<%= f.text_field :body, autofocus: true, autocomplete: 'off', :placeholder=>"Enter message", :id=>"message_body", class: "form-control", data: { turbo_stream: true } %>
<%= f.submit 'Post message', class: 'btn btn-primary d-inline', data: {disable_with: "Working on it..."} %>
</div>
Here is the _messages.html.erb partial:
<%= turbo_frame_tag dom_id(message) do %>
<p>
<%= message.is_owner == true ? "#{message.applicant.project.user.first_name}" : "#{message.applicant.user.first_name}" %> (<%= message.id %>):
<%= message.body %>
<% if message.is_owner == @is_owner %>
[<%= link_to "delete", message_delete_path(message.hashid), method: :delete, data: {turbo_method: :delete} %>]
<% end %>
</p>
<% end %>
I suggest to pass user as local variable to the partial, and check it inside
Also for broadcasting suggest use only controllers. It's some kind of anti-pattern that models can send something through network, and it is especially bad idea to do it using callbacks
It is just idea, you can improve it and apply to your code
In create action of messages controller:
if @message.save
# or use turbo_stream here
@message.broadcast_append_to(
"applicant_messages_#{@message.applicant_id}",
partial: "messages/message",
target: "messages",
locals: { message: @message, user: current_user }
)
end
In app/views/messages/_message.html.erb
partial:
<% if message.user_id == user.id %>
<%= render something.that.can.see.only.message.owner %>
<% else %>
<%= render something.that.see.others %>
<% end %>
Of course you need in this case pass current_user
as local user
in the message container in the app/views/applicants/show.html.erb
<div id="messages">
<%= render @messages, user: current_user %>
</div>