Облачные уведомления Firebase в Интернете с помощью React и Rails

Незадолго до этого я начал читать о Firebase. Перед прочтением у меня создалось впечатление, что это только для облачных уведомлений, но теперь я узнал о различных способах использования и преимуществах сервисов Firebase. Я хотел бы поделиться своим опытом со всеми вами, пожалуйста, не стесняйтесь обсуждать об этом больше, потому что я тоже нахожусь на этапе обучения, и это будет полезно для читателей.

Мы широко знаем Firebase для push-уведомлений на iOS, Android и в Интернете. Я уже использовал FCM на android и ios, несколько дней назад я также делал POC в Интернете. В этом блоге я собираюсь объяснить реализацию push-уведомлений в Интернете.

Push-уведомление в Интернете -

Когда я начал работать над отправкой push-уведомлений в Интернете, у меня возникло так много вопросов, как показано ниже -

1] Как настроить firebase с веб-приложением?

2] Кто может видеть уведомления? Авторизованные пользователи или все пользователи.

3] Нужно ли отправлять уведомление конкретным пользователям?

4] Нужна ли нам подписка по теме?

5] Как управлять уведомлениями на сайтах, которые полностью основаны на api и созданы в библиотеке, такой как react.js или angular framework.

Шаг 1 - Получите сообщения

Настроить учетную запись Firebase -

Я проведу вас через несколько основных шагов по настройке, поскольку это хорошо документировано на веб-сайте Firebase.

1] Создайте проект Firebase в консоли Firebase.

2] Если у вас нет существующего проекта Firebase, нажмите Добавить проект и введите либо имя существующего проекта Google Cloud Platform, либо имя нового проекта. Если у вас уже есть приложений в своем проекте, нажмите Добавить другое приложение на странице обзора проекта. На странице обзора проекта в консоли Firebase нажмите Добавить Firebase в свое веб-приложение.

3] Добавьте ниже настраиваемый фрагмент кода в firebase-messaging-sw.js.

В приведенном ниже примере у меня есть файл служебного работника. Нам нужно сохранить файл сервис-воркера в корне проекта.

/* #Filename - firebase-messaging-sw.js
 * We can only add firebase-messaging serive in the serive worker others 
 * services not allowed here. 
 */

importScripts('https://www.gstatic.com/firebasejs/3.5.0/firebase-app.js')
importScripts('https://www.gstatic.com/firebasejs/3.5.0/firebase-messaging.js')

/* Now initialize firebase app in the servie worker.
 *
 */
firebase.initializeApp({
    apiKey: "AOzaSyAc54587dshfiuedP5uZLio",
    authDomain: "stage-notifications.firebaseapp.com",
    databaseURL: "https://stage-notications.firebaseio.com",
    projectId: "stage-notifications",
    storageBucket: "stage-notications.appspot.com",
    messagingSenderId: "916580506"
}); //These are example configuration value

Существует множество сервисов Firebase, но в этой статье я сосредоточусь только на обмене сообщениями, для этого мы воспользуемся сервисом ниже.

  • firebase-messaging - Firebase Cloud Messaging (FCM)

Добавьте ниже фрагмент после инициализации приложения

/* #Filename - firebase-messaging-sw.js
 * This retrives firebase messageing instance into service worker to handle 
 * background message.*/

const messaging = firebase.messaging();

Есть разное поведение сообщений

  • Приложение на переднем плане
  • Приложение в фоновом режиме
  • Открыта другая вкладка
  • Приложение полностью закрыто

Во всех этих сценариях мы должны получить обратный вызов «onMessage», а когда приложение находится в фоновом режиме, нам нужно обработать функцию setBackgroungMessageHandler (), которая будет обрабатывать сообщения, когда наше приложение находится в фоновом режиме. . Мы можем отображать сообщения, когда приложение находится в фоновом режиме, и, щелкнув уведомление, мы можем вернуть приложение на передний план.

Посмотрите на фрагмент ниже, который обрабатывает сообщения, когда приложение находится в фоновом режиме.

// ##Filename - firebase-messaging-sw.js

messaging.setBackgroundMessageHandler(function (payload) {
  const notificationTitle = payload.data.title;
  const notificationOptions = {
    body: payload.data.body,
    icon: payload.data.icon
  };
  return self.registration.showNotification(notificationTitle,
    notificationOptions);
});
/* Here we have provided body, icon to show inside notification.*/

Пожалуйста, проверьте полностью firebase-messaging-sw.js -

#Filename- firebase-messaging-sw.js 

/*We can only add firebase-messaging serive in the serive worker others 
 * services not allowed here. 
 */

importScripts('https://www.gstatic.com/firebasejs/3.5.0/firebase-app.js')
importScripts('https://www.gstatic.com/firebasejs/3.5.0/firebase-messaging.js')

