How to implement Firebase(FCM) Push Notifications on nuxtjs / vuejs - javascript

Having devoted many hours to search the internet for a simple and straightforward way of how to implement Firebase FCM Push Notification on my nuxt project bore no fruit.

Here is how to implement FCM Push Notifications on your NuxtJs/Vuejs project
Step 1
Create your nuxt app like npx create-nuxt-app <your-app-name>
Step 2
Install firebase npm install firebase and #nuxt/firebase npm install #nuxt/firebase
Step 3
Creating your firebase project
Go to firebase console and create a project
Give it a name
Enable Google analytics if you like to then click on create
-Get some coffee as the projects creates ☕
On this page you want to copy the config, which we will use later
Finally, we land on the home page of our project on firebase, looks like below image
Let's go back to our project
Step 4
On your nuxt.config.js add
// Modules: https://go.nuxtjs.dev/config-modules
modules: [
'#nuxtjs/firebase',
],
// firebase FCM starts here
firebase: {
lazy: false,
config: {
apiKey: <apiKey>,
authDomain: <authDomain>,
projected: <projectId>,
storageBucket: <storageBucket>,
messagingSenderId: <messagingSenderId>,
appId: <appId>,
measurementId: <measurementId>,
databaseURL: <databaseURL>,
},
onFirebaseHosting: false,
services: {
messaging: true,
}
},
messaging: {
createServiceWorker: true,
actions: [
{
action: 'goHome',
url: 'https://localhost:3000'
}
],
fcmPublicVapidKey: <vapidKey>
},
To get your vapidKey navigate to Project Settings on your firebase console and select Cloud Messaging, scroll down and press on Generate Key Pair to have your vapidKey.See image below
copy and paste it on your nuxt.config.js
Step 5
On the static folder at your project root create a file named firebase-messaging-sw.js and paste the input the configs as below
importScripts('https://www.gstatic.com/firebasejs/8.2.7/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/8.2.7/firebase-messaging.js');
// Initialize the Firebase app in the service worker by passing the generated config
var firebaseConfig = {
apiKey: <apiKey>,
authDomain: <authDomain>,
projected: <projectId>,
storageBucket: <storageBucket>,
messagingSenderId: <messagingSenderId>,
appId: <appId>,
measurementId: <measurementId>,
databaseURL: <databaseURL>,
};
firebase.initializeApp(firebaseConfig);
// Retrieve firebase messaging
const messaging = firebase.messaging();
messaging.onBackgroundMessage(function (payload) {
console.log('Received background message ', payload);
const notificationTitle = payload.notification.title;
const notificationOptions = {
body: payload.notification.body
};
self.registration.showNotification(notificationTitle,
notificationOptions);
});
Step 6
On your index.vue configure it as follows
<template>
<div>
<h1>Get Notified</h1>
</div>
</template>
<script>
export default {
data() {
return {
listenersStarted: false,
idToken: "",
};
},
mounted() {
this.startListeners();
},
methods: {
// FCM NOTIFICATION FUNCTIONS
async startListeners() {
await this.startOnMessageListener();
await this.startTokenRefreshListener();
await this.requestPermission();
await this.getIdToken();
this.listenersStarted = true;
},
startOnMessageListener() {
try {
this.$fire.messaging.onMessage((payload) => {
console.info("Message received : ", payload);
console.log(payload.notification.body);
});
} catch (e) {
console.error("Error : ", e);
}
},
startTokenRefreshListener() {
try {
this.$fire.messaging.onTokenRefresh(async () => {
try {
await this.$fire.messaging.getToken();
} catch (e) {
console.error("Error : ", e);
}
});
} catch (e) {
console.error("Error : ", e);
}
},
async requestPermission() {
try {
const permission = await Notification.requestPermission();
console.log("GIVEN notify perms");
console.log(permission);
} catch (e) {
console.error("Error : ", e);
}
},
async getIdToken() {
try {
this.idToken = await this.$fire.messaging.getToken();
console.log("TOKEN ID FOR this browser");
console.log(this.idToken);
} catch (e) {
console.error("Error : ", e);
}
},
},
};
</script>
Step 7
Run npm run dev , open your console to see if the permissions to display notifications are granted. Accept if promoted to allow notifications.
Step 8
Navigate to Cloud Messaging on firebase Engage menu and click on Send your first message. Type the content you would like your notification to have and select your app as the target user, there we have it you should see a notification on your browser like so

