I've searched a lot for this problem that I'm stuck on for a week.
I have an Ionic PWA project that receive some notifications from firebase, I can receive the notifications with no problem and can get the payload on foreground (app is open), but I cant get the payload when the app is closed and I don't figure out what is goning on, so I came here to ask the masters to help me.
messaging.setBackgroundMessageHandler(function(payload) {
var notificationTitle = 'Teste';
var notificationOptions = {
body: 'Background Message body.',
tag: 'campanhas',
data: payload.data,
icon: 'https://firebasestorage.googleapis.com/v0/b/hemotomobile-edeb9.appspot.com/o/logo.png?alt=media&token=4a9fc487-b8cf-4d3c-875c-774454ff6f50'
};
return 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 == '/' && 'focus' in client)
return client.focus();
}
if (clients.openWindow)
return clients.openWindow('/');
}));
});
and on my provider for the notifications, I use this code:
public receiveMessage() {
console.log('notification')
this.messaging.onMessage((payload) => {
console.log('payload',payload)
});
}
I call this provider on my tabs-page:
ionViewDidLoad(){
this.notification.receiveMessage()
}
So, can anyone help me to get the payload when the PWA is closed?
have you tried by initializing firebase in the service worker and using it ,
self.addEventListener("notificationclick", (event) => {
//your code
});
importScripts("https://www.gstatic.com/firebasejs/4.7.0/firebase.js")
importScripts("https://www.gstatic.com/firebasejs/4.7.0/firebase-messaging.js")
// Initialize Firebase
firebase.initializeApp({
'messagingSenderId': 'your sender id'
});
const messaging = firebase.messaging();
messaging.setBackgroundMessageHandler(function(payload) {
//your code
});
this.messaging.onMessage((payload) => {
//your code
});
self.addEventListener('notificationclose', function (event) {
self.registration.getNotifications().then(function (notifications) {
notifications.forEach(function (notification) {
notification.close()
})
});
})
I have tried by using above code in the service worker and it is showing notification when the application is closed , if you want to show the notification when the browser is closed make sure you have enabled the Continue running background apps when Google Chrome is closed option in chrome settings.
Related
I am using Capacitor to generate both the IOS and Android apps (not using Iconic) - this works well, but we are trying to implement IAP (for IOS only at this stage) and cannot figure it out.
I have followed various guides (https://ionicframework.com/docs/native/in-app-purchase-2 and https://purchase.cordova.fovea.cc/ and https://capacitorjs.com/docs/guides/in-app-purchases) but simply cannot get it working with React (not React Native)
Can someone point me in the right direction, or provide sample code?
You didn't describe what is going wrong, but here's a basic configuration that works for me on iOS.
I'm only including the part about the store:
index.tsx
import { IAPProduct, InAppPurchase2 } from '#ionic-native/in-app-purchase-2';
const startStoreEventListeners = () => {
if (isPlatformMobile()) {
document.addEventListener(
'deviceready',
() => {
const store = InAppPurchase2;
// Needed to use IAP + cordova plugins.
// Set debug messages.
// Default.
store.verbosity = store.QUIET;
// store.verbosity = store.DEBUG;
store.register([
{
id: subMonthly,
type: store.PAID_SUBSCRIPTION,
},
{
id: subAnnual,
type: store.PAID_SUBSCRIPTION,
},
]);
// Upon approval, verify the receipt.
store.when(subMonthly).approved((product: IAPProduct) => {
product.verify();
});
store.when(subAnnual).approved((product: IAPProduct) => {
product.verify();
});
// Upon receipt validation, mark the subscription as owned.
store.when(subMonthly).verified((product: IAPProduct) => {
product.finish();
});
store.when(subAnnual).verified((product: IAPProduct) => {
product.finish();
});
// Track all store errors
store.error((err: Error) => {
debugLog('Store Error', JSON.stringify(err));
});
// https://billing-dashboard.fovea.cc/setup/cordova
store.validator =
'https://validator.fovea.cc/v1/validate?appName=secret';
store.refresh();
startIonic();
},
false,
);
} else {
startIonic();
}
};
startStoreEventListeners();
serviceWorker.unregister();
Note that #ionic-native packages are deprecated and need to be converted.
I'm developing a Progressive Web App with React that gets notifications when a new offer has been added to the DB. Everything works fine, I open the web, asks the user to grant permissions to enable notifications, we allow them, install the PWA, run it, add a new offer in the DB, and the a notification with the new offer gets displayed (Chrome + Windows 10).
But the issue is I don't get any notifications if the PWA is not running.. I would have thought the service worker is running in the background even if the PWA is closed. What am I missing?
here is my notifyNewOffer function in my notifications.ts file
function notifyNewOffer(newOffer: Offer) {
if ('serviceWorker' in navigator) {
const options = {
body: newOffer.subheading,
icon: './logo192.png',
image: './static/media/placeholder-offer.1bcbf040.png',
vibrate: [100, 50, 200],
badge: './favicon.ico',
tag: 'new-offers',
renotify: true,
actions: [
{ action: 'confirm', title: 'Check offer', icon: '' },
],
};
navigator.serviceWorker.ready.then(swreg => {
swreg.showNotification(newOffer.heading, options);
});
} else {
console.log('no serviceWorker');
}
}
And this is how I call it:
function addedOfferSubs<T>(setOffers: (offers:any) => void) {
// #ts-ignore
const subscription = API.graphql(graphqlOperation(addedOffer)).subscribe({
next: async (eventData: SubscriptionValue<T>) => {
const newOffer = (eventData.value.data as any).addedOffer;
await indexedDb.createObjectStore('offers', 'id'); // Opens db. Will create the table offers only if it doesnt already exist
await indexedDb.putValue('offers', newOffer); // Adds new offer
// Push notification
notifyNewOffer(newOffer);
// Set offers
const offersData = await getOffersFromIdb();
setOffers(offersData);
},
});
return () => subscription.unsubscribe()
}
Any ideas ?
Thanks very much
In order for notifications to appear when the app isn't open, you'll need to use Web Push as well. Web push allows you to send a notification from your server to device. When the push arrives on the device, it wakes up the service worker, and the notification is shown there.
Instructions for setting up Web Push & Notifications are available at https://developers.google.com/web/fundamentals/push-notifications
We're currently developing a Office UI addin using React. The addin should make a connection with a backend api and authenticate the user using bearer tokens. The backend api is protected by Azure AD.
We based our solution on the example that is offered by Microsoft: https://github.com/OfficeDev/PnP-OfficeAddins/tree/master/Samples/auth/Office-Add-in-Microsoft-Graph-React This uses msal.js for the authentication.
The login dialog is opened like so:
await Office.context.ui.displayDialogAsync(dialogLoginUrl, { height: 40, width: 30 }, result => {
if (result.status === Office.AsyncResultStatus.Failed) {
displayError(`${result.error.code} ${result.error.message}`);
} else {
loginDialog = result.value;
loginDialog.addEventHandler(Office.EventType.DialogMessageReceived, processLoginMessage);
loginDialog.addEventHandler(Office.EventType.DialogEventReceived, processLoginDialogEvent);
}
});
And the following code runs within the dialog:
import { UserAgentApplication } from "msal";
(() => {
// The initialize function must be run each time a new page is loaded
Office.initialize = () => {
const config = {
auth: {
clientId: "",
authority: "",
redirectUri: "https://localhost:3000/login.html",
navigateToLoginRequestUrl: false
},
cache: {
cacheLocation: "localStorage",
storeAuthStateInCookie: false
}
};
const userAgentApp = new UserAgentApplication(config);
const authCallback = (error, response) => {
if (!error) {
if (response.tokenType === "id_token") {
localStorage.setItem("loggedIn", "yes");
} else {
// The tokenType is access_token, so send success message and token.
Office.context.ui.messageParent(JSON.stringify({ status: "success", result: response.accessToken }));
}
} else {
const errorData = `errorCode: ${error.errorCode}
message: ${error.errorMessage}
errorStack: ${error.stack}`;
Office.context.ui.messageParent(JSON.stringify({ status: "failure", result: errorData }));
}
};
userAgentApp.handleRedirectCallback(authCallback);
const request = {
scopes: ["api://..."]
};
if (localStorage.getItem("loggedIn") === "yes") {
userAgentApp.acquireTokenRedirect(request);
} else {
// This will login the user and then the (response.tokenType === "id_token")
// path in authCallback below will run, which sets localStorage.loggedIn to "yes"
// and then the dialog is redirected back to this script, so the
// acquireTokenRedirect above runs.
userAgentApp.loginRedirect(request);
}
};
})();
Unfortunately this doesn't seem lead to a stable addin. The authentication dialog sometimes works as expected, but sometimes it doesn't. In Outlook on macOS it seems to work fine, but in Outlook on Windows the handling of the callback is not always working correctly. Also in the web version of Outlook it doesn't work as expected.
The question is whether someone has a working solution using React and msal.js in a Outlook addin.
I have a SPA PWA React app.
It is installed and running in standalone mode on the mobile device (Android+Chrome).
Let's say the app lists people and then when you click on a person it diplays details using /person route.
Now, I'm sending push notifications from the server and receiving them in the service worker attached to the app. The notification is about a person and I want to open that person's details when the user clicks on the notification.
The question is:
how do I activate the /person route on my app from the service worker
and pass data (e.g. person id, or person object)
without reloading the app
From what I understand, from the service worker notificationclick event handler I can:
focus on the app (but how do I pass data and activate a route)
open an url (but /person is not a physical route, and either way - I want avoid refreshing the page)
You can listen for click event for the Notification which you show to the user. And in the handler, you can open the URL for the corresponding person which comes from your server with push event.
notification.onclick = function(event) {
event.preventDefault();
// suppose you have an url property in the data
if (event.notification.data.url) {
self.clients.openWindow(event.notification.data.url);
}
}
Check these links:
https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerGlobalScope/notificationclick_event
https://developer.mozilla.org/en-US/docs/Web/API/Clients/openWindow
To answer my own question: I've used IndexedDB (can't use localStorage as it is synchronous) to communicate between SW and PWA, though I'm not too happy about it.
This is roughly how my service worker code looks (I'm using idb library):
self.addEventListener('notificationclick', function(event) {
const notif = event.notification;
notif.close();
if (notif.data) {
let db;
let p = idb.openDB('my-store', 1, {
upgrade(db) {
db.createObjectStore(OBJSTORENAME, {
keyPath: 'id'
});
}
}).then(function(idb) {
db = idb;
return db.clear(OBJSTORENAME);
}).then(function(rv) {
return db.put(OBJSTORENAME, notif.data);
}).then(function(res) {
clients.openWindow('/');
}).catch(function(err) {
console.log("Error spawning notif", err);
});
event.waitUntil(p);
}
});
and then, in the root of my react app ie in my AppNavBar component I always check if there is something to show:
componentWillMount() {
let self = this;
let db;
idb.openDB('my-store', 1)
.then(function (idb) {
db = idb;
return db.getAll(OBJSTORENAME);
}).then(function (items) {
if (items && items.length) {
axios.get(`/some-additional-info-optional/${items[0].id}`).then(res => {
if (res.data && res.data.success) {
self.props.history.push({
pathname: '/details',
state: {
selectedObject: res.data.data[0]
}
});
}
});
db.clear(OBJSTORENAME)
.then()
.catch(err => {
console.log("error clearing ", OBJSTORENAME);
});
}
}).catch(function (err) {
console.log("Error", err);
});
}
Have been toying with clients.openWindow('/?id=123'); and clients.openWindow('/#123'); but that was behaving strangely, sometimes the app would stall, so I reverted to the IndexedDB approach.
(clients.postMessage could also be the way to go though I'm not sure how to plug that into the react framework)
HTH someone else, and I'm still looking for a better solution.
I had a similar need in my project. Using your's postMessage tip, I was able to get an event on my component every time a user clicks on service worker notification, and then route the user to the desired path.
service-worker.js
self.addEventListener("notificationclick", async event => {
const notification = event.notification;
notification.close();
event.waitUntil(
self.clients.matchAll({ type: "window" }).then(clientsArr => {
if (clientsArr[0]) {
clientsArr[0].focus();
clientsArr[0].postMessage({
type: "NOTIFICATION_CLICK",
ticketId: notification.tag,
});
}
})
);
});
On your react component, add a new listener:
useEffect(() => {
if ("serviceWorker" in navigator) {
navigator.serviceWorker.addEventListener("message", message => {
if (message.data.type === "NOTIFICATION_CLICK") {
history.push(`/tickets/${message.data.ticketId}`);
}
});
}
}, [history]);
i am doing fcm web notification for the first time andgeting an message as
{ from: "205864560478", collapse_key: "do_not_collapse" }
my code is
messaging.onMessage(function(payload) {
console.log("Message received. ", payload);
// ...
});
and my firebase-messaging-sw.js
const messaging = firebase.messaging();
messaging.setBackgroundMessageHandler(function(payload) {
console.log('[firebase-messaging-sw.js] Received background message ', payload);
// Customize notification here
const notificationTitle = 'Background Message Title';
const notificationOptions = {
body: 'Background Message body.',
icon: '/firebase-logo.png'
};
return self.registration.showNotification(notificationTitle,
notificationOptions);
});
please help . i dont know why i get this error
i have perfectly working for ios and Android.
my web project is in localhost and do not have https
{ from: "205864560478", collapse_key: "do_not_collapse" } is the expected payload and it is not an error.
Also, for development, localhost is exempted from the https condition.
In FCM, if the user is currently viewing your web page and receives a push notification then it will not be shown as push notification (default behavior).
The behavior of messages differs depending on whether the page is in
the foreground (has focus), or in the background, hidden behind other
tabs, or completely closed.
When your app is in the foreground (the user is currently viewing your
web page), you can receive data and notification payloads directly in
the page.
https://firebase.google.com/docs/cloud-messaging/js/receive
If you still want to show a notification when user is currently viewing your web page then you can add push code logic at messaging.onMessage
Example
messaging.onMessage(function (payload) {
console.log('Message received. ', payload);
var options = {
body: 'Background Message body.', // payload.body
icon: '/firebase-logo.png' . // payload.icon
};
var n = new Notification('Notification says',options);
setTimeout(n.close.bind(n), 5000);
});