/* Now initialize firebase app in the servie worker.
 *
 */
firebase.initializeApp({
    apiKey: "AOzaSyAc54587dshfiuedP5uZLio",
    authDomain: "stage-notifications.firebaseapp.com",
    databaseURL: "https://stage-notifications.firebaseio.com",
    projectId: "stage-notifications",
    storageBucket: "stage-notifications.appspot.com",
    messagingSenderId: "916580506"
}); //These are example configuration value


const messaging = firebase.messaging();

messaging.setBackgroundMessageHandler(function (payload) {
  const notificationTitle = payload.data.title;
  const notificationOptions = {
    body: payload.data.body,
    icon: payload.data.icon
  };
  return self.registration.showNotification(notificationTitle,
    notificationOptions);
});

Резюме сервис-воркера Firebase.

  • Импортируйте приложение firebase и обмен сообщениями firebase, это позволяет работнику службы доступа к firebase.
  • Инициализируйте все конфигурации Firebase.
  • Получите экземпляр обмена сообщениями Firebase для обработки фоновых сообщений.
  • Добавьте функцию setBackgroundMessageHandler () для обработки фонового сообщения.

Шаг 2 - Настройте облачный обмен сообщениями в веб-приложении.

После настройки нашего сервис-воркера наша основная задача остается, как работает это уведомление Отправка / Получение?

Для лучшего понимания приведу пример собственного приложения.

У меня 2 разных приложения.

  1. Приложение «А» для отправки уведомлений в Интернет, iOS и Android.
  2. Приложение «B» будет получать уведомления, отправленные приложением «A».

Для этого я делаю подписку по теме.

Ждать!! , что теперь такое подписка по теме?

Обмен сообщениями по темам основан на модели публикации / подписки. Время от времени несколько устройств могут подписаться на темы и получать сообщения, отправленные по этой теме. FCM обрабатывает сообщения для доставки сообщений на подписанные устройства. На ios и android плагин FCM предоставляет слушателей, которые всегда будут прослушивать сообщения, но в Интернете нам нужно связать тему с токеном регистрации приложения с помощью FCM api, и тогда веб-приложение может получать уведомления об уникальном экземпляре приложения.

Для получения более подробной информации перейдите по ссылке Подписка по темам в Интернете.

Я создал 3 темы для android, ios и web в серверном приложении «A», так как я хочу отправить 3 разных типа уведомлений на мобильных устройствах и в Интернете. В приложениях для iOS и Android я написал такой код, что приложение всегда будет прослушивать определенную тему, чтобы получать уведомления, но в веб-сценарии это немного отличается.

В Интернете нам нужно запросить разрешение пользователя на показ push-уведомлений в приложении с помощью firebase_messaging.requestPermission (). Если пользователь разрешает показывать уведомление, Firebase создает уникальный токен на клиентском компьютере с помощью firebase_messaging. getToken () api. Затем нам нужно отправить этот уникальный токен в Firebase. Не зная токена, Firebase не может отправлять push-уведомления в нашем приложении. Мы можем передать наш регистрационный токен в FCM с помощью batchAdd api, а затем нам нужно связать нашу тему с уникальным регистрационным токеном приложения, поэтому при срабатывании уведомления FCM отправит это сообщение на наше приложение.

Если пользователь вышел из системы, мы также можем удалить связь с темой и нашим экземпляром приложения, то есть уникальным регистрационным идентификатором, используя deleteToken api, чтобы пользователь не получал уведомления.

Есть несколько других API, которые мы можем использовать для проверки наличия регистрационного токена в FCM, а также для проверки экземпляров приложения BatchRemove. Я не буду много писать об этом, вы можете проверить эти API в приведенной ниже ссылке на документацию по firebase, чтобы вы могли улучшить свой код в соответствии с вашими требованиями. Справочник по серверу

Нам также необходимо зарегистрировать нашего сервис-воркера в браузере, чтобы он всегда прослушивал сообщения. пожалуйста, проверьте нижеприведенный фрагмент.

// #Filename - messaging.js

  (function() {
    if('serviceWorker' in navigator) {
      navigator.serviceWorker.register('/firebase-messaging-sw.js');
    }
  })();

В приведенном ниже коде вы можете проверить, как запросить разрешение, создать токен, обновить токен, проверить, присутствует ли токен в FCM, если нет, то отправить токен в FCM с помощью batchAdd, связать тему с уникальным регистрационным токеном, удалить токен и т. Д.

  • messaging.js -