Related

firebase is not being recognized in Unity webGL Build

I have set up a test project in order to test Firebase with unity webGL builds.
I have created a *.jslib plugin to keep my js functions in there :
mergeInto(LibraryManager.library, {
GetJSON: function (path, objectName, callback, fallback) {
var parsedPath = Pointer_stringify(path);
var parsedObjectName= Pointer_stringify(objectName);
var parsedCallback = Pointer_stringify(callback);
var parsedFallback = Pointer_stringify(fallback);
try{
firebase.database().ref(parsedPath).once('value').then(function(snapshot) {
window.unityInstance.SendMessage(parsedObjectName, parsedCallback , JSON.stringify(snapshot.val()));
});
} catch(error){
window.unityInstance.SendMessage(parsedObjectName, parsedFallback, "There was an error: " + error.message);
}
}
});
After building and running, I add my Firebase config snippet into the index.html file(I have hidden the actual keys on this snippet):
<script type="module">
// Import the functions you need from the SDKs you need
import { initializeApp } from "https://www.gstatic.com/firebasejs/9.8.1/firebase-app.js";
import { getAnalytics } from "https://www.gstatic.com/firebasejs/9.8.1/firebase-analytics.js";
// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries
// Your web app's Firebase configuration
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
const firebaseConfig = {
apiKey: "key",
authDomain: "domain",
projectId: "id",
storageBucket: "bucket",
messagingSenderId: "senderID",
appId: "appId",
measurementId: "measurementId"
};
// Initialize Firebase
const app = initializeApp(firebaseConfig);
const analytics = getAnalytics(app);
</script>
In unity, I am testing this by calling the GetJSON function, and I have a callback, and a fallback method:
void Start()
{
text.text = "Start worked";
GetJSON(path: "example", gameObject.name, callback: "OnRequestSuccess", fallback: "OnRequestFailed");
}
public void OnRequestSuccess(string data)
{
text.color = Color.green;
text.text = data;
}
public void OnRequestFailed(string error)
{
text.color = Color.red;
text.text = error;
}
After I have built and run my webGL project, and updated the index.html file with the proper configurations, the text turns red (Which means the OnRequestFailed() was called) and it says: "There was an error: firebase is not defined". Which means, it does not recognize the firebase when I use it on the *.jslib plugin. Where should the firebase be defined exactly? Because I am defining it in my index.html, or at least I think so?

Firebase user is authenticated, so why is uploading to storage denied?

