Realtime Nested Comments: Part 3 Discussion
Very cool. I imagine a real-time comment feed like this will make a website feel come alive.
One issue I’d love to see addressed in a follow up video is the page kinda moving around as you’re reading comments. I believe Disqus solves this by not I’m immediately appending new comments, but adding a placeholder like “5 new comments” which you can click to reveal.
I imagine we could do something similar with Stimulus but it right require some changes to the broadcasts.
Are there any performance concerns to keep in mind with having a turbo_stream_from tag for each comment? Does this create a separate connection for each comment? I'm new to websockets/redis, not even quite sure the best way to test this lol
Loving these episodes, and learning so much! Thank you!
I'm getting the error Devise::MissingWarden in Comments::Comments#create when trying to call the current_user helper in the _comment partial. To check if the current_user is the author of the comment.
Same problem here. Did you end up finding any solution to this? Right now I am investigating the use of view_components
gem but seems heavy handed :/
I ran into this problem as well, according to DHH this makes sense as partials used for global streaming should be free of global references https://discuss.hotwired.dev/t/authentication-and-devise-with-broadcasts/1752/4?u=shawnaukstak
I got around this by using CSS described here
https://discuss.hotwired.dev/t/authentication-and-devise-with-broadcasts/1752/18?u=shawnaukstak
How does this work with someones avatar thats uploaded using activestorage? I get the following url for the image
http://example.org/rails/active_storage/representations/redirect/...../chewy.jpeg
where the example.org was set in application_controller_renderer.rb
. Even after replacing it to "localhost" it still doesn't work. When you do a full page reload all the avatars show up.
There's a bug in ActiveStorage. Wrap your call in url_for() to work around it.
# BUG: Uses http://example.com/... when rendering via a model broadcast
# See https://github.com/rails/rails/issues/41795
url_for(obj.avatar.variant(resize: "#{size}x#{size}!"))
For anyone else that is hitting this issue, you'll want to edit config/environment/development.rb to include:
config.action_controller.default_url_options = { host: "localhost", port: ENV.fetch("PORT", 3000).to_i }
Great series Chis thanks for putting this together, it was eye opening to how comments can really come alive with Hotwire. A bit of a newbie here, but would it be possible to take it a step further and bring nested comments inline akin to Google Docs as a way to bring more context into the comments themselves?
Great series! I'm curious how the nesting depth limit that was implemented with the AJAX version can be adapted for this approach? There's quite a bit that's different between the two versions, so I'm not sure where to even begin. Still new to Turbo and Hotwire, so any guidance would be appreciated.
If anyone is interested, I figured out what I needed to do. For context: in the first half of the Nested Comment Threads in Rails series, Chris introduces two local variables inside the comments/_comment.html.erb
partial called nesting
and max_nesting
. He also creates a helper method called reply_to_comment_id(comment, nesting, max_nesting)
that sets the parent_id of the reply comment. I couldn't get this approach to work with Hotwire and Turbo, but thanks to a comment by Jay Killeen on Nested Comment Threads in Rails - Part 3 I was able to get this working:
Create a migration to add nesting to comments:
class AddNestingToComments < ActiveRecord::Migration[6.1]
def change
add_column :comments, :nesting, :integer
end
end
Add this inside the Comment model:
def set_nesting
if self.parent.present? && self.parent.nesting.present?
self.nesting = self.parent.nesting + 1
else
self.nesting = 1
end
end
def self.max_nesting
2
end
Make this change inside the Commentable concern:
def create
...
...
if @parent&.nesting.blank? || @parent&.nesting < Comment.max_nesting
@comment.parent_id = @parent&.id
@comment.nesting = @comment.set_nesting
else
@comment.parent_id = @parent&.parent_id
@comment.nesting = @comment.set_nesting
end
...
...
end
This may not be the best way to handle things, so if anyone has another approach that is better please feel free to comment.
Hi Mark, have the exact same problem trying to make "continues thread" working. Will try your approach, but just wanted to see, if you made it work in your production property with above implementation?
Marc, thank you. This seems to work well for me and saved me hours of time, no doubt.
Hi Porter/Marc, were you able to implement conditional edit/delete buttons for different user comments?
Currently stuck on that, using following approach => https://www.colby.so/posts/conditional-rendering-with-turbo-stream-broadcasts
But no luck so far...
Hey cool session! everything worked out nice except when having to use my comments within a namespace.
I fixed it with a custom record helper method like so:
def model_scope_for_path(commentable, comment)
return [comment] if params[:action] == 'edit'
if commentable.instance_of?(Session.new.class)
[:backend, commentable, comment].compact
else
[commentable, comment].compact
end
end
I think it is a little bit ugly, what do you think? Is there a better way to taggle it? I was also wondering why we have to use 3 different controllers and if this could be even simplified.
Thanks Chris!
Inside _comment.html.erb
I'm calling @item.user
as bellow:
<div class="flex flex-col w-full text-gray-700 <%='bg-gray-200 m-1 p-1 rounded-md' if comment.user === @item.user%>">
<div class="relative flex flex-wrap items-center mb-1">
<% if comment.user.avatar.attached? %>
<div class="relative flex flex-col items-center group">
<%= link_to comment.user do %>
<%= image_tag url_for(user_avatar(comment.user, 300)), class: "rounded-full h-12 w-12 align-middle border-none shadow-lg mr-2", alt: comment.user.name, label: comment.user.name %>
<%end%>
<div class="absolute bottom-0 flex flex-col items-center hidden mb-12 right-1 -left-1 group-hover:flex">
<span class="relative z-10 p-2 text-xs font-semibold leading-none leading-relaxed text-white whitespace-no-wrap bg-black rounded-md shadow-lg min-w-max"><%= comment.user.name%></span>
<div class="w-3 h-3 -mt-2 rotate-45 bg-black"></div>
</div>
</div>
</div>
</div>
And I get an error undefined method
user' for nil:NilClassfrom
@item.user.
@item` variable works inside comment?
How could I get
Any better approach?
Thanks!
Something else I was wondering if the format.html redirect @commentable
lines are necessary. I deleted them all, because they had an error in my case and it didn't anything.
it was really painful to add the current_user (if you want to have edit and delete only for the person who created that comment) to this since device's current_user method it not working with hotwire. I had to add a lot of passing of the current_user to make it work.
In scenarios, like this, I use the user method on the object. Since the comment.user
will return the correct user for that comment.
But isn't the problem that, when comparing comment.user
to current_user
and current_user
is not available through the renderer??
Interested to know what OuttaSpaceTime was doing to successfully pass the current_user
around. Currently struggling to access it via the comment.rb
callbacks like after_create_commit
.
Did you pass the current_user to the partial?
<%= render comment, current_user: current_user %>
Hey, I'm fighting with this as well and I still don't understand how it's possible at all to make edit & delete buttons visible in the broadcasted partial depending on the user. I don't see how this can work.
The commit itself and the after_create_commit
are running in the context of the creating user, i.e. Current.user is always the user creating the post, which means everyone receives the same partial!
Only ways that I see is
- Use JS / Stimulus to postprocess the partial in the browser.
- Not sending the partial, but a trigger, so that every client pulls the update in his own context
Can anyone correct me, please?
Depth-limited nested comments based on this author's video.
https://github.com/secretpray/Hotwire-CRUD-MODAL
Unfortunately, with Rails 7, the forms with action-text text-areas are having the issue outlined here: https://github.com/hotwired/turbo-rails/issues/243
Hi. Yes, I'm experiencing that, at the moment I am applying the solution proposed by 'joostvanrijn'. Basically checking if the session exists before rendering the partials like:
<% if session.enabled? %>
But I still have an issue with brand new comments, toggle action does not work if I do not refresh.
Is it possible to render signed_in?
inside partial _comment_with_replies.html.erb
without errors?
Inside the partial, I have <% if signed_in? %>
to check if user signed in first to show Reply
section. When i do so, it throw Devise famous error Devise could not find the
Warden::Proxyinstance on your request environment.
.
Any solution?
<%= render comment_with_replies, current_user: current_user %>
if current_user.present?