Resources

Download Source Code

Summary

# Terminal
rails g scaffold comment
rails g model user name email
rails webpacker:install:stimulus
rails action_text:install
yarn add tribute-stimulus

# models/comment.rb
class Comment < ApplicationRecord
  has_rich_text :content
end

# comments_controller.rb
def create

  @comment = Comment.new(comment_params)

  if @comment.save
    MentionNotifier.call(@comment.id)
    redirect_to @comment, notice: 'Comment was successfully created.'
  else
    render :new
  end
end

def update
  if @comment.update(comment_params)
    MentionNotifier.call(@comment.id)
    redirect_to @comment, notice: 'Comment was successfully updated.'
  else
    render :edit
  end
end

private

def comment_params
  params.require(:comment).permit(:content)
end

# app/javascript/controllers/index.js
import TributeStimulus from 'tribute-stimulus'
application.register("mentions", TributeStimulus)

# views/comments/_form.html.erb
<%= form.rich_text_area :content, data: { controller: "mentions", target: "mentions.field" } %>

# routes.rb
resources :mentions, only: :index

# mentions_controller.rb
class MentionsController < ApplicationController
  def index
    @users = User.where('name like ?', "%#{params[:query]}%")
  end
end

# views/mentions/index.json.jbuilder
json.array! @users, partial: "users/user", as: :user

# views/users/_user.json.jbuilder
json.extract! user, :id, :name
json.sgid user.attachable_sgid
json.content render(partial: "users/user", locals: { user: user }, formats: :html)

# models/user.rb
class User < ApplicationRecord
  include ActionText::Attachable
end

# views/users/_user.html.erb
<%= link_to '#' do%>
  <%= image_tag 'https://www.placecage.com/10/10'%>
  <%= user.name %>
<% end %>

# models/mention_notifier.rb
class MentionNotifier
  def self.call(comment_id)
    new(comment_id).call
  end

  attr_accessor :comment_id
  def initialize(comment_id)
    @comment_id = comment_id
  end

  def call
    return unless comment

    users.each do |user|
      # do in a background job
      Rails.logger.info "#{user.name} was mentioned"
      # do in a background job
    end
  end

  private

  def comment
    @comment ||= Comment.find_by(id: comment_id)
  end

  def users
    comment.content.body.attachables.select { |o| o.class == User }.uniq
  end
end

# app/assets/stylesheets/comments.scss
.attachment {
  display: inline-block;
  position: relative;
  margin: 0;
}

.tribute-container {
  margin-top: 10px;
  max-height: 300px;
  max-width: 500px;
  overflow: auto;
  display: block;
  z-index: 999999;
  ul {
    margin: 0;
    margin-top: 2px;
    list-style: none;
  }
  li {
    background: #fff;
    padding: 0.2em 1em;
    min-width: 15em;
    max-width: 100%;
    cursor: pointer;
  }
  .highlight {
    background: #659cb8;
    color: 444;
    span {
      font-weight: bold;
    }
  }
}