In a react-native project, I'm using both react-native-firebase and the firebase sdk.
react-native-firebase does not allow use of firebase storage to upload image blobs, which is why im using the vanilla Firebase javascript SDK to do this part. For distinction's sake, in my code & this post, im identifying the firebase javascript sdk as 'FIREBASE', and the react-native-firebase as 'firebase'.
I had to initialize my firebase app (even though react-native-firebase doesn't require this for its function, firebase does), App.js constructor & imports:
import * as React from 'react';
import AppNavigation from './src/navigation';
import { Provider } from 'react-redux';
import { store, persistor } from './src/store/index.js';
import firebase from 'firebase/app';
import { PersistGate } from 'redux-persist/integration/react';
export default class App extends React.Component {
constructor (props) {
super(props);
const firebaseConfig = {
apiKey: '{apiKey}',
authDomain: 'project-ID.firebaseapp.com',
databaseURL: 'https://project-ID.firebaseio.com',
projectId: 'project-ID',
storageBucket: 'project-ID.appspot.com',
messagingSenderId: '9999999999'
};
if (!firebase.apps.length) {
firebase.initializeApp(firebaseConfig);
}
}
I implement firebase and FIREBASE in an action (firebase for auth/firestore, and FIREBASE for storage):
import * as types from '../actions/types';
import RNFetchBlob from 'rn-fetch-blob';
import firebase from 'react-native-firebase';
import * as FIREBASE from 'firebase/app';
import 'firebase/storage';
import { Platform } from 'react-native';
const Blob = RNFetchBlob.polyfill.Blob;
const fs = RNFetchBlob.fs;
export const registerUser = (registration) => {
const { email, pass, username, img } = registration;
return (dispatch) => {
dispatch({ type: types.REGISTER_USER });
console.log('starting registration process...');
// check username is unique
firebase
.firestore()
.collection('users')
.where('username', '==', username)
.get()
.then((querySnapshot) => {
if (querySnapshot.empty !== true) {
// back to registration form
registrationFail(dispatch, 'Username already taken. Try again.');
console.log("Registrant's username already exists");
} else {
console.log('Registrants username is unique');
// continue with registration
firebase
.auth()
.createUserWithEmailAndPassword(email, pass)
.then((userCredential) => {
// successful user creation, now authenticated
// write to img storage
uploadImg(dispatch, img, userCredential.user.uid)
.then((imgUrl) => {
// on success, write to firestore
uploadImgSuccess(dispatch, 'Profile image upload successful...');
// write rest of data to firestore
firebase
.firestore()
.collection('users')
.add({
createdAt: firebase.firestore.FieldValue.serverTimestamp(),
username: email,
uid: userCredential.user.uid,
profileImg: imgUrl,
email: email,
})
.catch((err) => {
console.log('Registration failed. Error: ' + err.message);
registrationFail(dispatch, err.message);
});
}
})
.catch((err) => {
// Image Profile NOT Uploaded
uploadImgFail(dispatch, err);
});
})
.catch((err) => {
// unsuccessful user creeation
registrationFail(dispatch, err.message);
});
}
})
.catch((err) => registrationFail(dispatch, err.message));
};
};
const uploadImg = async (dispatch, uri, uid, mime = 'image/png') => {
console.log('Starting image upload...');
dispatch({ type: types.UPLOAD_IMG, info: 'Uploading profile image...' });
const uploadUri = Platform.OS === 'ios' ? uri.replace('file://', '') : uri;
let uploadBlob = null;
// let downloadPath = '';
const imageRef = FIREBASE.storage().ref(uid).child('profileImg');
fs
.readFile(uploadUri, 'base64')
.then((data) => {
return Blob.build(data, { type: `${mime};BASE64` });
})
.then((blob) => {
uploadBlob = blob;
return imageRef.put(blob, { contentType: mime });
})
.then(() => {
uploadBlob.close();
return imageRef.getDownloadURL();
})
.then((url) => {
console.log('Returning Download URL: ' + url);
uploadImgSuccess(dispatch, 'Image upload successful...');
})
.catch((err) => {
uploadImgFail(dispatch, 'Image upload failed: ' + JSON.stringify(err));
});
};
but when I go through uploadImg(), I get an error:
{
"code_": "storage/unauthorized",
"message":"Firebase Storage: User does not have permission to access 'someReference/someChild',
"serverResponse":{"Code":403, "message": "permission denied."}
}
Here is Firestore rule:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if request.auth != null;
}
}
}
Here is Storage rule:
rules_version = '2';
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write: if request.auth != null;
}
}
}
I don't understand what's going on or why. The user is authenticated during react-native-firebase's createUserWithEmailAndPassword() and is even allowed to upload data to Firestore. My only guess is this possibly has something to do either with using both firebase & FIREBASE, or with the way i've setup firebase with FIREBASE. I've used both together in a previous test project and it worked successfully with the help of a forked project rn-fetch-blob (the maintained version of react-native-fetch-blob), however I had no security rules in place bc of testing, so...
Any ideas for solving this?
Yes, you have guessed it right, your FIREBASE instance is unaware of the auth being done by the firebase since the firebase handles the native aspects and FIREBASE is just a JS thing. so both of the instances have their own lives and their own prospects with own attributes to identify the user and provide authorization.
To resolve this, try authorizing the user either by the vanilla JS SDK or use the rn-firebase for the whole task.
I would recommend using react-native-firebase, it has good support for entire firebase stack.
https://rnfirebase.io/docs/v5.x.x/storage/reference/storage
EDIT:
Vanilla JS firebase SDK should not be used in mobile applications especially when it is native since firebase will treat the client as web client and going forward you will not be able to leverage all possible mobile things from firebase such as dynamic links, push notifications and analytics.
Hope this helps! Cheers!

