Rails Blog In VS Code-Noticed V1

How To Create A Blog in VS Code — Part VIII — Notifications for your Ruby on Rails app — RailsSeries#Episode 10

J3
Jungletronics

--

Let us continue with the preceding example and integrate Noticed. Noticed, a gem developed by Chris Oliver, facilitates the transmission of notifications of diverse kinds through different channels to various recipients within your application:

Here is the example we will be developing — About This Episode: Notifications in Rails applications are even easier now with the new release of Noticed Gem v1. We’ll explore how to add notifications to your Rails app in just a few steps. Welcome!

In this post (based on Deanin):

You will:

Learn How to Use Noticed version 1.5.7;

Learn how to use Bootstrap Badges;

Set up Turbo JavaScript to review a comment;

Enhance your blog by implementing comment notifications;

Create a bell icon in your navigation bar;

Counter to keep track of unread notifications.

Additionally, display read notifications in the dropdown menu.

Each comment notification will direct you to the
corresponding blog post and provide information
about the user who sent the notification.

Welcome!
Noticed and Business Logic Diagram — Making connections 👌️

Let’s Get Started!

Note: if you get stuck, please see my repo.

0#step — Download the last version (v7) post here and prepare your vscode environment.

Let’s open a new Feature Branch: git checkout -b use_noticed_gem .

1#step — On Terminal, Run:

bundle add noticed -v 1.5.7

2#step — Now:

rails g noticed:model
bundle install
rails db:migrate
Output:

🚚 Your notifications database model has been generated!

Next steps:
1. Run "rails db:migrate"
2. Add "has_many :notifications, as: :recipient" to your User model(s).
3. Generate notifications with "rails g noticed:notification"

3#step — Then, open this file and type:

app/models/user.rb

has_many :notifications, as: :recipient, dependent: :destroy

4#step — On Terminal:

rails g noticed:notification CommentNotification
Output:

create app/notifications/comment_notification.rb

5#step — Open this file and type:

app/models/post.rb

  has_noticed_notifications model_name: 'Notification'
has_many :notifications, through: :user, dependent: :destroy

6#step — Open this file and type:

app/models/comment.rb

...
after_create_commit :notify_recipient
before_destroy :cleanup_notifications
has_noticed_notifications model_name: 'Notification'

private

def notify_recipient
CommentNotification.with(comment: self, post:).deliver_later(post.user)
# Using deliver_later will execute a background job when you hit 'post' for your comment.
# This means that it won't stall the interface while the job is being processed.
# You can simply post your comment and continue with your tasks, while in the background,
# the Rails system takes care of delivering the comment in the background.
end

def cleanup_notifications
notifications_as_comment.destroy_all
# same as in console: Notification.where(comment_id: :comment_id)
end
...

7#step — Open this file and uncomment delivering by DB, defining def message and def URL methods:

app/notifications/comment_notification.rb

# To deliver this notification:
#
# CommentNotification.with(post: @post).deliver_later(current_user)
# CommentNotification.with(post: @post).deliver(current_user)

class CommentNotification < Noticed::Base
# Add your delivery methods
#
deliver_by :database
# deliver_by :email, mailer: "UserMailer"
# deliver_by :slack
# deliver_by :custom, class: "MyDeliveryMethod"

# Add required params
#
# param :post

# Define helper methods to make rendering easier.
def message
@post = Post.find(params[:comment][:post_id])
@comment = Comment.find(params[:comment][:id])
@user = User.find(@comment.user_id)
"#{@user.name} replied to #{@post.title.truncate(14)}"
end

# That allows us to do our url construction
def url
post_path(Post.find(params[:comment][:post_id]))
end
end

8#step —To preview how notifications appear, run the application and create some comments. Afterward, launch the rails console.

