Reputation: 61
I have recently finished implementing an in-app notification, albeit there is a huge flaw to the implementation. As much as it is quite feasible when sending an activation message to all users having successfully activated their account it gets a bit complicated when one user follows an end user. When the current user follows and unfollows a few times, the end user gets multiple notifications from the current user. How does one destroy a notification when the current user unfollows the end user, to stop sending multiple "current user started following you" notifications to the end user?
User Model
class User < ActiveRecord::Base
searchkick word_start: [:name, :surname]
has_many :active_relationships, class_name: "Relationship",
foreign_key: "follower_id",
dependent: :destroy
has_many :passive_relationships, class_name: "Relationship",
foreign_key: "followed_id",
dependent: :destroy
has_many :following, through: :active_relationships, source: :followed
has_many :followers, through: :passive_relationships, source: :follower
has_many :notifications, foreign_key: :recipient_id
...
# Follows a user.
def follow(other_user)
following << other_user
end
# Unfollows a user.
def unfollow(other_user)
following.delete(other_user)
end
# Returns true if the current user is following the other user.
def following?(other_user)
following.include?(other_user)
end
...
Relationship model
class Relationship < ActiveRecord::Base
belongs_to :follower, class_name: "User"
belongs_to :followed, class_name: "User"
validates :follower_id, presence: true
validates :followed_id, presence: true
end
Notification Model
class Notification < ActiveRecord::Base
belongs_to :recipient, class_name: "User"
belongs_to :actor, class_name: "User"
belongs_to :notifiable, polymorphic: true
scope :unread, ->{ where(read_at: nil) }
scope :recent, ->{ order(created_at: :desc).limit(5) }
end
Relationship Controller
class RelationshipsController < ApplicationController
before_action :logged_in_user
def create
@user = User.find(params[:followed_id])
current_user.follow(@user)
Notification.create(recipient: @user, actor: current_user, action: "following", notifiable: @user)
respond_to do |format|
format.html { redirect_to @user }
format.js
end
end
def destroy
@user = Relationship.find(params[:id]).followed
current_user.unfollow(@user)
# Where the notification should be destroyed
respond_to do |format|
format.html { redirect_to @user }
format.js
end
end
end
Upvotes: 1
Views: 143
Reputation: 107037
It depends on how you implemented your relationship model.
I would only send an email when a relation is created. If a user unfollows another user I would not delete that relation, but flag it as unfollowed. If the user decides to follow the same user again, then you just need to remove the flag that marks the relation as non-active. That means you do not create a new relation, therefore you do not need to send an email.
First, you will need to add an active
boolean flag to your Relationship
model. That can be done with a migration like this:
def change
add_column :relationships, :active, :boolean, default: true
end
Then you need to respect the active
flag in your associations and scopes:
has_many :following,
-> { Relationship.where(active: true) },
through: :active_relationships, source: :followed
has_many :followers,
-> { Relationship.where(active: true) },
through: :passive_relationships, source: :follower
Next, change the follow
and unfollow
methods:
def follow(other_user)
relationship = Relationship.where(
followed_id: other_user.id, follower_id: self.id
).first
if relationship.present?
relationship.update(active: true)
else
following << other_user
Notification.create(
action: 'following',
actor: self,
notifiable: other_user,
recipient: other_user
)
end
end
def unfollow(other_user)
relationship = Relationship.where(
followed_id: other_user.id, follower_id: self.id
)
relationship.update(active: false) if relationship.present?
end
def following?(other_user)
Relationship.exists?(
followed_id: other_user.id, follower_id: self.id, active: true
)
end
Note that the Notification
is created in the follow
method now. That allows to simplify the controller:
def create
@user = User.find(params[:followed_id])
current_user.follow(@user)
respond_to do |format|
format.html { redirect_to @user }
format.js
end
end
def destroy
@user = Relationship.find(params[:id]).followed
current_user.unfollow(@user)
respond_to do |format|
format.html { redirect_to @user }
format.js
end
end
Upvotes: 2