Ionic 3的PWA和Firebase云消息注册

Ser*_*nko 6 progressive-web-apps firebase-cloud-messaging ionic3

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

我做了什么:

  1. 正如文章中所建议的那样,将FCM库添加到service-worker.js中:

'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;
Run Code Online (Sandbox Code Playgroud)

  1. 然后在此处创建基于Firebase Messaging的提供程序:

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(); });
    });
  }
    
}
Run Code Online (Sandbox Code Playgroud)

现在在应用程序初始化期间,我调用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中做同样的事情.

Ser*_*nko 10

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

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

  1. 先决条件:
    • 我创建了离子空白应用程序(只是一个主页)
    • 安装angularfire2和firebase("angularfire2":"5.0.0-rc.4","firebase":"4.9.1")使用npm安装,我特别使用5.0.0-rc.4"因为我有稳定性问题最新的一个;(
    • 创建了config(src文件夹中的filename environment.ts):

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"
};
Run Code Online (Sandbox Code Playgroud)

  1. 我修改app.module.ts以这种方式添加firebase和angularfire2:

...
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 {}
Run Code Online (Sandbox Code Playgroud)

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

  1. 在providers文件夹中,我创建了firebase-messaging.ts,如下所示:

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(); });
    });
  }
    
}
Run Code Online (Sandbox Code Playgroud)

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

  1. 服务worker.js:

// 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);
});
Run Code Online (Sandbox Code Playgroud)

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

  1. src中的index.html取消注释:

 <!-- 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>
Run Code Online (Sandbox Code Playgroud)

  1. 好的几乎是最后一件事 - 你的manifest.json(在src中)应该有一行:"gcm_sender_id":"103953800507"

这结束了客户端的初始内容.请注意,当用户在应用程序本身时,我还没有实现处理通知的任何内容,现在想想它只是处理从服务器发送消息而您的选项卡不在焦点时(这是我测试的).

  1. 现在,您想要访问firebase控制台并获取服务器密钥(单击设置齿轮图标,然后查看云消息部分).复制服务器密钥.还运行客户端(离子服务并捕获你的本地令牌(我只是控制台.记录它).现在尝试使用POST方法向自己发送消息.(我用Postman做了)

// 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
    }
Run Code Online (Sandbox Code Playgroud)

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

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