Notification.all
irb(main):001:0> Notification.all
Notification Load (0.1ms) SELECT "notifications".* FROM "notifications"
Comment Load (0.1ms) SELECT "comments".* FROM "comments" WHERE "comments"."id" = ? LIMIT ? [["id", 3], ["LIMIT", 1]]
Post Load (0.1ms) SELECT "posts".* FROM "posts" WHERE "posts"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]]
Comment Load (0.1ms) SELECT "comments".* FROM "comments" WHERE "comments"."id" = ? LIMIT ? [["id", 4], ["LIMIT", 1]]
Post Load (0.0ms) SELECT "posts".* FROM "posts" WHERE "posts"."id" = ? LIMIT ? [["id", 2], ["LIMIT", 1]]
=>
[#<Notification:0x00007f55ff5fa2d8
id: 1,
recipient_type: "User",
recipient_id: 1,
type: "CommentNotification",
params:
{:comment=>
#<Comment:0x00007f55ff1e2a10
id: 3,
post_id: 1,
user_id: 2,
created_at: Sun, 17 Mar 2024 23:00:00.266022000 UTC +00:00,
updated_at: Sun, 17 Mar 2024 23:00:00.280587000 UTC +00:00>,
:post=>
#<Post:0x00007f55ff18d330
id: 1,
title: "First Post",
body: "From Example.",
created_at: Sun, 17 Mar 2024 22:46:20.617547000 UTC +00:00,
updated_at: Sun, 17 Mar 2024 23:00:00.347376000 UTC +00:00,
views: 5,
user_id: 1>},
read_at: nil,
created_at: Sun, 17 Mar 2024 23:00:00.329481000 UTC +00:00,
updated_at: Sun, 17 Mar 2024 23:00:00.329481000 UTC +00:00>,
#<Notification:0x00007f55ff29d2c0
id: 2,
recipient_type: "User",
recipient_id: 2,
type: "CommentNotification",
params:
{:comment=>
#<Comment:0x00007f55ff2cf338
id: 4,
post_id: 2,
user_id: 1,
created_at: Sun, 17 Mar 2024 23:00:55.881498000 UTC +00:00,
updated_at: Sun, 17 Mar 2024 23:00:55.888500000 UTC +00:00>,
:post=>
#<Post:0x00007f55fefff3d8
id: 2,
title: "Second Post",
body: "From Another.",
created_at: Sun, 17 Mar 2024 22:47:43.070307000 UTC +00:00,
updated_at: Sun, 17 Mar 2024 23:00:55.928286000 UTC +00:00,
views: 5,
user_id: 2>},
read_at: nil,
created_at: Sun, 17 Mar 2024 23:00:55.906453000 UTC +00:00,
updated_at: Sun, 17 Mar 2024 23:00:55.906453000 UTC +00:00>]

9#step —Navigate to the “Bootstrap 5 — Icons — Learn More” link and copy the CDN provided on the webpage: https://icons.getbootstrap.com/. Paste the copied CDN directly below the Bootstrap 5 link in your file. Then, open the file:

app/views/layouts/application.html.erb

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">

10#step — Open the Navbar file and insert this rendering command above the session manager:

app/views/layouts/_navbar.html.erb

<%= render 'layouts/notifications' %>

...above here...
<%= render 'user/session_manager' %>

11#step — Create this file and type:

app/views/layouts/_notifications.html.erb

<% if current_user %>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
<span class="badge rounded-pill bg-secondary">
<%= @unread.count >= 9 ? "9+" : @unread.count %> <i class="bi bi-bell-fill"></i>
</span>
</a>
<ul class="dropdown-menu dropdown-menu-lg-end" aria-labelledby="navbarDropdown">
<% @unread.each do |notification| %>
<%= render 'layouts/notification', notification: notification %>
<% end %>
<% if @read.count > 0 && @unread.count > 0 %>
<li>
<hr class="dropdown-divider">
</li>
<% elsif @read.count + @unread.count == 0 %>
<li class="dropdown-item">
No notifications yet.
</li>
<% end %>
<% @read.each do |notification| %>
<%= render 'layouts/notification', notification: notification %>
<% end %>
</ul>
</li>
<% end %>

12#step — Create this partial too:

app/views/layouts/_notification.html.erb

<li class="dropdown-item">
<%= link_to notification.to_notification.message, notification.to_notification.url %>
</li>

13#step — GoTo :

app/controllers/application_controller.rb

class ApplicationController < ActionController::Base
before_action :set_notifications, if: :current_user

private

def set_notifications
notifications = Notification.where(recipient: current_user).newest_first.limit(9)
@unread = notifications.unread
@read = notifications.read
end
end

