mayorsanmayor
mayorsanmayor

Reputation: 2988

Consume Rails ActionCable Object in React Frontend.

I have an Appointment model, I want to send a broadcast to the frontend application in react every time an appointment is created or updated. Here's the code of my model.

class Appointment < ApplicationRecord
  belongs_to :tutor, class_name: 'User'
  belongs_to :student, class_name: 'User'

  after_create :appointment_notification 
  after_update :appointment_notification 

  def appointment_notification
    Notification.create(
      from: student,
      to: tutor,
      name: :student_create_appointment, # here you can detect any type
      model: :appointment
    )
  end
end

and Notification model and table to save the history of all notifications

class Notification < ApplicationRecord

  belongs_to :from, class_name: 'User', foreign_key: :from_id
  belongs_to :to, class_name: 'User', foreign_key: :to_id

  before_create :set_seen

  after_create :push_notification

  def set_seen
    self.seen = false
  end

  def push_notification
    if to.user_push_notification.respond_to?(name) &&
       to.user_push_notification.send(name)

      PushNotificationJob.perform_later(
        from: from,
        to: to,
        message: message(:push),
        name: name,
        created_at: created_at.iso8601
      )
    end
  end


  def message(gate_scope)
    # gate_scope can be 'sms' or 'push' or 'email.body' if we have nested yml in translations
    I18n.t(
      "notification.#{model}.#{name}.#{gate_scope}",
      from: from,
      to: to,
      created_at: created_at
    )
  end
end

I have created a NotificationsChannel to look like so:

class NotificationsChannel < ApplicationCable::Channel
  def subscribed
    stream_from "notification_channel:#{current_user.id}"
  end

  def unsubscribed
    stop_all_streams
  end
end

And a PushNotificationJob to look like this:

class PushNotificationJob < ApplicationJob
  queue_as :default

  def perform(from:, to:, message:, name:, created_at:)
    NotificationsChannel.broadcast_to(
      to,
      type: name,
      caller: from,
      message: message,
      created_at: created_at
    )
  end
end

Everything works great, the only missing link is the part where I get to broadcast it to the user on the frontend: Here's what I have on the JavaScript side so far.

App.notificationsChannel = App.cable.subscriptions.create(
  'NotificationsChannel',
  {
    connected: function() {
      // Called when the subscription is ready for use on the server
      console.log('Notification Channel connected.');
    },

    disconnected: function() {
      // Called when the subscription has been terminated by the server
      console.log('Notification Channel disconnected.');
    },

    received: function(data) {
      // Called when there's incoming data on the websocket for this channel
      console.log(data);
    }
  }
);

// App.notificationsChannel.send({ test: 'data' });

I can't get anything to print in the browser console, except what's there in connect and disconnect.

After creating an Appointment this is what my terminal log looks like.

enter image description here

Any idea what else I'm missing and what I need to do?

BTW, I also created these routes URLs in the route file

resources :notifications, only: :index do
   collection do
       post 'seen_all', to: "notifications#seen_all"
    end
    member do
       post :seen
    end
end

And finally my NotificationsController.

module API
  module V1
    class NotificationsController < ApiController
      before_action :set_user

      def index
        @user.incoming_notifications.page(params[:page]).per(params[:per_page])
      end

      def seen_all
        Notification.where(seen: false, to_id: @user.id).update(seen: true)
      end

      def seen
        @user.incoming_notifications.find_by(id: params[:id]).seen!
      end

      private

      def set_user
        @user = current_user
      end
    end
  end
end

Please guide me on how to print the notifications in the browser console and thereafter, consume it in React through the API. Thanks.

Upvotes: 0

Views: 1092

Answers (2)

Abhishek Kanojia
Abhishek Kanojia

Reputation: 837

Please check the Delayed Job Logs for more information. Give the following a try:

 def perform(from:, to:, message:, name:, created_at:)
    ActionCable.server.broadcast "notification_channel:#{to.id}", { type: type, caller: caller.name, message: message, created_at: created_at }
  end

A tip here: use after_commit: :do_something, on: [:create, :update] instead of using after_create. This will ensure that the notification will be triggered only after successful creation of Appointment.

Upvotes: 2

Usman Khan
Usman Khan

Reputation: 388

you can use the following tutorial to get this working. This is quite simple to achieve Using Action Cable With React I have already followed the guidelines for a few projects.

Upvotes: 0

Related Questions