Web firebase.messaging().onMessage not fired, but background notification perfectly fired

I want to reload or trigger some event in foregrounf if push message is sent with firebase.messaging().onMessage, but it not fired. I'm using firebase.mesaging.sw.js with background notification and it works correctly what is wrong with my code?
firebase.js
const config = {
apiKey: "x",
projectId: "x",
storageBucket: "x",
messagingSenderId: "x"
};
firebase.initializeApp(config);
const msg = firebase.messaging()
msg.requestPermission()
.then(() => {
return msg.getToken()
})
.then((token) => {
})
.catch((err) => {
})
msg.onMessage(function(payload) {
alert("Foreground message fired!")
console.log(payload)
});
firebase.messaging.sw.js
importScripts("https://www.gstatic.com/firebasejs/7.0.0/firebase-app.js");
importScripts("https://www.gstatic.com/firebasejs/7.0.0/firebase-messaging.js");
const config = {
apiKey: "x",
projectId: "x",
storageBucket: 'x',
messagingSenderId: "x"
};
firebase.initializeApp(config);
const msg = firebase.messaging()
msg.setBackgroundMessageHandler(function(payload) {
let options = {
body: payload.data.body,
icon: payload.data.icon
}
return self.registration.showNotification(payload.data.title, options);
});
I don't know what is wrong with my code
Simple solution to this is update your Firebse to latest version.
Eg.
importScripts('https://www.gstatic.com/firebasejs/7.8.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/7.8.0/firebase-messaging.js');
Note: Once you have updated your firebase libraries versions then messagingSenderId will not work in your firebase-messaging-sw.js file. You have to provide all other params eg. apiKey, projectId, appId along with messagingSenderId.
If still not work. Clean your browser cache and re-register service worker.
For more details you can refer to this solution
Still had the same issue in 2020. In my case it was like this:
you need to have same versions in importScripts for background messages and in your app for foreground messages
call it after obtaining token for background service
firebaseApp.messaging().getToken().then((currentToken) => {
if (currentToken) {
console.log(currentToken)
} else {
// Show permission request.
console.log(
'No Instance ID token available. Request permission to generate one.')
}
/** When app is active */
firebase.messaging().onMessage((payload) => {
console.log(payload)
}, e => {
console.log(e)
})
})
For anyone else with this problem, I finally solved it by:
Upgrading the Firebase SDK version in both header-included JS files and the SW JS file to latest (currently, that would be 7.8.1).
Adding the entire firebaseConfig array to the SW firebase.initializeApp(), as the previous answer suggests.
Cleaning the Chrome cache from the Application > Clear Storage section in the Developer Tools.
Deleting the previous registration token from my database.
Blocking and unblocking notifications from the browser to force a new token generation.
Basically, a total fresh start with updated Firebase SDK seems to fix issues like this.
You are missing lots of things and onMessage will only work if firebase is initialized before calling it. Please follow this. I have done it like this and it is working.
initialize firebase and get the token
export class BrowserFcmProvider {
export const FIREBASE_CONFIG = {
apiKey: "****",
authDomain: "****",
databaseURL: "****",
projectId: "****",
storageBucket: "****",
messagingSenderId: "****",
appId: "****"
}
firebase.initializeApp(FIREBASE_CONFIG);
async webGetToken() {
try {
const messaging = firebase.messaging();
await messaging.requestPermission();
const token = await messaging.getToken();
let uuidTemp = new DeviceUUID().get();
return this.saveTokenToFireStoreFromWeb(token, uuidTemp)
} catch (e) {
console.log(e);
}
}
saveTokenToFireStoreFromWeb(token, uuid) {
try {
const docData = {
token: token,
device_type: 'web',
uuid: uuid
}
const devicesRef = this.db.collection('devices')
return devicesRef.doc(uuid).set(docData);
} catch (e) {
console.log(e, 'saveTokenError');
}
}
showMessage() {
try {
const messaging = firebase.messaging();
messaging.onMessage((payload) => {
console.log(payload);
})
} catch (e) {
console.log(e)
}
}
}
And calling the method while app loads like this
async configureFirebaseForBrowser(res) {
await this.bfcm.webGetToken();
this.bfcm.showMessage();
}
Firebase function and payload type
const payloadWeb = {
title: title,
body: body,
data: {
title: title,
body: body
},
tokens: uniqueDevicesTokenArrayWeb,
}
const responseWeb = await admin.messaging().sendMulticast(payloadWeb);
console.log(responseWeb.successCount + ' notifications has been sent to Web successfully');
I have used async and await as we need to manage firebase/firestore operations asynchronously.
fcm does not work in Incognito mode and safari browser
Same issue i was faced. In my case firebase version in "package.json" and "firebase-messaging-sw.js" importScripts version was different. After set same version in "firebase-messaging-sw.js" importScripts which was in
"package.json", my issue is resolved.
Before change
**"package.json"**
"firebase": "^8.2.1",
**"firebase-messaging-sw.js"**
importScripts('https://www.gstatic.com/firebasejs/7.8.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/7.8.0/firebase-messaging.js');
After change
**"package.json"**
"firebase": "^8.2.1",
**"firebase-messaging-sw.js"**
importScripts('https://www.gstatic.com/firebasejs/8.2.1/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/8.2.1/firebase-messaging.js');