14#step — GoTo:

app/controllers/posts_controller.rb

In the show() method, type:

mark_notifications_as_read

And in the private area, create this method:

  def mark_notifications_as_read
return unless current_user

notifications_to_mark_as_read = @post.notifications_as_post.where(recipient: current_user)
notifications_to_mark_as_read.update_all(read_at: Time.zone.now)
end

15#step — For those who’ve caught on, that’s all! 😎️

Now, onto the final episode, where the interface button shifts from Edit to Cancel with a simple toggle.

GoTo:

app/views/comments/_comment.html.erb

<div class="comment-<%= comment.id %> container"
style="border: 1px solid black; padding: 1em; margin: 1em;">
<%= comment.user.email %><br />
<% if (comment.updated_at - comment.created_at ) > 1 %>
<span>Edited <%= time_ago_in_words(comment.updated_at) %> ago</span>
<% else %>
<span>Posted <%= time_ago_in_words(comment.created_at) %> ago</span>
<% end %>
<% if current_user == comment.user %>
<div class="btn-group float-end">
<%= link_to "Edit", nil, remote: true, class: "btn btn-warning",
data: {
controller: "comments",
action: "comments#toggleForm",
comments_form_param: "edit-form-#{comment.id}",
comments_body_param: "comment-body-#{comment.id}",
comments_edit_param: "edit-button-#{comment.id}"},
id: "edit-button-#{comment.id}" %>
<%= button_to "Delete", [post, comment], class:"btn btn-danger", method: :delete %>
</div>
<div id="edit-form-<%= comment.id %>" class="d-none" >
<%= render 'comments/form', post: @post, comment: comment, submit_label: "Update" %>
</div>
<% end %>
<hr />
<div id="comment-body-<%= comment.id %>">
<%= comment.body %>
</div>
</div>

16#step — Go to:

app/javascript/controllers/comments_controller.js

import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
initialize() {}
connect() {}
toggleForm(event) {
console.log("I clicked the edit button.");
event.preventDefault();
event.stopPropagation();
const formID = event.params["form"];
const commentBodyID = event.params["body"];
const editButtonID = event.params["edit"];

const form = document.getElementById(formID);
const commentBody = document.getElementById(commentBodyID);
const editButton = document.getElementById(editButtonID);

form.classList.toggle("d-none");
form.classList.toggle("mt-5");
commentBody.classList.toggle("d-none");
this.toggleEditButton(editButton);
}

toggleEditButton(editButton) {
if (editButton.innerText === "Edit") {
editButton.innerText = "Cancel";
this.toggleEditButtonClass(editButton);
} else {
editButton.innerText = "Edit";
this.toggleEditButtonClass(editButton);
}
}

toggleEditButtonClass(editButton) {
editButton.classList.toggle("btn-secondary");
editButton.classList.toggle("btn-warning");
}
}

Run and hope for the best!

As you can see, the notifications are categorized. Once Another user reads them, they are moved to a lower section. This creates an excellent notification system that is both simple, sophisticated, and discrete.🫶️ ::

That’s All, Folks!

Stay tuned for the next episode where we delve into Noticed v2 👋️👋️👋️!

👉️ rails_blog_v8

Credits & References

Add Comment Notifications To Your Blog | Ruby On Rails For Beginners Part 5 by Deanin

Bootstrap5 Badges by getbootstrap.com

Noticed — 1.5.7Docs by rubydoc.info/gems/noticed/1.5.7 (this tut uses it)

Noticed — 2.1.3Docs by rubydoc.info/gems/noticed/2.1.3 (the next one, soon…)

Related Posts:

00# Episode — RailsSeries — Installing Ruby on Rails Using ASDF — Why ASDF is Better Than RBENV for Rails Bootstrap App?

01# Episode — RailsSeries — How To Send Email In Rails 7? — User Registration and Onboarding.

02# Episode — RailsSeries — 14 Ruby Extensions 4 Vs Code — Based On This Deanin’s video.

03# Episode — RailsSeries — A Rails Blog In VS Code — Quick Start — How To Create A Blog in VS Code — Part I

04# Episode — RailsSeries — A Rails Blog In VS Code — Styling — How To Create A Blog in VS Code — Part II

