I'm trying to implement Google's Firebase Cloud Messaging (FCM) into my Nuxt.js APP.
So far I've installed firebase, created a firebase.js plugin inside ./plugins folder, imported and initialized firebase and the messaging service, everything seems to be working fine.
Now I'm not sure how or where to go from here..
The idea is to handle everything inside vuex, in notifications module.
I want to handle both background and foreground notifications. Background gets handled by service-worker, for the foreground I've made a simple notification component that I want to show everytime I receive a push notification from FCM.
The question:
How would I go about registering a service worker, requesting permission and handling the foreground/background notifications? I mean the exact location/file/way specific to Nuxt.js? Should I make another plugin just for that, use middleware folder or just handle everything in my default layout file?
Whats the cleanest way to go about it?
Thanks in advance!
Step 1) Install dependencies
npm install firebase
npm install #nuxtjs/firebase
Step 2) Create a file serviceWorker.js on your project's root folder.
self.addEventListener('push', function (e) {
data = e.data.json();
var options = {
body: data.notification.body,
icon: data.notification.icon,
vibrate: [100, 50, 100],
data: {
dateOfArrival: Date.now(),
primaryKey: '2'
},
};
Step 3) Config your nuxt.config.js as follows.
Add this line to the top of your file.
const fs = require('fs')
Update your modules array with firebase credentials.
[
'#nuxtjs/firebase',
{
config: {
apiKey: "<yourKey>",
authDomain: "<yourAuthDomain>",
projectId: "<yourProjectId>",
storageBucket: "<yourStorageBucket>",
messagingSenderId: "<yourMessagingSenderId>",
appId: "<yourAppId>",
measurementId: ",<yourMeasurementId>"
},
onFirebaseHosting: false,
services: {
messaging: {
createServiceWorker: true,
fcmPublicVapidKey: '<yourFCMPublicVapidKey>',
inject: fs.readFileSync('./serviceWorker.js')
}
}
}
]
Step 4 > Finally.. to your index.js or layout file.
async mounted() {
const currentToken = await this.$fire.messaging.getToken()
const data = JSON.stringify({
notification: {
title: 'firebase',
body: 'firebase is awesome',
click_action: 'http://localhost:3000/',
icon: 'http://localhost:3000/assets/images/brand-logo.png'
},
to: currentToken
})
const config = {
method: 'post',
url: 'https://fcm.googleapis.com/fcm/send',
headers: {
'Content-Type': 'application/json',
'Authorization': 'key=<yourServerKey>'
},
data
};
const response = await axios(config)
this.$fire.messaging.onMessage((payload) => {
console.info('Message received: ', payload)
})
this.$fire.messaging.onTokenRefresh(async () => {
const refreshToken = await this.$fire.messaging.getToken()
console.log('Token Refreshed',refreshToken)
})
}
For more details, to understand the steps, you can visit this article.
you can check the Nuxt Module Nuxt Firebase https://firebase.nuxtjs.org
the documentation is very good https://firebase.nuxtjs.org/service-options/messaging
Once you have installed #nuxtjs/firebase module and insert following code into your nuxt.config.js where you can get those data from firebase console. I placed them as a dotenv module because it is easier to manage configuration templates for different projects.
firebase: {
config: {
apiKey: dotenv.parsed.apiKey,
authDomain: dotenv.parsed.authDomain,
databaseURL: dotenv.parsed.databaseURL,
projectId: dotenv.parsed.projectId,
storageBucket: dotenv.parsed.storageBucket,
messagingSenderId: dotenv.parsed.messagingSenderId,
appId: dotenv.parsed.appId,
measurementId: dotenv.parsed.measurementId
},
onFirebaseHosting: false,
services: {
messaging: {
createServiceWorker: true,
fcmPublicVapidKey: dotenv.parsed.vapidKey // OPTIONAL : Sets vapid key for FCM after initialization
}
Once you have implemented it, the module will generate service workers by itself and you can view them on your inspect element console.
All done and said,
In your vuex store, just call this.$fire.messaging.getToken() to ask users for permission to receive notification.
You can initiate your receiving message with this function below where you call this.$fire.messaging.getToken()
messaging.onMessage(function (payload) {
context.dispatch('yourDesireDispatchFunction', payload)
})
Related
Here is my whole code:
import { initializeApp } from "https://www.gstatic.com/firebasejs/9.16.0/firebase-app.js";
import { getMessaging, getToken } from "https://www.gstatic.com/firebasejs/9.16.0/firebase-messaging.js";
//This data is filled correctly just clearing it here for the question
const firebaseConfig = {
apiKey: "",
authDomain: "",
projectId: "t",
storageBucket: "",
messagingSenderId: "",
appId: "",
measurementId: ""
};
// Initialize Firebase
const bbFirebase = initializeApp(firebaseConfig);
const messaging = getMessaging();
// Add the public key generated from the console here.
getToken(messaging, { vapidKey: 'I_HAVE_PLACED_VALID_KEY_HERE' }).then((currentToken) => {
if (currentToken) {
console.log("TOKEN: " + currentToken);
} else {
// Show permission request UI
console.log('No registration token available. Request permission to generate one.');
// ...
}
}).catch((err) => {
console.log('An error occurred while retrieving token. ', err);
// ...
});
messaging.onBackgroundMessage((payload) => {
console.log('[firebase-messaging-sw.js] Received background message ', payload);
// Customize notification here
const notificationTitle = payload.notification.title;
const notificationOptions = {
body: payload.notification.body,
icon: '/firebase-logo.png'
};
self.registration.showNotification(notificationTitle,
notificationOptions);
});
function requestPermission() {
console.log('Requesting permission...');
Notification.requestPermission().then((permission) => {
if (permission === 'granted') {
console.log('Notification permission granted.');
// TODO(developer): Retrieve a registration token for use with FCM.
// In many cases once an app has been granted notification permission,
// it should update its UI reflecting this.
resetUI();
} else {
console.log('Unable to get permission to notify.');
}
});
}
So when I execute this code I can generate a token. I see the token clearly and all good there. However I have this error:
Uncaught TypeError: messaging.onBackgroundMessage is not a function
at firebase.js:31:11
Any idea why I can this error and how can I at least console.log() print the incoming notifications?
The onBackgroundMessage() is a top level function just like getToken() in the new functional syntax imported from firebase/messaging/sw as mentioned in the documentation.
import { getMessaging, onMessage } from 'firebase/messaging'
import { onBackgroundMessage } from 'firebase/messaging/sw'
onBackgroundMessage(messaging, (payload) => {
// ...
})
I´m having trouble with this too but if you paste this code in firebase-messaging-sw.js and that file is in the root of firebase hosting it works.
// Import and configure the Firebase SDK
// These scripts are made available when the app is served or deployed on Firebase Hosting
// If you do not serve/host your project using Firebase Hosting see https://firebase.google.com/docs/web/setup
importScripts('/__/firebase/9.2.0/firebase-app-compat.js');
importScripts('/__/firebase/9.2.0/firebase-messaging-compat.js');
importScripts('/__/firebase/init.js');
const messaging = firebase.messaging();
/**
* Here is is the code snippet to initialize Firebase Messaging in the Service
* Worker when your app is not hosted on Firebase Hosting.
// Give the service worker access to Firebase Messaging.
// Note that you can only use Firebase Messaging here. Other Firebase libraries
// are not available in the service worker.
importScripts('https://www.gstatic.com/firebasejs/9.2.0/firebase-app-compat.js');
importScripts('https://www.gstatic.com/firebasejs/9.2.0/firebase-messaging-compat.js');
// Initialize the Firebase app in the service worker by passing in
// your app's Firebase config object.
// https://firebase.google.com/docs/web/setup#config-object
firebase.initializeApp({
apiKey: 'api-key',
authDomain: 'project-id.firebaseapp.com',
databaseURL: 'https://project-id.firebaseio.com',
projectId: 'project-id',
storageBucket: 'project-id.appspot.com',
messagingSenderId: 'sender-id',
appId: 'app-id',
measurementId: 'G-measurement-id',
});
// Retrieve an instance of Firebase Messaging so that it can handle background
// messages.
const messaging = firebase.messaging();
**/
// If you would like to customize notifications that are received in the
// background (Web app is closed or not in browser focus) then you should
// implement this optional method.
// Keep in mind that FCM will still show notification messages automatically
// and you should use data messages for custom notifications.
// For more info see:
// https://firebase.google.com/docs/cloud-messaging/concept-options
messaging.onBackgroundMessage(function(payload) {
// console.log('[firebase-messaging-sw.js] Received background message ', payload);
console.log('[firebase-messaging-sw.js] PAYLOAD NOTIFICATION: ', payload.notification);
// Customize notification here
const notificationTitle = payload.notification.title
const notificationOptions = {
body: payload.notification.body,
icon: payload.notification.image
};
self.registration.showNotification(notificationTitle,
notificationOptions);
});
I am writing cypress e2e tests for my nextjs web app, which uses firebase on the back end. I have followed the guide in the docs for setting it up using the cypress-firebase package (https://www.npmjs.com/package/cypress-firebase), but I am getting an error relating to webpack:
Webpack Compilation Error
./node_modules/cypress-firebase/lib-esm/plugin.js 18:27
Module parse failed: Unexpected token (18:27)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
| export default function pluginWithTasks(cypressOnFunc, cypressConfig, adminInstance, overrideConfig) {
| // Only initialize admin instance if it hasn't already been initialized
> if (adminInstance.apps?.length === 0) {
| initializeFirebase(adminInstance, overrideConfig);
| }
Here is the contents of my cypress/support/commands.js file (I am using firebase version 9.15.0, hence the imports from firebase/compat/*):
import firebase from 'firebase/compat/app';
import 'firebase/compat/auth';
import 'firebase/compat/database';
import 'firebase/compat/firestore';
import { attachCustomCommands } from 'cypress-firebase';
const fbConfig = {
apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
authDomain: process.env.NEXT_PUBLIC_AUTH_DOMAIN,
projectId: process.env.NEXT_PUBLIC_PROJECT_ID,
storageBucket: process.env.NEXT_PUBLIC_STORAGE_BUCKET,
messagingSenderId: process.env.NEXT_PUBLIC_MESSAGING_SENDER_ID,
appId: process.env.NEXT_PUBLIC_APP_ID
};
firebase.initializeApp(fbConfig);
attachCustomCommands({ Cypress, cy, firebase });
Here is the contents of my cypress.config.js file:
const { defineConfig } = require('cypress');
const cypressFirebasePlugin = require('cypress-firebase').plugin;
const admin = require('firebase-admin');
module.exports = defineConfig({
e2e: {
baseUrl: 'http://localhost:3000',
chromeWebSecurity: false,
setupNodeEvents(on, config) {
return cypressFirebasePlugin(on, config, admin, { projectId: process.env.NEXT_PUBLIC_PROJECT_ID });
},
},
});
I have a CYPRESS_TEST_UID in my .env file and have downloaded the serviceAccount.json file required by firebase-admin. I am not sure what is causing this error; any help would be much appreciated.
I also have the same problem just in 3.0.1; I am not sure if it is a plugin bug.
I have a sitemap created with nuxt and firebase but when I deploy the site to firebase the sitemap throws and error since I am referencing my fireInit file but my nuxt.config.file has been moved to my firebase functions folder so I can deploy it. When I try to add the fireInit file in my firebase functions folder I then get an error because I am importing outside a module. Also when I take out my dynamic routes by commenting out my async routes function the site map doesn't show my custom domain url. It shows the https://us-central1-PROJECT_NAME.cloudfunctions.net.
File Structure
functions/
- nuxt.config (prod)
- index.js
public/
- _nuxt/ (client)
src/
- nuxt.config (dev)
firebase.json
Nuxt Config
module.exports = {
modules: ['#nuxtjs/sitemap'],
sitemap: {
hostname: process.env.BASE_URL,
exclude: ['/listings/create', '/listings/edit', '/profile/**'],
async routes() {
const { db } = require('./plugins/fireInit');
const snapshots = await db.collection('listings').get();
return snapshots.docs.map((doc) => `/listings/${doc.id}`);
},
},
}
./plugins/fireInit
import firebase from 'firebase/app';
import 'firebase/firestore';
const firebaseConfig = {
apiKey: 'KEY',
authDomain: 'KEY',
projectId: 'KEY',
appId: 'KEY',
measurementId: 'KEY',
};
// init firebase
!firebase.apps.length ? firebase.initializeApp(firebaseConfig) : '';
// init services
export const db = firebase.firestore();
You can't use the firebase web in functions and import it outside a module.
You have to read Firestore via firebase-admin on your index.js
const functions = require('firebase-functions')
const admin = require('firebase-admin')
admin.initializeApp()
And in your sitemap config:
sitemap: {
hostname: 'YOUR HOST',
exclude: ['/listings/create', '/listings/edit', '/profile/**'],
async routes() {
const snapshot = await admin.firestore().collection('listings').get()
return snapshot.docs.map((doc) => `/listings/${doc.id}`);
},
},
In a Firebase Cloud Function running Express, I am attempting to set custom user claims when a client posts a token to a setCustomClaims route. When I call admin.auth().setCustomUserClaims(uid, {admin: true}) within that route, I get an error saying this is "not a function."
My authentication provider is the email/password provider via Firebase authentication (i.e. I am not creating custom tokens).
Do I have to be creating custom tokens to set custom user claims?
Here is my cloud function code:
const functions = require('firebase-functions');
const admin = require("firebase-admin");
import express from "express"
admin.initializeApp(functions.config().firebase);
const app = express()
app.post('/setCustomClaims', (req, res) => {
uid = "some-uid"
admin.auth().setCustomUserClaims(uid, {admin:true}).then(()=> {
res.end(JSON.stringify( { status: 'success' } ) );
})
});
export let api = functions.https.onRequest((request, response) => {
if (!request.path) {
request.url = `/${request.url}` // prepend '/' to keep query params if any
}
return app(request, response)
})
npm install firebase-admin#latest --save
firebase-admin#5.4.3 work, good luck for fun app.
Note: client needs this code
// Force token refresh. The token claims will contain the additional claims.
firebase.auth().currentUser.getIdToken(true);
In the new SDKs, you no longer instantiate a database references via new Firebase. Instead, you will initialize the SDK via firebase.initializeApp():
BEFORE
var ref = new Firebase("https://databaseName.firebaseio.com");
AFTER
// See https://firebase.google.com/docs/web/setup#project_setup for how to
// auto-generate this config
var config = {
apiKey: "apiKey",
authDomain: "projectId.firebaseapp.com",
databaseURL: "https://databaseName.firebaseio.com"
};
firebase.initializeApp(config);
var rootRef = firebase.database().ref();>
I have found same issue on the stackoverflow, check this: firebase.database is not a function
I am trying to implement Firebase Cloud Messaging (FCM) for push notifications into my web application (AngularJS). For this I have created one firebase-messaging-sw.js in the root app folder.
// firebase-messaging-sw.js
'use strict';
console.log('Starting service worker');
if ('function' === typeof importScripts) {
importScripts('https://www.gstatic.com/firebasejs/3.5.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/3.5.0/firebase-messaging.js');
// importScripts('core/decoder.js');
// Initialize the Firebase app in the service worker by passing in the
// messagingSenderId.
firebase.initializeApp({
'messagingSenderId': '1043000236721'
});
// Retrieve an instance of Firebase Messaging so that it can handle background
// messages.
var messaging = firebase.messaging();
messaging.setBackgroundMessageHandler(function (payload) {
var shinyData = payload || {};
var title = 'New Notification';
console.log('[firebase-messaging-sw.js] Received background message ', payload, shinyData);
return self.registration.showNotification(title, {
body: shinyData.body,
icon: '/resources/images/web-logo.png',
data: { url: '/#!/home' }
});
});
self.addEventListener("notificationclick", function (event) {
var urlToRedirect = event.notification.data.url;
event.notification.close();
event.waitUntil(self.clients.openWindow(urlToRedirect));
});
}
and one manifest.json file containing the gcm-sender-id as follows:
{
"manifest_version": 2,
"name": "My Extension",
"version": "versionString",
"default_locale": "en",
"description": "A plain text description",
"gcm_sender_id": "103953800517"
}
In the index.html I have initialized Firebase as follows:
<script>
var config = {
apiKey: "API-key",
authDomain: "some-authdomain",
databaseURL: "firebase-databaseUrl",
storageBucket: "storage-bucketUrl",
messagingSenderId: "senderId"
};
firebase.initializeApp(config);
</script>
With all these configuration the FCM push notifications are working fine. But the problem is coming when we logout. At the time of logout I am unregistering the service worker as follows:
navigator.serviceWorker.getRegistrations().then(function (registrations) {
angular.forEach(registrations, function (registration) {
registration.unregister();
});
});
Now, when I try to login again then push notification doesn't works as
after unregistering all the service worker , I am unable to
reinitialise the firebase app. If I reload the page after logout then
the FCM works perfectly fine as on reload firebase-messaging-sw.js is
loaded again and setup the service worker again.
Please help me to provide the solution for proper logout and login for enabling smooth functioning of FCM.
I haven't really tried this solution but I believe this should work.
Write this code in a function as:
async function init() {
const registration = await navigator.serviceWorker.register('/firebase-messaging-sw.js');
await navigator.serviceWorker.ready;
var config = {
apiKey: "API-key",
authDomain: "some-authdomain",
databaseURL: "firebase-databaseUrl",
storageBucket: "storage-bucketUrl",
messagingSenderId: "senderId"
};
firebase.initializeApp(config);
// getToken and all stuffs
}
And call the function init() as soon as the user login.
Also, whenever the user logout, you should unregister the service worker with
navigator.serviceWorker.getRegistrations().then(function (registrations) {
angular.forEach(registrations, function (registration) {
registration.unregister();
});
});
Hope this works for you.