Why can't I set persistence even though login functions work with Firebase?

I have a component with a form that holds the email and password. Imports at the top:
import Component from '#ember/component';
import { inject as service } from '#ember/service';
import { computed } from '#ember/object';
import * as firebase from 'firebase';
Next, I inject the service for the firebaseApp and session (session just has some attrs that I set to tell the app we're authenticated -- these will be refactored later):
session: service(),
firebaseApp: service(),
If I pass these values into the signInWithEmailAndPassword() function from fbApp.auth(), the account is validated and logged in. Works as expected:
login() {
const session = this.get('session');
const fbApp = this.get('firebaseApp');
const e = this.get('form.email');
const p = this.get('form.password');
fbApp.auth().signInWithEmailAndPassword(e, p).then(u => {
session.set('user.email', u.email);
session.set('user.signedIn', true);
this.get('goHome')(); // route transition to home
}).catch(e => {
this.set('error', 'Something went wrong. Try again?');
});
}
Next, I wanted to persist the session so that the user is not logged out on refresh, only if the tab is closed. So I modified the code and wrapped the signInWithEmailAndPassword in with setPersistence as follows:
login() {
const session = this.get('session');
const fbApp = this.get('firebaseApp');
const e = this.get('form.email');
const p = this.get('form.password');
fbApp.auth().setPersistence(firebase.auth.Auth.Persistence.SESSION)
.then(()=> {
fbApp.auth().signInWithEmailAndPassword(e, p).then(u => {
session.set('user.email', u.email);
session.set('user.signedIn', true);
this.get('goHome')(); // route transition to home
}).catch(e => {
this.set('error', 'Something went wrong. Try again?');
});
}).catch(e => {
this.set('error', 'Something went wrong. Try again?');
});
}
This doesn't work. I get a Cannot read property 'SESSION' of undefined error in the console. I'm sure it has to do with how firebase is imported but I'm having trouble with it.
This question is very similar to the one I answered here on how to import Material Components Web JavaScript file.
Luckily, I been using Firebase and Firebase Auth in my Ambitious EmberJS project for about a year now. The process is dead simple, complete working example below.
In package.json
....
"devDependencies": {
"firebase": "^5.5.8",
....
In ember-cli-build.js
....
let app = new EmberApp(defaults, {
....
fingerprint: {
enabled: true,
generateAssetMap: true,
exclude: [
'firebase-app',
'firebase-auth',
'firebase-messaging',
'firebase-messaging-sw',
....
]
},
....
});
....
app.import('node_modules/firebase/firebase-app.js', { outputFile: 'firebase-app.js'});
app.import('node_modules/firebase/firebase-auth.js', { outputFile: 'firebase-auth.js'});
app.import('node_modules/firebase/firebase-messaging.js', { outputFile: 'firebase-messaging.js'});
....
In index.html before vendor.js and app.js files
....
<script src="/firebase-app.js"></script>
<script src="/firebase-auth.js"></script>
<script src="/firebase-messaging.js"></script>
<script>
// Initialize Firebase
var config = {
apiKey: "ABC123XYZ",
authDomain: "auth.my-app.com", // Did you know you can customize this or use default my-app.firebaseapp.com
databaseURL: "https://my-app.firebaseio.com",
projectId: "my-app",
storageBucket: "app.appspot.com",
messagingSenderId: "123456789"
};
if( typeof firebase !== 'undefined' && firebase) {
firebase.initializeApp(config);
}
</script>
....
In .eslintrc.js
....
module.exports = {
....
env: {
browser: true
},
globals: {
"firebase": false
},
....
};
....
Then anywhere but not in Fastboot (if using it) just called firebase methods and properties.e.g
....
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
user.getIdTokenResult()
.then((idTokenResult) => {
session.setToken(idTokenResult.token);
if (idTokenResult.claims.admin) {
session.setIsAdmin(true);
} else {
session.setIsAdmin(false);
}
session.setUser(user);
session.setIsAuthenticated(true);
})
.catch((error) => {
console.log(error);
});
} else {
// User is signed out.
// ...
}
});
....
Enjoy!

