I am willing to use web-push notifications on my web app. I have already setup serviceWorkers on the front-end(React) and implemented web-push notifications on my backend(NodeJS). Now I just need to send notifications which are user specific, means only specific users should receive those notifications.
For e.g. In my web app's backend I will be receiving some live values. Say, there is a collection named "users" where all the user's data will be stored. Now these users will have a field named "device" where the user will receive numeric values which will be updated within 40-50 seconds.
Now, their will be a threshold for these values. Say, for e.g. if the value reaches above 200 then that specific user should receive a push notification, letting them know that the device has reached it's limit.
How is it possible for me to create such user specific push notifications where the notification will be sent to only that user who's device value has reached above 200 ?. P.S I am using Mongoose for the database.
FrontEnd code(react.js)
sw.js:
self.addEventListener("notificationclick", function (event) {
// on Notification click
let notification = event.notification;
let action = event.action;
console.log("Notification====>", notification);
if (action === "confirm") {
console.log("Confirm clicked");
notification.close(); // Closes the notifcation
} else {
event.waitUntil(
clients.matchAll().then(function (clis) {
var client = clis.find(function (c) {
return c.visibilityState === "visible";
});
if (client !== undefined) {
// found open window
client.navigate("http://localhost:3000"); // means website opens on the same tab where user is on
client.focus();
} else {
// if client's window was not open
clients.openWindow("http://localhost:3000"); // when browser window is closed, open website
}
notification.close();
})
);
console.log(action); // name of action, basically id
}
});
self.addEventListener("notificationclose", function (event) {
console.log("Notification closed", event);
});
// triggers when we get an incoming push message
self.addEventListener("push", function (event) {
console.log("Push notifications recieved from eventListner", event);
var data = { title: "New!", content: "New things" };
if (event.data) {
// check if payload exists(from backend)
data = JSON.parse(event.data.text()); // recieve payload & store
}
var options = {
body: data.content,
icon: "https://iconarchive.com/download/i90141/icons8/windows-8/Cinema-Avengers.ico",
tag: "id1",
renotify: true,
};
event.waitUntil(self.registration.showNotification(data.title, options));
});
swReg.js:
if ("serviceWorker" in navigator) {
console.log("Registering service worker");
navigator.serviceWorker
.register("/sw.js")
.then(() => {
console.log("Service Worker has been registered");
})
.catch((err) => console.error(err));
}
function urlBase64ToUint8Array(base64String) {
const padding = "=".repeat((4 - (base64String.length % 4)) % 4);
const base64 = (base64String + padding).replace(/-/g, "+").replace(/_/g, "/");
const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
}
function displayConfirmNotification() {
if ("serviceWorker" in navigator) {
const options = {
body: "After subscription managing done",
// icon: "/src/assets/img/pattern_react.png",
// tag:"" ==> in advanced options.
vibrate: [100, 50, 200],
// badge:""
tag: "confirm",
renotify: true,
actions: [
{ action: "confirm", title: "okay" }, // optnl icon:""
{ action: "cancel", title: "cancel" },
],
};
navigator.serviceWorker.ready.then(function (swreg) {
swreg.showNotification("Successfully subscribed sW", options);
});
}
}
function configPushSub() {
if (!("serviceWorker" in navigator)) {
return;
}
var reg;
navigator.serviceWorker.ready
.then(function (swreg) {
// access to sW registration
reg = swreg;
return swreg.pushManager.getSubscription(); // returns any existing subscription
})
.then(function (sub) {
// sub holds the current subscription, if subscription doesn't exist then it returns null
if (sub === null) {
// Create a new subscription
var vapidPublicKey = KEY;
var convertedPublicKey = urlBase64ToUint8Array(vapidPublicKey);
return reg.pushManager.subscribe({
userVisibleOnly: true, // for security
applicationServerKey: convertedPublicKey, // for security & server storage
}); // create new subscription
} else {
// We already have a subscription
}
})
.then(function (newSub) {
// have to pass this subscription(new one) to backend
console.log("New subb =======>", newSub);
return fetch("http://localhost:8000/subscribeToPushNotifications", {
method: "POST",
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify({
subscriptionObj: newSub,
}),
});
})
.then(function (res) {
if (res.ok) {
displayConfirmNotification();
}
})
.catch(function (e) {
console.log("err while subbing====>", e);
});
}
function askForNotificationPermission() {
Notification.requestPermission(function (result) {
console.log("User's choice", result);
if (result !== "granted") {
console.log("Permission rights not granted");
} else {
configPushSub();
// displayConfirmNotification();
}
});
}
if ("Notification" in window) {
askForNotificationPermission();
}
Backend: API to subscribe:
exports.subscribeToPushNotifications = async (req, res) => {
const { subscriptionObj } = req.body;
// console.log("Subscription object=====>", subscriptionObj);
if (subscriptionObj != undefined || subscriptionObj != null) {
let newSubscription = new Subscription({
pushSubscription: subscriptionObj,
});
await newSubscription.save();
if (newSubscription) {
console.log(newSubscription);
return res.status(200).send("Subscription made");
} else {
console.log("Not subbed");
return res.status(400).send("Subscription not made");
}
} else {
console.log("Sub obj is null");
return res.status(400).send("Sub obj was null");
}
};
Checking if values are more than the threshold and then sending notification:(For example purposes). This is an example for single user only.
exports.checkStatus = async () => {
schedule.scheduleJob("*/10 * * * * *", async () => {
let subscriptions = await Subscription.find({});
let Email = "james@mail.com";
let findUser = await User.findOne({ Email });
if (findUser) {
if (findUser.device > 200) // findUser.device contains the value
{
for (let i = 0; i < subscriptions.length; i++) { //Notification will be sent to all users which I don't want.
webpush.sendNotification(
subscriptions[i].pushSubscription,
JSON.stringify({
title: "Alert",
content: "Value has reached it's limit",
})
);
}
}
}
});
};
How can I make this work such that only those users who's device's value has gone above 200 will only receive the notification and not all the subscribed users.