notification and activity
Bài đăng này đã không được cập nhật trong 8 năm
I.Activity
Thêm gem** 'public_activity'** sau đó bundle Tiếp tục tạo model nhé
+ rails g public_activity:migration
+ rake db:migrate
Xem trong db vừa tạo gồm những trường gì class CreateActivities < ActiveRecord::Migration
  def self.up
    create_table :activities do |t|
      t.belongs_to :trackable, :polymorphic => true
      t.belongs_to :owner, :polymorphic => true
      t.string  :key
      t.text    :parameters
      t.belongs_to :recipient, :polymorphic => true
      t.boolean :read, default: false # trường này mình thêm vào
      t.boolean :enable, default: true # trường này mình thêm vào
      t.integer :activity_type, default: 0 # trường này mình thêm vào
      t.references :user # trường này mình thêm vào để tạo quan hệ với bảng user
      t.timestamps
    end
    add_index :activities, [:trackable_id, :trackable_type]
    add_index :activities, [:owner_id, :owner_type]
    add_index :activities, [:recipient_id, :recipient_type]
  end
  # Drop table
  def self.down
    drop_table :activities
  end
end
Nếu bạn muốn thêm 1 số trường thì đơn giản chỉ tạo 1 model mới rồi kế thừa nó
class Activity < PublicActivity::Activity
  belongs_to :user
end
Bây giờ các bạn có thể gọi Activity.new xem nó có những trường nào nhé
Activity.new
#<Activity id: nil, trackable_type: nil, trackable_id: nil, owner_type: nil, owner_id: nil, key: nil, parameters: {}, recipient_type: nil,
    recipient_id: nil, read: false, enable: true, activity_type: "active", user_id: nil, created_at: nil, updated_at: nil>
Trước tiên muốn sử dụng được thì các bạn làm như sau:
- include "include PublicActivity::Model" vào ApplicationRecord
class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true
  include PublicActivity::Model
end
- Sau đó gọi "tracked" vào model nào bạn muốn luôn tất cả hành động của nó
class User < ApplicationRecord
    tracked
end
Như vậy là xong, ngoài ra bạn có thể override lại nó
II.Notification
Tiếp tục nhé, mình tạo 1 model Notification cũng kế thừa PublicActivity::Activity
class Notification < PublicActivity::Activity
    scope :all_notify, ->{where activity_type: 1} # mình thêm scope này để lọc theo Notification
end
Cũng như trên, đơn giản ..... 
Giờ mình sẽ hưỡng dẫn realtime Trước tiên :
- rails g channel Notify app/channels/notify_channel.rb
class NotifyChannel < ApplicationCable::Channel
  def subscribed
    stream_from "notify_chanel"
  end
  def unsubscribed
    stop_all_streams
  end
end
app/assets/javascripts/channels/notify.coffee
App.notify = App.cable.subscriptions.create "NotifyChannel",
  connected: ->
    # Called when the subscription is ready for use on the server
  disconnected: ->
    # Called when the subscription has been terminated by the server
  received: (data) ->
  # data
Tiếp tục: rails g job NotificationBroadcastJob
class NotificationBroadcastJob < ApplicationJob
  queue_as :default
    # ở đây các bạn override nó
  def perform count, notification
    ActionCable.server.broadcast "notify_channel", counter: render_count_notiication(count)
  end
  def render_count_notiication count
    ApplicationController.renderer.render partial: "layouts/count_notification",
      locals: {count_notification: count}
  end
end
- ở controller
class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
  before_action :load_notifications, if: :user_signed_in?
  def load_notifications
  # Bạn nhớ lọc ra nhưng record của notification thôi nhé, vì mình dùng chùng 1 table kế thừa, viết scope theo  activity_type
    @notifications = current_user.Notification.all_notify.count 
  end
end
+ở view
application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>ElearningV1</title>
       ....
        <%= action_cable_meta_tag %>
       ...
  </head>
 <span class="label label-warning" id="notification-counter">
    <%= render partial: "layouts/count_notification",
      locals: {count_notification: @notifications} %>
  </span>
... /views/layouts/_count_notification.html.erb
 <%= count_notification %>
- config/routes.rb
Rails.application.routes.draw do
   ...  
   mount ActionCable.server => '/cable'
   ...
