How to use web push notifications with Meteor? - meteor

I am just hoping to find some good examples, but I haven't found anything. Pushjs is working on localhost, but how do I use the service worker correctly? I have linked the file, but this:
self.addEventListener('push', function (event) {
console.log('Received a push message', event);
// var notificationOptions = {
// body: 'The highlights of Google I/O 2015',
// icon: 'images/icon#192.png',
// tag: 'highlights-google-io-2015',
// data: null
// };
var notificationOptions = {
body: "Martin matches 89% with you!",
icon: '/images/LogoStore.jpg',
timeout: 4000,
link: "/",
onClick: function () {
window.focus();
this.close();
}
}
if (self.registration.showNotification) {
self.registration.showNotification('Timely News', notificationOptions);
return;
} else {
new Notification('Timely News', notificationOptions);
}
});
does not work.

Related

Firebase Service Worker

I have added actions to my firebase notifications, View and Opt-Out. I want users to be directed to the url which comes with the notification. The code below is supposed to work but it doesn't can someone please tell me what I am doing wrong.
const messaging = firebase.messaging();
messaging.onBackgroundMessage((payload) => {
console.log('[firebase-messaging-sw.js] Received background message ', payload);
// Customize notification here
var notificationTopic = topic;
var notificationTitle = title;
var notificationOptions = {
body: body,
icon: icon,
image: image,
badge: badge,
click_action: url,
actions: [
{action: 'view', title: 'View'},
{action: 'unsubscribe', title: 'Opt-Out'}
]
}
self.registration.showNotification(notificationTitle, notificationOptions);
});
self.addEventListener('notificationclick', function(event) {
event.notification.close();
event.waitUntil(clients.matchAll({
type: "window"
}).then(function(clientList) {
for (var i = 0; i < clientList.length; i++) {
var client = clientList[i];
if (client.url == url && 'focus' in client)
return client.focus();
}
if (clients.openWindow)
return clients.openWindow(url);
}));
});
I had the same problem, in the of the day, I surrendered and just opened URL in a new tab. To pass URL I used at "data" object

web push notification - getting same notifications multiple times on chrome

I'm working on push notification. I'm facing the issue of getting same notification multiple time even I have written code for closing notification.
self.addEventListener('push', async (event) => {
if (event.data) {
const [data] = event.data.json()
const body = `Notification: ${data.timestampV} - ${data.eventType}`
const title = 'Web Notification'
const options = {
body,
icon: 'images/icon.png',
badge: 'images/badge.png',
}
event.waitUntil(self.registration.showNotification(title, options))
} else {
console.log('This push event has no data.')
}
})
self.addEventListener('notificationclick', function (event) {
event.notification.close()
// close all notifications
self.registration.getNotifications().then(function (notifications) {
notifications.forEach(function (notification) {
notification.close();
});
});
// eslint-disable-next-line no-undef
clients.openWindow('/feeds')
})
Thanks in advance.
This code works for me.
Add async function
var newEvent = new Event('push');
newEvent.waitUntil = e.waitUntil.bind(e);
newEvent.data = {
json: async function () {
var newData = e.data.json();
newData.notification = newData.data;
self.registration.showNotification(newData.data.title, {
...newData.data,
data: newData.data
});
return newData;
},
};
please check it from
https://github.com/firebase/quickstart-js/issues/71#issuecomment-396958853

Nodejs-Krakenjs: PayPal Checkout Server Integration

