ServiceWorker Push Notification options not passing to notification - javascript

The notification options that I am passing to my notifications are not passing to the notification and I am getting a default notification. (Title is the website, body is "The site has been updated in the background").
Service worker is an adapted create-react-app service worker.
Also, the console.log statements in the push event handler are not passing to the browser. Why is this?
The push event listener is directly after the load event listener in the CRA Service Worker
Web-Push API Call to create a web-push notification:
router.post('/:userid', auth, async (req, res) => {
try {
const user = await User.findById(req.params.userid);
user.pushSubscriptions.forEach((sub) => {
if (sub === null) {
return;
}
webpush.setVapidDetails(
'mailto:contact#email.com',
config.get('vapidPublic'),
config.get('vapidSecret')
);
const options = {
endpoint: sub.endpoint,
expirationTime: sub.expirationTime,
keys: {
p256dh: sub.keys.p256dh,
auth: sub.keys.auth,
},
};
console.log(options.endpoint);
webpush
.sendNotification(
options,
JSON.stringify({
title: 'NotifTitle',
body: 'Body',
})
)
.catch((error) => console.log(error));
});
return res.status(200).json({ msg: 'Notification Sent' });
} catch (error) {
console.log(error);
return res.status(500);
}
});
Push listener in sw.js:
window.addEventListener('push', (event) => {
console.log('Notification Recieved', event);
//Fallback data
let data = {
title: 'TestABC',
body: '123456',
};
if (event.data) {
data = JSON.parse(event.data.text());
}
//Notification options
var options = {
body: data.body,
icon: '../public/logo192.png',
image: '../public/logo192.png',
};
event.waitUntil(
console.log(options),
navigator.serviceWorker.registration.showNotification(
data.title,
options
)
);
});
Thanks

try to convert data like this
data = event.data.json();
you can read more here

Related

How to send notification to specefic user

Hello guys i'm struggling to find a solution on how to send a notification to a specific user
with web-push and and serviceWorker
here's the code
(working but everyone can see the notification)
server :
#Post('/notifications/subscribe')
notif(#Body() body: any) {
const subscription = body;
console.log(subscription);
globals.payload = subscription;
const payload = JSON.stringify({
email: subscription.email,
title: 'Hello!',
body: 'It workssss.',
});
webpush
.sendNotification(subscription, payload)
.then((result) => console.log(result))
.catch((e) => console.log(e.stack));
}
serviceWorker
self.addEventListener("push", (event) => {
const data = event.data.json();
const options = {
body: data.body,
};
event.waitUntil(self.registration.showNotification(data.title, options));
});

Firebase functions: send multiple notifications based on array elements