end
Bây giờ muốn nó hoạt động: Bạn tạo 1 Notification
application_helper.rb
module ApplicationHelper
  ...
  def create_notification_for_member model, object, name, user_id
    Notification.create trackable_type: model, trackable_id: object.id,
       owner_type: current_user.role, owner_id: current_user.id, key: name,
       activity_type: Notification.activity_types[:notice], user_id: user_id
  end
...
end
Trước hết bạn đã tạo 1 model user nhé Mình tạo 1 user mới nhé
- users_controller.rb
 def create
    @user = User.new user_params
    if @user.save
      create_notification_for_member User.name, @user, "create", @user.id
      flash[:success] = t "devise.registrations.signed_up"
      redirect_to admins_users_path
    else
      render :new
    end
  end
class Notification < PublicActivity::Activity
  after_create :send_notification
  def send_notification
    NotificationBroadcastJob.perform_now Notification.all_notify.count, self
  end
end
Bạn sửa lại file notify.coffee nhé
App.notify = App.cable.subscriptions.create "NotifyChannel",
  connected: ->
    # Called when the subscription is ready for use on the server
  disconnected: ->
    # Called when the subscription has been terminated by the server
  received: (data) ->
    this.update_counter(data.counter)
  update_counter: (counter) ->
    $counter = $('#notification-counter')
    val = parseInt $counter.text()
    val++
    $counter
    .text(val)
    .css({top: '-10px'})
=> Sau khi tạo đối tượng notification xong thì nó sẽ thực hiện gọi NotificationBroadcastJob.perform rồi ra view, ở view chúng ta có file notify.coffee sẽ bắt kếnh truyền đó
++++ Bây giờ bạn muốn gửi đên từng user mà bạn muốn, MÌnh nghĩ bạn nên tạo 1 model để thấy rõ hành động này.. vì nãy giờ thực hiện tạo notification sau khi tạo 1 user. Nhưng bây giờ mình gửi notification cho user. nên bạn hạy tọ thêm 1 model. và model này chưa id người tạo. vd: rails g model Course user:references name:string Bây giờ bạn thêm trong CourseController.rb
def create
    @course = Course.new course_params
    if @course.save
      flash[:success] = t "devise.registrations.signed_up"
      create_notification_for_member Course.name, @course, "create", @course.user_id
      redirect_to courses_path
    else
      render :new
    end
  end
trước hết các bạn tạo kết nối cho nó: app/chanels/application_cable/connection.rb
module ApplicationCable
  class Connection < ActionCable::Connection::Base
    identified_by :current_user
    def connect
      self.current_user = find_verified_user
      logger.add_tags "ActionCable", current_user.email
    end
    protected
    def find_verified_user
      if current_user = User.find_by(id: cookies.signed["user.id"])
        current_user
      else
        reject_unauthorized_connection
      end
    end
  end
end
Bạn kiểm tra kết nối có thành công không. Nếu không kết nối đc chứng tỏ nó k lấy được cookies của các user đang đăng nhập, thì bạn thêm 1 file này : ../config/initializers/warden_hooks.rb
Warden::Manager.after_set_user do |user, auth, opts|
  scope = opts[:scope]
  auth.cookies.signed["#{scope}.id"] = user.id
  auth.cookies.signed["#{scope}.expires_at"] = 30.minutes.from_now
end
Warden::Manager.before_logout do |user, auth, opts|
  scope = opts[:scope]
  auth.cookies.signed["#{scope}.id"] = nil
  auth.cookies.signed["#{scope}.expires_at"] = nil
end
Bây giờ các bạn sữa lại
- file ../noitify_chanel.rb
class NotifyChannel < ApplicationCable::Channel
  def subscribed
    stream_from "notify_channel_#{current_user.id}"
  end
  def unsubscribed
  end
end
- file ../notification_broadcast_job.rb
class NotificationBroadcastJob < ApplicationJob
 queue_as :default
   # ở đây các bạn override nó
 def perform count, notification
     # ở trên khi tạo 1 notification mình có lưu id người nhận là user_id, nên kênh truyên đi cho những người đang đăng nhập đúng id đó sẽ được nhận
   ActionCable.server.broadcast "notify_channel_#{notification.user_id}", counter: render_count_notiication(count)
 end
 def render_count_notiication count
   ApplicationController.renderer.render partial: "layouts/count_notification",
     locals: {count_notification: count}
 end
end
Chúc các bạn thành công, Link tham khảo: activity: https://github.com/chaps-io/public_activity notification: https://gist.github.com/excid3/4ca7cbead79f06365424b98fa7f8ecf6
All rights reserved
 
  
 