I try to follow instruction here: https://developer.paypal.com/docs/checkout/how-to/server-integration/# to integrate paypal checkout into my website (developed by Nodejs-krakenjs, Angularjs)
// FOR PAYPAL PAYMENT
function renderPaypalButton() {
paypal.Button.render({
env: 'sandbox', // Or 'production'
// Set up the payment:
// 1. Add a payment callback
payment: function (data, actions) {
// 2. Make a request to your server
console.log('Make a request to your server');
return actions.request.post('/file/createPayment', {
//_token: csrf_token()
}).then(function (res) {
console.log('return res id');
// 3. Return res.id from the response
return res.id;
});
},
// Execute the payment:
// 1. Add an onAuthorize callback
onAuthorize: function (data, actions) {
// 2. Make a request to your server
return actions.request.post('/file/executePayment', {
paymentID: data.paymentID,
payerID: data.payerID
})
.then(function (res) {
// 3. Show the buyer a confirmation message.
alert(res);
console.log(res);
});
}
}, '#paypal-button');
}
Server side:
// Set up the payment:
// 1. Set up a URL to handle requests from the PayPal button
router.post('/createPayment', function (req, res) {
console.log('aa');
// 2. Call /v1/payments/payment to set up the payment
request.post(PAYPAL_API + '/v1/payments/payment',
{
auth:
{
user: CLIENT,
pass: SECRET
},
body:
{
intent: 'sale',
payer:
{
payment_method: 'paypal'
},
transactions: [
{
amount:
{
total: '5.99',
currency: 'USD'
}
}],
redirect_urls:
{
return_url: 'http://localhost:1272',
cancel_url: 'http://localhost:1272'
}
},
json: true
}, function (err, response) {
if (err) {
console.error(err);
return res.sendStatus(500);
}
// 3. Return the payment ID to the client
res.json(
{
id: response.body.id
});
});
});
// Execute the payment:
// 1. Set up a URL to handle requests from the PayPal button.
router.post('/executePayment', function (req, res) {
// 2. Get the payment ID and the payer ID from the request body.
var paymentID = req.body.paymentID;
var payerID = req.body.payerID;
// 3. Call /v1/payments/payment/PAY-XXX/execute to finalize the payment.
request.post(PAYPAL_API + '/v1/payments/payment/' + paymentID + '/execute',
{
auth:
{
user: CLIENT,
pass: SECRET
},
body:
{
payer_id: payerID,
transactions: [
{
amount:
{
total: '10.99',
currency: 'USD'
}
}]
},
json: true
},
function (err, response) {
if (err) {
console.error(err);
return res.sendStatus(500);
}
// 4. Return a success response to the client
res.json(
{
status: 'success'
});
});
});
}
The paypal library were loaded in index.html:
<script src="https://www.paypalobjects.com/api/checkout.js"></script>
And in a Modal form that the paypal checkout button is integrated:
<div id="paypal-button"></div>
<script>
setTimeout(renderPaypalButton(), 3000);
</script>
The Paypal-checkout button is rendered and displayed on the modal,but after click the button, the Palpay Login modal appears a few seconds then disappears, the "Error: CSRF token missing" appears:
POST http://localhost:1272/file/createPayment 500 (Internal Server Error) Uncaught Error: Error: Request to post /file/createPayment failed with 500 error. Correlation id: unknown
HomeInternal server errorThe URL /file/createPayment had the following error Error: CSRF token missing.
at XMLHttpRequest.<anonymous> (https://www.paypalobjects.com/api/checkout.js:14010:39)
at Object._RECEIVE_MESSAGE_TYPE.(anonymous function) [as postrobot_message_response] (https://www.paypalobjects.com/api/checkout.js:2569:31)
at receiveMessage (https://www.paypalobjects.com/api/checkout.js:2614:60)
at messageListener (https://www.paypalobjects.com/api/checkout.js:2635:13)
at XMLHttpRequest.<anonymous> (https://www.paypalobjects.com/api/checkout.js:14010:39)
at Object._RECEIVE_MESSAGE_TYPE.(anonymous function) [as postrobot_message_response] (https://www.paypalobjects.com/api/checkout.js:2569:31)
at receiveMessage (https://www.paypalobjects.com/api/checkout.js:2614:60)
at messageListener (https://www.paypalobjects.com/api/checkout.js:2635:13)
at deserializeError (https://www.paypalobjects.com/api/checkout.js:3302:23)
at https://www.paypalobjects.com/api/checkout.js:3323:270
at https://www.paypalobjects.com/api/checkout.js:3052:30
at eachArray (https://www.paypalobjects.com/api/checkout.js:3035:51)
at each (https://www.paypalobjects.com/api/checkout.js:3041:35)
at replaceObject (https://www.paypalobjects.com/api/checkout.js:3051:13)
at https://www.paypalobjects.com/api/checkout.js:3053:169
at eachObject (https://www.paypalobjects.com/api/checkout.js:3038:65)
at each (https://www.paypalobjects.com/api/checkout.js:3041:144)
at replaceObject (https://www.paypalobjects.com/api/checkout.js:3051:13)
You all could please help me. I don't understand what makes Csrf here. Thanks for all !!
The example code shown in the PayPal documentation does not work. You
have to pass it an QAuth access token not a CLIENT/SECRET. I got the code
to work but had to change it quite a bit.
Here's the node.js server code:
var express = require('express');
var request = require('request');
var app = express();
var port = 3000;
var bodyParser = require('body-parser');
app.use(bodyParser.json());
// support json encoded bodies
app.use(bodyParser.urlencoded({ extended: true })); // support encoded bodies
var PAYPAL_API = 'https://api.sandbox.paypal.com';
app.post('/my-api/create-payment/', function(req, res)
{
// Set up the payment:
// 1. Set up a URL to handle requests from the PayPal button
// 2. Allow cross-domain
res.setHeader('access-control-allow-origin', '*');
request.post(PAYPAL_API + '/v1/payments/payment',
{
headers: {
Authorization: 'Bearer <your access token>'
},
body:
{
intent: 'sale',
payer:
{
payment_method: 'paypal'
},
transactions: [
{
amount:
{
total: '5.99',
currency: 'USD'
}
}],
redirect_urls:
{
return_url: 'https://www.yourwebsite.com',
cancel_url: 'https://www.yourwebsite.com'
}
},
json: true
}, function(err, response)
{
if (err)
{
console.error(err);
return res.sendStatus(500);
}
// 3. Return the payment ID to the client
res.json(
{
id: response.body.id
});
});
})
// Execute the payment:
// 1. Set up a URL to handle requests from the PayPal button.
app.post('/my-api/execute-payment/', function(req, res)
{
// 2. Get the payment ID and the payer ID from the request body.
var paymentID = req.body.paymentID;
var payerID = req.body.payerID;
res.setHeader('access-control-allow-origin', '*');
// 3. Call /v1/payments/payment/PAY-XXX/execute to finalize the payment.
request.post(PAYPAL_API + '/v1/payments/payment/' + paymentID +
'/execute',
{
headers: {
Authorization: 'Bearer <your access token>'
},
body:
{
payer_id: payerID
},
json: true
},
function(err, response)
{
if (err)
{
console.error(err);
return res.sendStatus(500);
}
// 4. Return a success response to the client
res.json(
{
status: 'success'
});
});
}).listen(3000, function()
{
console.log('Server listening at http://localhost:3000/');
});
Here's the HTML:
<!DOCTYPE html>
<html>
<title>PayPal Client Test</title>
<body>
<p>This is the PayPal button for my client sandbox test</p>
<script src="https://www.paypalobjects.com/api/checkout.js"></script>
<div id="paypal-button"></div>
<script>
paypal.Button.render({
env: 'sandbox', // Or 'production'
// Set up the payment:
// 1. Add a payment callback
payment: function(data, actions) {
// 2. Make a request to your server
return actions.request.post('http://localhost:3000/my-api/create-payment/')
.then(function(res) {
// 3. Return res.id from the response
//alert("Res ID="+res.id);
return res.id;
});
},
// Execute the payment:
// 1. Add an onAuthorize callback
onAuthorize: function(data, actions) {
return actions.request.post('http://localhost:3000/my-api/execute-payment/', {
paymentID: data.paymentID,
payerID: data.payerID
})
.then(function(res) {
alert("Payment made!");
});
},
onError: function (err) {
alert("err="+err);
}
}, '#paypal-button');
</script>
</body>
</html>

GCM pushEvent issue

I have sent push notification to service-worker using GCM api. But in my service-worker has no attribute data. Hence, I am getting event.data as undefined.
self.addEventListener('push', function(event) {
console.log('Received a push message', event);
console.log('testing');
var data = event.data;
var title = data.title;
var body = data.body;
var icon = '/images/image.png';
var tag = 'simple-push-demo-notification-tag';
event.waitUntil(function() {
self.registration.showNotification(title, {
body: body,
icon: icon,
tag: tag
})
});
});
In below code, I call GCM api.
uri = 'https://android.googleapis.com/gcm/send'
payload = json.dumps({
'registration_ids': [
User.objects.filter(id=user_id)[0].push_key
],
'data': json.dumps({'title':'New notification','message': 'new message'})
})
headers = {
'Content-Type': 'application/json',
'Authorization': 'key=<Project key>'
}
requests.post(uri, data=payload, headers=headers)
This looks like you've taken parts of the code, not all of it, and as a result, it's not working.
If you at the simple push demo repo, there is a function showNotification.
Here for Ref
function showNotification(title, body, icon, data) {
console.log('showNotification');
var notificationOptions = {
body: body,
icon: icon ? icon : '/images/touch/chrome-touch-icon-192x192.png',
tag: 'simple-push-demo-notification',
data: data
};
return self.registration.showNotification(title, notificationOptions);
}
There is where data is defined.
The data passed into this isn't from the event (or have anything to do with the event object).
To get your code running, just simplify it:
self.addEventListener('push', function(event) {
console.log('Received a push message', event);
console.log('testing');
var title = 'My Title';
var body = 'My notification body';
var icon = '/images/image.png';
var tag = 'simple-push-demo-notification-tag';
event.waitUntil(function() {
self.registration.showNotification(title, {
body: body,
icon: icon,
tag: tag
})
});
});

Need help updating Router.js for upgrade to Meteor 1.0 and iron:router package

I'm in the midst of updating from Meteor 8.2 to Meteor 1.0. I've removed all my old meteorite packages and installed the relevant meteor package system packages. I had to install the new iron-router package and I'm getting the following error in my console on meteor run:
Route dispatch never rendered. Did you forget to call this.next() in an onBeforeAction?
The migration notes for the package say: "onBeforeAction hooks now require you to call this.next(), and no longer take a pause() argument."
I tried following the example by remove pause from the function and adding this.next(); after the else statement, but to no avail.
How to edit my router so it uses the new onBeforeAction hook? Also, anything else you can call out from the migration that might be problematic would be much appreciated. Thanks!
Here's my router file:
/*****************************************************************************/
/* Client and Server Routes */
/*****************************************************************************/
// TODO: use these as per the Event Mind CLI tool.
//Router.configure({
// templateNameConverter: 'upperCamelCase',
// routeControllerNameConverter: 'upperCamelCase'
//});
Router.configure({
layoutTemplate: 'devLayout',
notFoundTemplate: 'devMain',
loadingTemplate: 'loading'
});
Router.onRun(function () {Session.set("waiting-on", null); });
Router.onBeforeAction(function() { Alerts.clearSeen(); });
var filters = {
nProgressHook: function (pause) {
// we're done waiting on all subs
if (this.ready()) {
NProgress.done();
} else {
NProgress.start();
pause(); // stop downstream funcs from running
}
}
};
Router.onBeforeAction(filters.nProgressHook);
Meteor.startup(function () {
Router.map(function () {
this.route('loading');
// reset password urls use hash fragments instead of url paths/query
// strings so that the reset password token is not sent over the wire
// on the http request
this.route('reset-password', {
template: 'devMain',
layoutTemplate: 'devLayout',
onRun: function () {
var token = this.params.hash;
Meteor.logout(function () {
Session.set("viewing-settings", true);
Session.set("set-password-token", token);
Session.set("settings-set-password", true);
// Session.set("enrolling", true) // do something special?
});
}
});
this.route('verify-email', {
template: 'devMain',
layoutTemplate: 'devLayout',
action: function () {
var self = this;
var token = self.params.hash;
Accounts.verifyEmail(token, function (err) {
if (!err) {
Alerts.throw({
message: "Your email address is now verified!",
type: "success", where: "main",
autoremove: 3000
});
Router.go('home');
} else {
Alerts.throw({
message: "Hmm, something went wrong: \""+err.reason +
"\". Try again?",
type: "danger", where: "main"
});
Session.set("viewing-settings", true);
Router.go('home');
}
});
}
});
this.route('leave-game', {
template: 'devMain',
layoutTemplate: 'devLayout',
action: function () {
var self = this;
var token = self.params.hash;
Meteor.call("leaveGameViaToken", token, function (err, res) {
if (!err) {
// Idempotently verify user's email,
// since they got the token via email.
Accounts.verifyEmail(token);
if (res.error) {
// e.g. "Leave-game link is for unknown game"
Alerts.throw({
message: res.error.reason, type: "danger", where: "main"
});
Router.go("home");
} else {
Alerts.throw({
message: "OK, you are no longer in this game.",
type: "success", where: res.gameId
});
Router.go("devDetail", {_id: res.gameId});
}
} else {
Alerts.throw({
message: "Hmm, something went wrong: \""+err.reason + "\".",
type: "danger", where: "main"
});
Router.go("home");
}
});
}
});
this.route('game-on', {
template: 'devMain',
layoutTemplate: 'devLayout',
action: function () {
var self = this;
var token = self.params.hash;
Meteor.call("gameOnViaToken", token, function (err, res) {
if (err || (res && res.error)) {
errorMessage = err ? "Hmm, something went wrong: \"" + err.reason + "\"." : res.error.reason;
Alerts.throw({
message: errorMessage, type: "danger", where: "main"
});
Router.go("home");
} else {
Alerts.throw({
message: "Woohoo! Players will be notified.",
type: "success", where: res.gameId
});
Router.go("devDetail", {_id: res.gameId, token: token });
}
});
}
});
this.route('cancel-game', {
template: 'devMain',
layoutTemplate: 'devLayout',
action: function () {
var self = this;
var token = self.params.hash;
Meteor.call("cancelGameViaToken", token, function (err, res) {
if (!err) {
Accounts.verifyEmail(token);
if (res.error) {
Alerts.throw({
message: res.error.reason, type: "danger", where: "main"
});
Router.go("home");
} else {
Alerts.throw({
message: "OK, your game is now cancelled, and players "
+ "will be notified.",
type: "success", where: "main"
});
Router.go("home");
}
} else {
Alerts.throw({
message: "Hmm, something went wrong: \""+err.reason + "\".",
type: "danger", where: "main"
});
Router.go("home");
}
});
}
});
// quite similar to 'leave-game' route
this.route('unsubscribe-all', {
template: 'devMain',
layoutTemplate: 'devLayout',
action: function () {
var self = this;
var token = self.params.hash;
Meteor.call("unsubscribeAllViaToken", token, function (err, res) {
if (!err) {
// Idempotently verify user's email,
// since they got the token via email.
Accounts.verifyEmail(token);
if (res.error) {
// e.g. "Token provided in link is not an unsubscribe-all token"
Alerts.throw({
message: res.error.reason, type: "danger", where: "main"
});
Router.go("home");
} else {
Alerts.throw({
message: "OK, you will no longer receive emails "
+ "from Push Pickup.",
type: "success", where: "main"
});
Router.go("home");
}
} else {
Alerts.throw({
message: "Hmm, something went wrong: \""+err.reason + "\".",
type: "danger", where: "main"
});
Router.go("home");
}
});
}
});
this.route('enroll-account', {
template: 'devMain',
layoutTemplate: 'devLayout',
onRun: function () {
var token = this.params.hash;
Meteor.logout(function () {
Session.set("viewing-settings", true);
Session.set("set-password-token", token);
Session.set("settings-set-password", true);
// Session.set("enrolling", true) // do something special?
});
}
});
// the home page. listing and searching for games
this.route('home', {
path: '/',
template: 'devMain',
layoutTemplate: 'devLayout'
});
// typical user interaction with a single game
this.route('devDetail', {
path: '/g/:_id/:token?',
layoutTemplate: 'devLayout',
onRun: function () {
Session.set("joined-game", null);
},
waitOn: function () {
return Meteor.subscribe('game', this.params._id);
},
onBeforeAction: function (pause) {
Session.set("soloGame", this.params._id);
},
data: function () {
var game = Games.findOne(this.params._id);
if (game) {
Session.set("gameExists", true);
}
return game;
},
action: function () {
var token = this.params.token;
if (Session.get("gameExists")) {
this.render();
} else {
Router.go('home');
Alerts.throw({
message: "Game not found",
type: "warning", where: "top"
});
}
if (token) {
Meteor.call("sendReminderEmailsViaToken", token, function (err, res) {
var errorMessage;
Accounts.verifyEmail(token);
if (err || (res && res.error)) {
errorMessage = err ? "Hmm, something went wrong: \"" + err.reason + "\"." : res.error.reason;
Alerts.throw({
message: errorMessage, type: "danger", where: "main"
});
Router.go("home");
}
});
}
},
onStop: function () {
Session.set("soloGame", null);
Session.set("gameExists", null);
}
});
this.route('devAddGame', {
path: '/addGame',
template: 'devEditableGame',
layoutTemplate: 'devLayout',
onRun: function () {
Session.set("selectedLocationPoint", null);
Session.set("newGameDay", null);
Session.set("newGameTime", null);
InviteList.remove({});
},
waitOn: function() {
Meteor.subscribe('recently-played');
},
data: function () {
return {
action: 'add',
title: 'Add game',
submit: 'Add game'
};
}
});
this.route('invitePreviousPlayers', {
path: 'invitePlayers',
template: 'invitePreviousPlayers',
layoutTemplate: 'devLayout'
});
this.route('devEditGame', {
path: '/editGame/:_id',
template: 'devEditableGame',
layoutTemplate: 'devLayout',
onRun: function () {
Session.set("selectedLocationPoint", null);
},
waitOn: function () {
return Meteor.subscribe('game', this.params._id);
},
onBeforeAction: function (pause) {
Session.set("soloGame", this.params._id);
},
data: function () {
return _.extend({
action: 'edit',
title: 'Edit game',
submit: 'Update game'
}, Games.findOne(this.params._id));
},
action: function () {
var self = this;
var user = Meteor.user();
var game = self.data();
if (user && user._id === game.creator.userId ||
user && user.admin) {
self.render();
} else {
Router.go('home');
}
}
});
this.route('adminView', {
path: '/admin',
onBeforeAction: function () {
var user = Meteor.user();
if (!user || !user.admin) {
this.render('home');
}
}
});
});
});
New onBeforeAction hook must call this.next(); You must call it in every onBeforeAction. For example your admin route in new Iron Router would look like this:
Router.route('/admin', {
name: 'adminView',
onBeforeAction: function () {
var user = Meteor.user();
if (!user || !user.admin) {
this.render('home');
}
this.next();
}
});
Replace all this.route(...) in Router.map with Router.route('/path', options) and remove Router.map()
Your global onBeforeAction will look like this:
Router.onBeforeAction(function() {
Alerts.clearSeen();
this.next();
});
Also, you don't need to wrap your routes in Meteor.startup(...). You can remove it.
And there is no pause parameter anymore, instead of pause call this.next() outside condition:
var filters = {
nProgressHook: function () {
// we're done waiting on all subs
if (this.ready()) {
NProgress.done();
} else {
NProgress.start();
}
this.next();
}
};
Router.onBeforeAction(filters.nProgressHook);

Resources