Ionic 3的PWA& Firebase云消息传递注册

时间:2018-02-07 17:02:46

标签: firebase-cloud-messaging ionic3 progressive-web-apps

我在这里关注这篇文章(遗憾的是这并不完整),试图学习如何结识基于Ionic 3的PWA和Firebase云消息传递:Push Notifications with FCM

我做了什么:

  1. 正如文章中所建议的那样,将FCM库添加到service-worker.js:
  2. 'use strict';
    importScripts('./build/sw-toolbox.js');
    importScripts('https://www.gstatic.com/firebasejs/4.9.0/firebase-app.js');
    importScripts('https://www.gstatic.com/firebasejs/4.9.0/firebase-messaging');
    
    firebase.initializeApp({
      // get this from Firebase console, Cloud messaging section
      'messagingSenderId': '47286327412'
    });
    
    const messaging = firebase.messaging();
    
    messaging.setBackgroundMessageHandler((payload) => {
      console.log('Received background message ', payload);
      // here you can override some options describing what's in the message; 
      // however, the actual content will come from the service sending messages
      const notificationOptions = {
        icon: '/assets/img/appicon.png'
      };
      return self.registration.showNotification(notificationTitle, notificationOptions);
    });
    
    self.toolbox.options.cache = {
      name: 'ionic-cache'
    };
    
    // pre-cache our key assets
    self.toolbox.precache(
      [
        './build/main.js',
        './build/vendor.js',
        './build/main.css',
        './build/polyfills.js',
        'index.html',
        'manifest.json'
      ]
    );
    
    // dynamically cache any other local assets
    self.toolbox.router.any('/*', self.toolbox.cacheFirst);
    
    // for any other requests go to the network, cache,
    // and then only use that cached resource if your user goes offline
    self.toolbox.router.default = self.toolbox.networkFirst;

    1. 然后在此处创建基于Firebase Messaging的提供程序:
    2. import { Injectable } from "@angular/core";
      import * as firebase from 'firebase';
      import { Storage } from '@ionic/storage';
      
      @Injectable()
      export class FirebaseMessagingProvider {
        private messaging: firebase.messaging.Messaging;
        private unsubscribeOnTokenRefresh = () => {};
      
        constructor(
          private storage: Storage
        ) {
          this.messaging = firebase.messaging();
        }
      
        public enableNotifications() {
          console.log('Requesting permission...');
          return this.messaging.requestPermission().then(() => {
              console.log('Permission granted');
              // token might change - we need to listen for changes to it and update it
              this.setupOnTokenRefresh();
              return this.updateToken();
            });
        }
      
        public disableNotifications() {
          this.unsubscribeOnTokenRefresh();
          this.unsubscribeOnTokenRefresh = () => {};
          return this.storage.set('fcmToken','').then();
        }
      
        private updateToken() {
          return this.messaging.getToken().then((currentToken) => {
            if (currentToken) {
              // we've got the token from Firebase, now let's store it in the database
              return this.storage.set('fcmToken', currentToken);
            } else {
              console.log('No Instance ID token available. Request permission to generate one.');
            }
          });
        }
      
        private setupOnTokenRefresh(): void {
          this.unsubscribeOnTokenRefresh = this.messaging.onTokenRefresh(() => {
            console.log("Token refreshed");
            this.storage.set('fcmToken','').then(() => { this.updateToken(); });
          });
        }
          
      }

      现在在应用初始化期间,我调用enableNotifications()并收到错误消息,指出找不到默认服务工作者(404):

      获取脚本时收到错误的HTTP响应代码(404)。 :8100 / firebase-messaging-sw.js无法加载资源:net :: ERR_INVALID_RESPONSE

      如果我将service-worker.js firebase相关内容移动到WWW文件夹中的默认服务工作者 - 我从Firebase获得一般错误(错误,无法注册服务工作者)。

      问题: - Ionic 3的PWA&是否有新的指南? FCM? - 在高层次上,在Ionic 3和Angular中注册服务人员的区别是什么?我确实看过关于Angular的教程,但无法想象如何在Ionic 3中做同样的事情。

2 个答案:

答案 0 :(得分:10)

更新:以下内容自今天(02/12/2018)起生效,一旦AngularFire2支持消息传递模块,很可能不太相关。因此,请采用以下假设......

