使用FCM为前台浏览器中的每个选项卡接收一个通知

时间:2017-06-02 09:43:38

标签: javascript firebase firebase-cloud-messaging

我使用FCM API接收来自浏览器的推送通知。 firebase-messaging-sw.js按预期工作,messaging.setBackgroundMessageHandler仅在网络应用处于后台时触发一次。但是,当应用程序处于前台时,我会收到每个浏览器选项卡的一个通知(如果我在3个选项卡中打开了应用程序,则会收到3个通知)。我想知道我应该怎么处理这个,因为我找不到任何关于这个问题的参考。这是前台FCM消息的代码:

import NotificationActionCreators from '../actions/NotificationActionCreators';
import NotificationService from './NotificationService';
import LocalStorageService from './LocalStorageService';
import { FIREBASE_SCRIPT, FCM_URL, FCM_API_KEY, FCM_AUTH_DOMAIN, FCM_PROJECT_ID, FCM_SENDER_ID, PUSH_PUBLIC_KEY } from '../constants/Constants';


class ServiceWorkerService {

constructor() {
    this._messaging = null;
    this._subscriptionData = null;
}

// This function is called once
init() {
    this.loadScript(FIREBASE_SCRIPT, () => this.onFirebaseLoaded());
}

onFirebaseLoaded() {
    // Initialize Firebase
    let config = {
        apiKey: FCM_API_KEY,
        authDomain: FCM_AUTH_DOMAIN,
        projectId: FCM_PROJECT_ID,
        messagingSenderId: FCM_SENDER_ID
    };
    firebase.initializeApp(config);
    this._messaging = firebase.messaging();

    this.requestPermission();

    // Callback fired if Instance ID token is updated.
    this._messaging.onTokenRefresh(() => {
        this._messaging.getToken()
            .then((refreshedToken) => {
                console.log('Token refreshed.');
                NotificationActionCreators.unSubscribe(this._subscriptionData).then(() => {
                    // Indicate that the new Instance ID token has not yet been sent to the
                    // app server.
                    this.setTokenSentToServer(false);
                    // Send Instance ID token to app server.
                    this.sendTokenToServer(refreshedToken);
                }, () => console.log('Error unsubscribing user'));
            })
            .catch(function(err) {
                console.log('Unable to retrieve refreshed token ', err);
            });
    });

    // Handle incoming messages.
    // *** THIS IS FIRED ONCE PER TAB ***
    this._messaging.onMessage(function(payload) {
        console.log("Message received. ", payload);
        const data = payload.data;

        NotificationActionCreators.notify(data);
    });
}

requestPermission() {
    console.log('Requesting permission...');
    return this._messaging.requestPermission()
        .then(() => {
            console.log('Notification permission granted.');
            this.getToken();
        })
        .catch(function(err) {
            console.log('Unable to get permission to notify.', err);
        });
}

getToken() {
    // Get Instance ID token. Initially this makes a network call, once retrieved
    // subsequent calls to getToken will return from cache.
    return this._messaging.getToken()
        .then((currentToken) => {
            if (currentToken) {
                this.sendTokenToServer(currentToken);
            } else {
                // Show permission request.
                console.log('No Instance ID token available. Request permission to generate one.');
                this.setTokenSentToServer(false);
            }
        })
        .catch(function(err) {
            console.log('An error occurred while retrieving token. ', err);
            this.setTokenSentToServer(false);
        });
}

sendTokenToServer(currentToken) {
    const subscriptionData = {
        endpoint: FCM_URL + currentToken,
        platform: 'Web'
    };
    if (!this.isTokenSentToServer()) {
        console.log('Sending token to server...');
        this.updateSubscriptionOnServer(subscriptionData);
    } else {
        console.log('Token already sent to server so won\'t send it again ' +
            'unless it changes');
    }
    this._subscriptionData = subscriptionData;
}

isTokenSentToServer() {
    return LocalStorageService.get('sentToServer') == 1;
}

setTokenSentToServer(sent) {
    LocalStorageService.set('sentToServer', sent ? 1 : 0);
}

updateSubscriptionOnServer(subscriptionData) {
    if (subscriptionData) {
        NotificationActionCreators.subscribe(subscriptionData);
        this.setTokenSentToServer(true);
        this._subscriptionData = subscriptionData;
    } else {
        console.log('Not subscribed');
    }
}

unSubscribe() {
    this.removeSetTokenSentToServer();
    return this._messaging.getToken()
        .then((currentToken) => {
            return this._messaging.deleteToken(currentToken)
                .then(() => {
                    console.log('Token deleted.');
                    return NotificationActionCreators.unSubscribe(this._subscriptionData);
                })
                .catch(function(err) {
                    console.log('Unable to delete token. ', err);
                    return new Promise(function(resolve, reject) {
                        reject(error)
                    });
                });
       })
        .catch(function(err) {
            console.log('Error retrieving Instance ID token. ', err);
            return new Promise(function(resolve, reject) {
                reject(error)
            });
        });
    }
}

removeSetTokenSentToServer() {
    LocalStorageService.remove('sentToServer');
}

loadScript = function (url, callback) {
    let head = document.getElementsByTagName('head')[0];
    let script = document.createElement('script');
    script.type = 'text/javascript';
    script.src = url;

    script.onload = callback;

    // Fire the loading
    head.appendChild(script);
}
}

有没有办法只针对找到的第一个标签显示通知?

3 个答案:

答案 0 :(得分:9)

我发现实现此目的的唯一方法是使用setTimeout随机时间在本地存储中检查并设置“通知ID”变量:

this._messaging.onMessage(function(payload) {
    const data = payload.data;
    // This prevents to show one notification for each tab
    setTimeout(() => {
        if (localStorage.getItem('lastNotificationId')) != parseInt(data.notId)) {
            localStorage.setItem('lastNotificationId', parseInt(data.notId))
            NotificationActionCreators.notify(data);
        }
    }, Math.random() * 1000);
});

notId在推送​​通知中发送,并且是通知的标识符。

答案 1 :(得分:0)

一种方法是为每个选项卡创建一个唯一变量,例如Math.random()或(new Date())。getMilliseconds()并将其与令牌存储在服务器上。现在,服务器可以通过将变量附加到消息中来定位每个选项卡,并且每个选项卡在执行操作之前都会检查消息变量。

要减少定位关闭的标签的几率,请在每次请求时都将变量发送出去,因此服务器始终以最新的请求为目标。

答案 2 :(得分:0)

使用document.hidden检测活动标签页

if (!document.hidden) {
    NotificationActionCreators.notify(data);
}