Its possible for me to send a notification to the reciever: idTo which is a string in the database. However, is it possible to use the array-field instead?: participants and send a notification to everyone in the array?
I store my users with their respective tokens at this path in firebase: Users->{userId}:
I've tried changing:
const idTo = doc.idTo
admin.firestore().collection('users').where('uid', '==', idTo).get().then(querySnapshot => {
to:
const participants = doc.participants
admin.firestore().collection('users').where('uid', 'arrayContains', participants).get().then(querySnapshot => {
Full code:
exports.sendNotification = functions.firestore
.document('messages/{roomId1}/room/{message}/message/{messageId}')
.onCreate((snap, context) => {
console.log('----------------start function--------------------')
const doc = snap.data()
console.log(doc)
const idFrom = doc.idFrom
const idTo = doc.idTo
const contentMessage = doc.message
// Get push token user to (receive)
admin.firestore().collection('users').where('uid', '==', idTo).get().then(querySnapshot => {
querySnapshot.forEach(userTo => {
console.log(`Found user to: ${userTo.data().uid}`)
if (userTo.data().pushToken) {
// Get info user from (sent)
admin.firestore().collection('users').where('uid', '==', idFrom).get().then(querySnapshot2 => {
querySnapshot2.forEach(userFrom => {
console.log(`Found user from: ${userFrom.data().uid}`)
const payload = {
notification: {
title: `${userFrom.data().name}`,
body: contentMessage,
badge: '1',
sound: 'default',
clickAction: 'FLUTTER_NOTIFICATION_CLICK',
// badge: '1'
},
data: {
title: '',
content: '',
image: '',
uploader: '',
type: 'chat',
},
}
// Let push to the target device
admin.messaging().sendToDevice(userTo.data().pushToken, payload).then(response => {
return console.log('Successfully sent message:', response)
}).catch(error => {
console.log('Error sending message:', error)
})
})
return console.log('failed')
}).catch(error => {
console.log('Error sending message:', error)
})
} else {
console.log('Can not find pushToken target user')
}
})
return console.log('error: invalid path')
}).catch(error => {
console.log('Error sending message:', error)
})
return null
})
I'm thinking maybe I need to loop over the array for each of the users and somehow execute the push notification. Any ideas are welcome
var messageToSend=req.body.messageToSend;
var message = { //this may vary according to the message type (single recipient, multicast, topic, et cetera)
registration_ids:regTokens , // Id List if more then one recipent
//to : regTokens, //use if One recipient
data: { //This is only optional, you can send any data
score: '',
time: ''
},
notification:{
body : messageToSend
}
};
console.log(message);
fcm.send(message, function(err, response){
if (err) {
console.log("Something has gone wrong!",err);
} else {
console.log("Successfully sent with response: ", response);
}
});

Getting data from Cognito triggers

I have an existing table of users. And I need to get data on user login.
Is it possible to use some cognito trigger for it?
I was trying to use postAuthentication:
postAuthentication:
handler: triggers.postAuthentication
timeout: 10
environment:
GET_USER_LAMBDA: ${file(./env.yml):${'GET_USER_LAMBDA'}}
events:
- cognitoUserPool:
pool: ${file(./env.yml):${'POOL'}}
trigger: PostAuthentication
existing: true
module.exports.postAuthentication = (event, context, callback) => {
try {
const firstName = event.request.userAttributes['custom:firstName'];
const lastName = event.request.userAttributes['custom:lastName'];
lambda.invoke({
FunctionName: GET_USER_LAMBDA,
Payload: JSON.stringify({
query: `${firstName}_${lastName}`
}, null, 2)
})
.promise()
.then(data => {
const body = JSON.parse(data['Payload']).body;
if (body && body.Items && body.Items[0]) {
event.request.clientMetadata = {};
event.request.clientMetadata.body = JSON.stringify(body.Items[0]);
callback(null, event);
} else {
callback(new Error(`Couldn't fetch USER`));
}
});
} catch (error) {
context.done(error);
}
};
The lambda.invoke successfully returns data and there is no any errors but I can't find clientMetadata on front-end.
What trigger should I use and how to get user data?

How to update state without refreshing page in reactjs

I would like to update my dashboard if there are any changes from backend as notification in Facebook.
I have two pages:
A page for the user sending a request message
A page for the user profile where the user can see all the request messages
If there is a new request message, the user needs to refresh the user profile in order to see the new message. I want the new message to be displayed without refreshing the page. Here is my code:
In a message page
state = {
team: {
message: 'Hi! I would like to join in your team! Please accept my request',
invitation_message: 'Hi! I would like to invite you to join in my team.',
email: '',
},
}
// Invite user to a team
handleInvite = event => {
event.preventDefault();
const userObject = JSON.parse(localStorage.getItem('user'));
const jwt = userObject.jwt;
const config = {
headers: { 'Authorization': `bearer ${jwt}` },
};
api
.post('/teammembers', {
team: this.state.teaminfo,
profile: responseData.data[0],
status: "invited",
message: this.state.team.invitation_message,
}, config)
.then(response => {
this.setState({
success_message: true,
})
console.log('Success', response);
})
.catch(err => {
console.log('An error occurred:', err);
});
}
In a user profile page
export class UserProfile extends React.Component {
import socketIOClient from "socket.io-client";
state = {
invited_teams:[],
endpoint: "myurl"
}
componentDidMount() {
const { endpoint } = this.state;
//Very simply connect to the socket
const socket = socketIOClient(endpoint);
socket.on('request', (data) => {
this.setState({ state: data.requests });
});
if (localStorage.getItem('userData')) {
const userObject = JSON.parse(localStorage.getItem('user'));
api
.get(`/profiles/?user=${userObject.user.id}`)
.then(responseData => {
this.setState({
invited_teams: responseData.data
})
}
}
}
Could anyone help me to solve this problem?
Use socket.IO library. You can set a listener on new request and then update the state.
socket.on('request' , (data) => {
this.setState({state: data.requests});
});

How to customize browser webpush notification body & icon?

How can we make the browser notification icon and body dynamically customized based upon the #challenge.image and the #challenge.description?
I got the notification to pop up by clicking webpush-button, but only the title of the message is dynamically changing. How can I make it work for body and icon too?
challenges/show.html.erb
<script>
$('.webpush-button').on('click', (e) => {
navigator.serviceWorker.ready
.then((serviceWorkerRegistration) => {
serviceWorkerRegistration.pushManager.getSubscription()
.then((subscription) => {
$.post('/push', {
subscription: subscription.toJSON(),
message: "<%= #challenge.name %>", # This works dynamically for the title, but how can I get message to also dynamically send body and icon so I can use <%= #challenge.image %> for icon and <%= #challenge.description %> for body?
});
});
});
});
</script>
push_notifications_controller
class PushNotificationsController < ApplicationController
def push
Webpush.payload_send(
message: params[:message],
endpoint: params[:subscription][:endpoint],
p256dh: params[:subscription][:keys][:p256dh],
auth: params[:subscription][:keys][:auth],
vapid: {
subject: "mailto:sender#example.com",
public_key: ENV['VAPID_PUBLIC_KEY'],
private_key: ENV['VAPID_PRIVATE_KEY']
}
)
end
end
application.js
if (navigator.serviceWorker) {
navigator.serviceWorker.register('/serviceworker.js')
.then(function(reg) {
console.log('Service worker change, registered the service worker');
});
}
// Otherwise, no push notifications :(
else {
console.error('Service worker is not supported in this browser');
}
// When serviceWorker is supported, installed, and activated,
// subscribe the pushManager property with the vapidPublicKey
navigator.serviceWorker.ready.then((serviceWorkerRegistration) => {
serviceWorkerRegistration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: window.vapidPublicKey
});
});
// Let's check if the browser supports notifications
if (!("Notification" in window)) {
console.error("This browser does not support desktop notification");
}
// Let's check whether notification permissions have already been granted
else if (Notification.permission === "granted") {
console.log("Permission to receive notifications has been granted");
}
// Otherwise, we need to ask the user for permission
else if (Notification.permission !== 'denied') {
Notification.requestPermission(function (permission) {
// If the user accepts, let's create a notification
if (permission === "granted") {
console.log("Permission to receive notifications has been granted");
}
});
}
serviceworker.js.erb
console.log('[Service Worker] Hello world!');
var CACHE_VERSION = 'v1';
var CACHE_NAME = CACHE_VERSION + ':sw-cache-';
function onInstall(event) {
console.log('[Serviceworker]', "Installing!", event);
event.waitUntil(
caches.open(CACHE_NAME).then(function prefill(cache) {
return cache.addAll([
// make sure serviceworker.js is not required by application.js
// if you want to reference application.js from here
'<%#= asset_path "application.js" %>',
'<%= asset_path "application.css" %>',
'/offline.html',
]);
})
);
}
function onActivate(event) {
console.log('[Serviceworker]', "Activating!", event);
event.waitUntil(
caches.keys().then(function(cacheNames) {
return Promise.all(
cacheNames.filter(function(cacheName) {
// Return true if you want to remove this cache,
// but remember that caches are shared across
// the whole origin
return cacheName.indexOf(CACHE_VERSION) !== 0;
}).map(function(cacheName) {
return caches.delete(cacheName);
})
);
})
);
}
// Borrowed from https://github.com/TalAter/UpUp
function onFetch(event) {
event.respondWith(
// try to return untouched request from network first
fetch(event.request).catch(function() {
// if it fails, try to return request from the cache
return caches.match(event.request).then(function(response) {
if (response) {
return response;
}
// if not found in cache, return default offline content for navigate requests
if (event.request.mode === 'navigate' ||
(event.request.method === 'GET' && event.request.headers.get('accept').includes('text/html'))) {
console.log('[Serviceworker]', "Fetching offline content", event);
return caches.match('/offline.html');
}
})
})
);
}
self.addEventListener("push", (event) => {
let title = (event.data && event.data.text()) || "Yay a message";
let body = "We have received a push message";
let icon = '/assets/default.png';
event.waitUntil(
self.registration.showNotification(title, { title,body, icon })
)
});
self.addEventListener('install', onInstall);
self.addEventListener('activate', onActivate);
self.addEventListener('fetch', onFetch);
serviceworker-companion.js
if (navigator.serviceWorker) {
navigator.serviceWorker.register('/serviceworker.js', { scope: './' })
.then(function(reg) {
console.log('[Companion]', 'Service worker registered!');
});
}
I implemented push via serviceworker gem, webpush gem, VAPID tutorial.
The event.data object in the self.addEventListener("push", (event)=> {...}), has a method json(), that method convert to json the event.data value.
This will works only if the sended data is in the proper json format.
Here is an example how I'm using web push:
self.addEventListener("push", function onPush(event) {
var data = event.data.json()
event.waitUntil(self.registration.showNotification(data.message.title, {
body: data.message.body,
icon: data.message.icon,
actions: [
{ action: 'Button one', title: "Button one text" },
{ action: 'Button two', title: "Button two text" }
]
}));
});
Here is a method from my user.rb model:
def send_web_push(title: , body: )
# the web_push is a payload with I save to the user record,
# that's allow me to send web push with background worker.
payload = {
endpoint: web_push['endpoint'],
keys: {
auth: web_push['auth'],
p256dh: web_push['p256dh']
}
}
push = WebPush.new(payload)
push.set_vapid_details(
'mailto:support#awesomesupewebapp.com',
Rails.application.secrets.web_push_public_key,
Rails.application.secrets.web_push_private_key
)
# below is a `:message` key in json format
push.send_notification({
message: {
title: title,
body: body,
icon: "/this_is_my_icon.png"
}
}.to_json)
end
I use some other ruby gem for web_push, but its common I assume. From that example you should see how is the body, title and icon keys can be changed.
view
<%= content_tag(:button, "Foo", class: "webpush-button") %>
<script>
$('.webpush-button').on('click', (e) => {
navigator.serviceWorker.ready
.then((serviceWorkerRegistration) => {
serviceWorkerRegistration.pushManager.getSubscription()
.then((subscription) => {
$.post('/push', {
subscription: subscription.toJSON(),
message: "<%= #challenge.name %>",
body: "<%= #challenge.why %>",
icon: "/assets/default.png"
});
});
});
});
</script>
controller
class PushNotificationsController < ApplicationController
def push
Webpush.payload_send(
message: JSON.generate({
title: params[:message],
body: params[:body],
icon: params[:icon]
}),
endpoint: params[:subscription][:endpoint],
p256dh: params[:subscription][:keys][:p256dh],
auth: params[:subscription][:keys][:auth],
vapid: {
subject: "mailto:sender#example.com",
public_key: ENV['VAPID_PUBLIC_KEY'],
private_key: ENV['VAPID_PRIVATE_KEY']
}
)
end
end
js
self.addEventListener("push", (event) => {
var data = event.data.json();
let title = (event.data && data.title) || "Yay a message";
let body = (event.data && data.body) || "We have received a push message";
let icon = (event.data && data.icon) || "/assets/blue-astronaut.png";
event.waitUntil(
self.registration.showNotification(title, { title,body, icon })
)
});

Categories

Resources