好的,我研究过并最终在我的Ionic 3 PWA上运行,所以我在这里发布解决方案:

  1. 先决条件:
    • 我创建了离子空白应用程序(只是一个主页)
    • 使用npm install安装了angularfire2和firebase(“angularfire2”:“5.0.0-rc.4”,“firebase”:“4.9.1”),我特意使用5.0.0-rc.4“因为我有最新的稳定性问题;(
    • 创建了config(src文件夹中的filename environment.ts):
  2. export const firebaseConfig = {
        apiKey: "Your Stuff Here from FB",
        authDomain: "YOURAPPNAME.firebaseapp.com",
        databaseURL: "https://YOURAPPNAME.firebaseio.com",
        projectId: "YOURAPPNAME",
        storageBucket: "YOURAPPNAME.appspot.com",
        messagingSenderId: "FROMFIREBASECONEOLE"
    };

    1. 我修改了app.module.ts以这种方式添加firebase和angularfire2:
    2. ...
      import { AngularFireModule } from 'angularfire2';
      import 'firebase/messaging'; // only import firebase messaging or as needed;
      import { firebaseConfig } from '../environment';
      import { FirebaseMessagingProvider } from '../providers/firebase-messaging';
      ...
      
      @NgModule({
        declarations: [
          MyApp,
          HomePage
        ],
        imports: [
          BrowserModule,
          IonicModule.forRoot(MyApp),
          AngularFireModule.initializeApp(firebaseConfig),
          IonicStorageModule.forRoot()
        ],
        bootstrap: [IonicApp],
        entryComponents: [
          MyApp,
          HomePage
        ],
        providers: [
          FirebaseMessagingProvider,
          StatusBar,
          SplashScreen,
          {provide: ErrorHandler, useClass: IonicErrorHandler}
        ]
      })
      export class AppModule {}

      在这里,我们还导入我们的提供商,其代码如下:

        在provider文件夹中的
      1. 我创建了firebase-messaging.ts,如下所示:
      2. import { Injectable } from "@angular/core";
        import { FirebaseApp } from 'angularfire2';
        // I am importing simple ionic storage (local one), in prod this should be remote storage of some sort.
        import { Storage } from '@ionic/storage';
        
        @Injectable()
        export class FirebaseMessagingProvider {
          private messaging;
          private unsubscribeOnTokenRefresh = () => {};
        
          constructor(
            private storage: Storage,
            private app: FirebaseApp
          ) {
            this.messaging = app.messaging();
            navigator.serviceWorker.register('service-worker.js').then((registration) => {
            this.messaging.useServiceWorker(registration);
            //this.disableNotifications()
            this.enableNotifications();
        });
          }
        
          public enableNotifications() {
            console.log('Requesting permission...');
            return this.messaging.requestPermission().then(() => {
                console.log('Permission granted');
                // token might change - we need to listen for changes to it and update it
                this.setupOnTokenRefresh();
                return this.updateToken();
              });
          }
        
          public disableNotifications() {
            this.unsubscribeOnTokenRefresh();
            this.unsubscribeOnTokenRefresh = () => {};
            return this.storage.set('fcmToken','').then();
          }
        
          private updateToken() {
            return this.messaging.getToken().then((currentToken) => {
              if (currentToken) {
                // we've got the token from Firebase, now let's store it in the database
                console.log(currentToken)
                return this.storage.set('fcmToken', currentToken);
              } else {
                console.log('No Instance ID token available. Request permission to generate one.');
              }
            });
          }
        
          private setupOnTokenRefresh(): void {
            this.unsubscribeOnTokenRefresh = this.messaging.onTokenRefresh(() => {
              console.log("Token refreshed");
              this.storage.set('fcmToken','').then(() => { this.updateToken(); });
            });
          }
            
        }

        请注意我初始化firebase应用程序,然后在构造函数中注册离子的默认服务工作者(service-worker.js),默认情况下包含以下内容:

        1. 服务worker.js:
        2. // firebase messaging part:
          importScripts('https://www.gstatic.com/firebasejs/4.9.0/firebase-app.js');
          importScripts('https://www.gstatic.com/firebasejs/4.9.0/firebase-messaging.js');
          
          firebase.initializeApp({
            // get this from Firebase console, Cloud messaging section
            'messagingSenderId': 'YOURIDFROMYOURFIREBASECONSOLE' 
          });
          
          const messaging = firebase.messaging();
          
          messaging.setBackgroundMessageHandler(function(payload) {
            console.log('Received background message ', payload);
            // here you can override some options describing what's in the message; 
            // however, the actual content will come from the Webtask
            const notificationOptions = {
              icon: '/assets/images/logo-128.png'
            };
            return self.registration.showNotification(notificationTitle, notificationOptions);
          });

          此时你还需要确保你的应用程序已启用为PWA,Josh Morony有一个很好的指南,今天在youtube上有一个视频流来覆盖它。在TLDR中,您需要在index.html中取消注释:

          1. index中的src取消注释:
          2.  <!-- un-comment this code to enable service worker -->
              <script>
                if ('serviceWorker' in navigator) {
                  navigator.serviceWorker.register('service-worker.js')
                    .then(() => console.log('service worker installed'))
                    .catch(err => console.error('Error', err));
                }
              </script>

            1. 好的几乎是最后一件事 - 你的manifest.json(在src中)应该有一个确切的行: “gcm_sender_id”:“103953800507”
            2. 这结束了客户端上的初始内容。请注意,当用户在应用程序本身时,我没有实现处理通知的任何内容,现在想想它只是处理从服务器发送消息而您的选项卡不在焦点时(这是我测试的)。

              1. 现在您想要访问您的firebase控制台并获取服务器密钥(单击设置齿轮图标,然后查看那里的云消息部分)。复制服务器密钥。还运行客户端(离子服务并捕获你的本地令牌(我只是控制台。记录它)。现在尝试使用POST方法向自己发送消息。(我用Postman做过)
              2. // method: "POST",
                //url: "https://fcm.googleapis.com/fcm/send",
                    // get the key from Firebase console
                    headers: { Authorization: `key=${fcmServerKey}` }, 
                    json: {
                        "notification": { 
                            "title": "Message title",
                            "body": "Message body",
                            "click_action": "URL to your app?"
                        },
                        // userData is where your client stored the FCM token for the given user
                        // it should be read from the database
                        "to": userData.fcmRegistrationKey
                    }

                通过这样做,我能够在应用程序处于后台时向自己发送消息。我还没有处理前景,但是这个问题是关于如何初始化默认服务工作者并将其与FCM结合。

                我希望这将有助于未来的学习者。

答案 1 :(得分:0)

我已经成功实现了该过程,并获得了API调用的成功响应。但是我的浏览器上没有通知弹出窗口。 有什么想法吗?

api: https://fcm.googleapis.com/fcm/send

收到的回复:

  

{“ multicast_id”:6904414188195222649,“ success”:1,“ failure”:0,“ canonical_ids”:0,“ results”:[{“ message_id”:“ 0:1545375125056264%e609af1cf9fd7ecd”}]}}

检查控制台的附加网址: enter image description here