In my project I have setup the firebase like this:
import { initializeApp } from 'firebase/app';
import {
getMessaging,
getToken,
onMessage,
isSupported
} from 'firebase/messaging';
import store from './flag';
import { setToken } from './flag';
export const VAPID_KEY = process.env.REACT_APP_FIREBASE_VAPID_KEY
export const firebaseConfig = {
apiKey: process.env.REACT_APP_FIREBASE_apiKey,
authDomain: process.env.REACT_APP_FIREBASE_authDomain,
databaseURL: process.env.REACT_APP_FIREBASE_databaseUrl,
projectId: process.env.REACT_APP_FIREBASE_projectId,
storageBucket: process.env.REACT_APP_FIREBASE_storageBucket,
messagingSenderId: process.env.REACT_APP_FIREBASE_messagingSenderId,
appId: process.env.REACT_APP_FIREBASE_appId,
measurementId: process.env.REACT_APP_FIREBASE_measurementId
};
//Initialize Firebase Messaging
const InitializeMessaging = () => {
const app = initializeApp(firebaseConfig);
isSupported().then(is_supported => {
if (is_supported) {
requestForToken().then(fcm_token => {
store.dispatch(setToken(fcm_token));
});
}
});
};
const requestForToken = () => {
const messaging = getMessaging();
return getToken(messaging, {
vapidKey: VAPID_KEY
})
.then(currentToken => {
if (currentToken) {
return currentToken;
} else {
console.log(
'No registration token available. Request permission to generate one.'
);
}
})
.catch(err => {
console.error('An error occurred while retrieving token. ', err);
});
};
InitializeMessaging();
export const onMessageListener = () =>
new Promise(resolve => {
onMessage(getMessaging(), payload => {
resolve(payload);
});
});
And I have a store that upon message receival I change the mobx state (flag.js
):
import { configureStore } from '@reduxjs/toolkit'
import { onMessageListener } from './firebase';
const initialState = { flag: true, token: "" }
function flagReducer(state = initialState, action) {
// Check to see if the reducer cares about this action
if (action.type === 'flag/change') {
// If so, make a copy of `state`
return {
...state,
// and update the copy with the new value
flag:action.payload
}
}
if (action.type === 'flag/token') {
return {
...state,
// and update the copy with the new value
token:action.payload
}
}
// otherwise return the existing state unchanged
return state
}
const store = configureStore({ reducer: flagReducer });
export const setFlag = active => ({
type: 'flag/change',
payload: active==='true'
});
export const setToken = token => ({
type: 'flag/token',
payload: token
});
onMessageListener().then(payload => {
})
.catch(err => console.log('failed: ', err));
navigator.serviceWorker.addEventListener(
'message',
function(event) {
const message = event.data.data;
if(typeof message !== 'undefined' && typeof message['flag'] !== 'undefined' ) {
store.dispatch(setFlag(message.flag));
}
}
);
export default store;
My state has a single flag that is toggled from firebase message.
Also I have the following worker (firebase-messaging-sw.js
):
importScripts('https://www.gstatic.com/firebasejs/8.2.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/8.2.0/firebase-messaging.js');
// Initialize the Firebase app in the service worker by passing the generated config
const firebaseConfig = {
apiKey: `REPLACE_WITH_YOUR_FIREBASE_MESSAGING_API_KEY`,
authDomain: `REPLACE_WITH_YOUR_FIREBASE_MESSAGING_AUTH_DOMAIN`,
projectId: `REPLACE_WITH_YOUR_FIREBASE_MESSAGING_PROJECT_ID`,
storageBucket: `REPLACE_WITH_YOUR_FIREBASE_MESSAGING_STORAGE_BUCKET`,
messagingSenderId: `REPLACE_WITH_YOUR_FIREBASE_MESSAGING_SENDER_ID`,
appId: `REPLACE_WITH_YOUR_FIREBASE_MESSAGING_APP_ID`,
measurementId: `REPLACE_WITH_YOUR_FIREBASE_MESSAGING_MEASUREMENT_ID`,
};
firebase.initializeApp(firebaseConfig);
// Retrieve firebase messaging
const messaging = firebase.messaging();
// Handle incoming messages while the app is not in focus (i.e in the background, hidden behind other tabs, or completely closed).
messaging.onBackgroundMessage(function(payload) {
console.log('Received background message', payload);
// How I can send the received message upon mobx
});
What I want to achieve is when I receive a background message I want also the flag at my state to be modified as well. Is there a way?
What I want is to propagate the message from firebase-messaging-sw.js
into flag.js
so I can update the redux state as well.
My application uses the state for showing a logo based upon the state flag:
import { connect } from 'react-redux';
import logo from './logo.svg';
function Logo({shown,token}){
return (
<img src={logo} className="App-logo" style={{display:!shown?"none":""}} alt="logo" />
);
}
const mapStateToProps = state => {
console.log(state);
return {
shown: state.flag,
};
};
export default connect(
mapStateToProps,
)(Logo);
This is achievable by placing at firebase-messaging-sw.js
the following:
messaging.onBackgroundMessage(function(payload) {
console.log('Received background message', payload);
// Propagate message upon UI
if(typeof payload.data !== 'undefined' && typeof payload.data['flag'] !== 'undefined' ) {
self.clients.matchAll({includeUncontrolled: true}).then(function (clients) {
//you can see your main window client in this list.
clients.forEach(function(client) {
client.postMessage({...payload});
});
})
}
});
And at the flag.js
file by placing:
navigator.serviceWorker.addEventListener(
'message',
function(event) {
const message = event.data.data;
if(typeof message !== 'undefined' && typeof message['flag'] !== 'undefined' ) {
store.dispatch(setFlag(message.flag));
}
}
);
What is done at the code above is with:
self.clients.matchAll({includeUncontrolled: true}).then(function (clients) {
//you can see your main window client in this list.
clients.forEach(function(client) {
client.postMessage({...payload,'type':'firebaseBG'});
});
})
We propagate the message back to frontent via a special event that is handled via:
navigator.serviceWorker.addEventListener(
'message',
function(event) {
// Event handled here
}
);
The message payload is available at event via:
event.data.data;