import firebase from 'firebase';
import request from 'utility/restUtils';
import API_ENDPOINT from 'utility/api-endpoints';

  if (!firebase.apps.length) {
    firebase.initializeApp({
       apiKey: "AOzaSyAc54587dshfiuedP5uZLio",
       authDomain: "stage-notifications.firebaseapp.com",
       databaseURL: "https://stage-notifications.firebaseio.com",
       projectId: "stage-notifications",
       storageBucket: "stage-nitifications.appspot.com",
       messagingSenderId: "916580506"
    });
  }

  const FIREBASE_MESSAGING = firebase.messaging();


  /**
  * Name : subscribeToNotifications()
  * Description: This method will request permission to user to show notification on browser.
  * If user allow to show push notifications then it will procide with rest of the steps.
  **/export function subscribeToNotifications() {
    FIREBASE_MESSAGING.requestPermission()
      .then(() => FIREBASE_MESSAGING.getToken())
      .then(token => { handleTokenRefresh(token)})
      .catch((err) => {
        console.log("error getting permission :(");
      });
  }


  /**
  * Name : handleTokenRefresh()
  * Description: This method passes token with firebase api to check if this token is already exist.
  * We also get topic subscription details in this method so we can check if user is already subscribed  
  * with the tpoic or we need to call api to get him subscribe for topic.
  **/

  function handleTokenRefresh(token) {
    window.firebaseToken = token;
    return request.doGetFirebase(API_ENDPOINT.formatUrl(API_ENDPOINT.firebaseTokenDetails,
     window.firebaseToken))
      .then((resp) => resp.json())
      .then((res) => {
        checkSubscription(res)
      })
      .catch(e => console.log("handleRefreshToken: "+e.message))
    }


  /**
  * Name : checkSubscription()
  * Description: If registration token is not added in FCM then we add it to firebase 
  *  so we could send push notification to added tokens.
  *  Here we are sendign topic "firebase-myWebApp" and unique registration token
  *  of app to relate with each other.
  **/function checkSubscription(resp) {
    if(resp) {
      if(resp.status != 200 || !resp.authorizedEntity || !resp.rel) {
        var reqBody = {
          "to" : "/topics/firebase-myWebApp",
          "registration_tokens" : [`${window.firebaseToken}`]
        }
        return request.doPostFirebase(API_ENDPOINT.firebaseBatchAddApi, reqBody)
        .then((re) => relateAppInstanceWithTopic(re))
        .catch(e => console.log("checkSubscription: "+e.message))
      }
    }
  }

  /**
  * Name : relateAppInstanceWithTopic()
  * Description: This method create relationship mapping with registration token and topic. 
  **/function relateAppInstanceWithTopic(res) {
    if(res.status == 200 && !res.results) {
      let params = [  
            window.firebaseToken,
            'firebase-myWebApp'
          ]
      return request.doPostFirebase(API_ENDPOINT.formatUrl(API_ENDPOINT.mapTopicWithFirebaseInstance,
       ...params))
      .then((response) => {
        if(response.status == 200) {
          console.log('User is subscribed for push notification.')
        } else {
          console.log('User is not subscribed for push notification.')
        }
      })
      .catch(e => console.log(e.message))
    }
  }

  /**
  * Name : logoutFirebase()
  * Description: Delete current user token and also remove current token from firebase database (batch).
  */export function logoutFirebase() {
    FIREBASE_MESSAGING.getToken().then((token) => {
       var fbToken = token;
        FIREBASE_MESSAGING.deleteToken(token)
        return fbToken;
    })
    .then((fbToken) => {
      var reqBody = {
          "to" : "/topics/firebase-firebase-myWebApp",
          "registration_tokens" : [`${fbToken}`]
        }
        return request.doPostFirebase(API_ENDPOINT.firebaseBatchRemove, reqBody)
        .then((res) => {
          if(res.status != 200) {
            console.log(resp.results.error)
          } 
        })
        .catch(e => console.log("checkSubscription: "+e.message))
    })
    .catch((err) => {
      console.log("error deleting token :(");
    });

  }

  /**
  * Name : onTokenRefresh()
  * Description: If token is deleted or expired , we need to create new token for current user 
  * and need to add that token in the firebase database to get push notification.
  **/
  FIREBASE_MESSAGING.onTokenRefresh(function() {
    FIREBASE_MESSAGING.getToken()
      .then(function(refreshedToken) {
        handleTokenRefresh(refreshedToken)
        .then((resp) => { checkSubscription(resp)})
        .catch((err) => {
          console.log("error getting permission :(");
        });
       
      })
      .catch(function(err) {
        console.log('Unable to retrieve refreshed token ', err);
      });
  });


  /**
  * Name : onMessage()
  * Description: When firebase receives push notification for current subscribed topic
  * this listner will get execute. 
  **/
  FIREBASE_MESSAGING.onMessage(function (payload) {
      console.log("Message received. ", payload);

  });

  (function() {
    if('serviceWorker' in navigator) {
      navigator.serviceWorker.register('/firebase-messaging-sw.js');
    }
  })();

