All threads / Realtime Nested Comments: Part 3 Discussion
Ask A Question

Notifications

You’re not receiving notifications from this thread.

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.

Reply

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!

An individual user will create one consumer-connection pair per browser tab, window, or device they have open. ( source )

Thank you Marc, that answered it!

Reply
Reply

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

Reply

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}!"))

Sean, thanks. I had the same issue and you saved me a ton of time!

Reply

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?

Reply

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...

Reply

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.

Reply

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 methoduser' for nil:NilClassfrom@item.user.
How could I get
@item` variable works inside comment?
Any better approach?
Thanks!

what is your item? do you want the current_user?

Reply

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.

Reply

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.

Reply

Depth-limited nested comments based on this author's video.
https://github.com/secretpray/Hotwire-CRUD-MODAL

Reply

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.

Reply

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 theWarden::Proxyinstance on your request environment..

Any solution?

Reply

Thanks a lot for this tutorial ! :)

How would you deal with N+1 ? Using :includes doesn't look quite effective (because of polymorphic associations ?).

Should we fine tune ActiveRecord::Associations::Preloader in that case ?

Reply
Join the discussion

Want to stay up-to-date with Ruby on Rails?

Join 62,791+ developers who get early access to new tutorials, screencasts, articles, and more.

    We care about the protection of your data. Read our Privacy Policy.

    logo Created with Sketch.

    Screencast tutorials to help you learn Ruby on Rails, Javascript, Hotwire, Turbo, Stimulus.js, PostgreSQL, MySQL, Ubuntu, and more. Icons by Icons8

    © 2022 GoRails, LLC. All rights reserved.