Firebase cloud message/notifications are not receiving. JS

I am trying to send FCM to my react app using cloud functions.
Cloud function is executing, but notification is not being received on client side.
Here cloud function code.
exports.sendPush = functions.database.ref('/settings2').onWrite(event => {
let projectStateChanged = false;
let projectCreated = false;
let projectData = event.data.val();
if (!event.data.previous.exists()) {
projectCreated = true;
}
if (!projectCreated && event.data.changed()) {
projectStateChanged = true;
}
let msg = 'A project state was changed';
if (projectCreated) {
msg = `The following new project was added to the project: ${projectData.title}`;
}
tokens.push("fIGxxxxGtDGxxxx DEVICE Token");
let payload = {
notification: {
title: 'Firebase Notification',
body: 'This Is Message',
sound: 'default',
badge: '1'
}
};
return admin.messaging().sendToDevice(tokens, payload);
});
Here is the log of Cloud function
And here is code on my client side:
messaging.onMessage(function(payload) {
console.log("Message received. ", payload);
// ...
});
There are not any errors, but nothing is in console. Do I have to do something with service worker file?
Any help would be appreciated.
There was an issue with my firebase-messaging-sw.js file. I have resolved this by following code.
importScripts('https://www.gstatic.com/firebasejs/3.9.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/3.9.0/firebase-messaging.js');
var config = {
apiKey: "AIzaSyDtg4aQMQW67Jla1nUzrTTSEhVqpUeqKXI",
authDomain: "payxxxxxxx.firebaseapp.com",
databaseURL: "https://pxyxxxxx.firebaseio.com",
projectId: "pxxoll-pxxxs-fxxx",
storageBucket: "pxxx-pxxx-fxxxx1.apxxxot.com",
messagingSenderId: "2xxxx5xxx"
};
firebase.initializeApp(config);
const messaging = firebase.messaging();
For anyone new that may run across this post and are having issues with this especially if you copied and pasted something from online make sure you check the importScripts version and make sure its the same version as in your package.json file.
Example
Currently this package version is 8.3.1
so update
importScripts('https://www.gstatic.com/firebasejs/3.9.0/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/3.9.0/firebase-messaging.js');
to
importScripts('https://www.gstatic.com/firebasejs/8.3.1/firebase-app.js');
importScripts('https://www.gstatic.com/firebasejs/8.3.1/firebase-messaging.js');

Categories

Resources