Другие необходимые файлы -

  • api-endpoints.js -

Здесь я добавил все необходимые API.

let firebaseService = 'https://iid.googleapis.com/iid/'

var API_ENDPOINT = {
    firebaseTokenDetails:`${firebaseService}info/{0}?details=true`,
    firebaseBatchAddApi:`${firebaseService}v1:batchAdd`,
    mapTopicWithFirebaseInstance:`${firebaseService}v1/{0}/rel/topics/{1}`,
    firebaseBatchRemove: `${firebaseService}v1:batchRemove`,
    
    formatUrl: function (url) {
        var args = Array.prototype.slice.call(arguments, 1);
        return url.replace(/{(\d+)}/g, function(match, number) {
          return args[number] ? args[number] : match;
        });
    }
}

export default API_ENDPOINT;
  • requestUtils.js -

Я использую isomorphic-fetch для вызовов ajax ..

import fetch from 'isomorphic-fetch';

class Request {

    constructor() {
        this._doGetParam = this._doGetParam.bind(this);
        this._doPostParam = this._doPostParam.bind(this);

        this.firebaseHeaders = {
            'Content-Type': 'application/json',
            'Authorization': "key=AOzaSyAc54587dshfiuedP5uZLio"
        }
    }

     _doGetParam (headers) {
        var params = {
            method: 'GET',
            dataType: 'JSON',
            headers: headers
        };
        return params;
    }; 

    _doDeleteParam (headers, requestBody) {
        var params = {
            method: 'DELETE',
            dataType: 'JSON',
            headers: headers,
            body: JSON.stringify(requestBody)
        };
        return params;
    }; 

    _doPostParam (headers, requestBody) {
        var params = {
            method: 'POST',
            dataType: 'JSON',
            headers: headers,
            body: JSON.stringify(requestBody)
        };
        return params;
    }; 



    doGetFirebase (url) {
        var param = this._doGetParam(this.firebaseHeaders)
        return fetch(url, param)
    }

    doPostFirebase (url, body) {
        var params = this._doPostParam(this.firebaseHeaders, body);
        return fetch(url, params);
    }

}

var request = new Request();

export default request;

Теперь все было о настройке на стороне клиента,

Теперь как отправить уведомление приложению «Б»?

Мы отправляем push-уведомления из приложения «А», написанного на рубине. Мы отправим уведомление с помощью запроса curl, пожалуйста, проверьте его ниже.

#Filename - notifications.rb

require 'shellwords'

module Notificationdef push_notification(alert)
    firebase_key = ENV['FIREBASE_KEY']
    firebaseApi = "curl -X POST --header 'Authorization: key=#{firebase_key}' --Header 'Content-Type: application/json' https://fcm.googleapis.com/fcm/send -d "
    alert_title, alert_description =
     [ActionController::Base.helpers.strip_tags(alert.title),
      ActionController::Base.helpers.strip_tags(alert.description)]
    
    #Notification params for Mobile
    notificationParams = {
      notification: {
        body: alert_description,
        title: alert_title,
        sound: 'default',
        click_action: 'FCM_PLUGIN_ACTIVITY',
        icon: 'fcm_push_icon',
        content_available: true
      },
      data: {
        title: alert_title,
        body: alert_description,
        click_action: 'FCM_PLUGIN_ACTIVITY',
        icon: 'fcm_push_icon'
      },
      priority: "high"
    }


    #Notification params for web
    notificationParamsWeb = {
      notification: {
        body: alert_description,
        title: alert_title,
        icon: '/apple-touch-icon.png'
      }
    }

    notificationTopicAndroid = {
      to: '/topics/firebase-android'
    }
    notificationTopicIos = {
      to: '/topics/firebase-ios'
    }
    notificationTopicWeb = {
      to: '/topics/firebase-myWebApp'
    }

    requestAndroid = notificationParams.merge(notificationTopicAndroid).to_json
    requestIos = notificationParams.merge(notificationTopicIos).to_json
    requestWeb = notificationParamsWeb.merge(notificationTopicWeb).to_json
    system("#{firebaseApi} #{Shellwords.escape(requestAndroid)}")
    system("#{firebaseApi} #{Shellwords.escape(requestIos)}")
    system("#{firebaseApi} #{Shellwords.escape(requestWeb)}")
  end
end

Также подтвердите, что сервисный работник зарегистрирован в вашем браузере.

Я надеюсь, что эта статья поможет людям. Не стесняйтесь оставлять свои отзывы, чтобы мы могли сделать это лучше.

Спасибо,

Калашри Аундхкар