# 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;
}
}
}