05# Episode — RailsSeries — A Rails Blog In VS Code — Create Posts — How To Create A Blog in VS Code — Part III

06# Episode — RailsSeries — A Rails Blog In VS Code — Posts Tips&Tricks — How To Create A Blog in VS Code — Part IV

07# Episode — RailsSeries — A Rails Blog In VS Code — Devise — How To Create A Blog in VS Code — Part V

08# Episode — RailsSeries — A Rails Blog In VS Code — Add Comments to Post — How To Create A Blog in VS Code — Part VI

09# Episode — RailsSeries — Rails Blog In VS Code — Using Stimulus — How To Create A Blog in VS CodePart VII

10# Episode — RailsSeries — Rails Blog In VS Code — Noticed V1 — Notifications for your Ruby on Rails app — Part VIII (this one)

11# Episode — RailsSeries — Rails Blog In VS Code — Noticed V2 — Notifications for your Ruby on Rails app — Part IX

Tag it all:

git tag -a rails_blog_v8 -m "Blog in Rails 8 - v1.0:  Go to  https://j3-rails-blog-demo.herokuapp.com/" -m "0- Learn How to Use Noticed Gem;" -m "1- Learn notifications version 1;" -m "2- Go to https://github.com/excid3/noticed;" -m "3- Noticed is a gem that allows your application to send notifications of varying types, over various mediums, to various recipients" -m "Thank you for downloading this project 😘️👌️👋️😍️"
git push origin rails_blog_v8
https://github.com/excid3/noticed
This is my endeavor to follow Deanin’s Noticed v1 tutorial. Deanin stands out as my favorite tutor for Rails 7. I’ve created several episodes based on his tutorials available on YouTube. For a deeper understanding, you can explore Deanin’s YouTube playlist. I assure you, it’s worth your time.
This is an explanation by Chris Oliver about Noticed v2. Chris Oliver is renowned as the creator of GoRails, Hatchbox.io, and Jumpstart. Devoting his time to crafting tutorials and developing tools, he assists Ruby on Rails developers in building applications more efficiently. For further insights, you can explore Chris Oliver’s GitHub.

For your convenience(git cmmds):

-----------------------------
I made a mistake
and decided to discontinue
the initial use_noticed_gem.
-----------------------------
git checkout master
git -d use_noticed_gem
git branch -d use_noticed_gem
git reset --hard HEAD
rails s
-----------------------------
Init again :)
-----------------------------
git checkout -b use_noticed_gem
bundle add noticed -v 1.5.7
rails g noticed:model
bundle install
rails db:migrate
rails g noticed:notification CommentNotification
rails s
rails c
clear
rails s
git status
git add -A
git commit -m ":lipstick: feat: Add Noticed v1 Gem"
git push --set-upstream origin use_noticed_gem
git branch --list
git checkout master
git fetch
git status
git pull
git tag -a rails_blog_v6 -m "Blog in Rails 7 - v1.0: Go to https://j3-rails-blog-demo-5a0a55d44e12.herokuapp.com/" -m "0- Add Comments to Each Post;" -m "1- Set up Action Text, which is a framework in Ruby on Rails for handling rich text content;" -m "2- Upload Rails 7 project to Heroku." -m "Thank you for downloading this project 😘️👌️👋️😍️"
-----------------------------
I made a mistake: wrong tag:/
-----------------------------
git tag -d rails_blog_v6
-----------------------------
git tag -a rails_blog_v8 -m "Blog in Rails 8 - v1.0: Go to https://j3-rails-blog-demo.herokuapp.com/" -m "0- Learn How to Use Noticed Gem;" -m "1- Learn notifications version 1;" -m "2- Go to https://github.com/excid3/noticed;" -m "3- Noticed is a gem that allows your application to send notifications of varying types, over various mediums, to various recipients" -m "Thank you for downloading this project 😘️👌️👋️😍️"
git push origin rails_blog_v8
history > history

--

--

J3
Jungletronics

Hi, Guys o/ I am J3! I am just a hobby-dev, playing around with Python, Django, Ruby, Rails, Lego, Arduino, Raspy, PIC, AI… Welcome! Join us!