Problem :I am getting one extra notification when my react-app is in open in background in browser .that extra notification comes with message "This site has been updated in background" .
I am implementing push notification functionality for background notification using firebase cloud messaging.
what I have tried so far. I have tried using returning self.registration.showNotification also
tried with setTimeout of 1sec and then returned self.registration.showNotification through promise.
what are the solution that I don't want .
1.remove onBackgroundMessage itself FCM will trigger automatic
notification.
2. by listening push event . because I have foreground notification
also if I use push event listener and use show-notification init my
foreground notification also converted to background .
Observation : if react-app is not in background mean tab itself is closed than it is working fine but when tab is open and chrome or Edge in background it triggers 1 extra notification with message "This site has been updated in background".
//firebase-messaging-sw.js
// Scripts for firebase and firebase messaging
importScripts('https://www.gstatic.com/firebasejs/9.6.1/firebase-app-compat.js');
importScripts('https://www.gstatic.com/firebasejs/9.6.1/firebase-messaging-compat.js');
const firebaseConfig = {
apiKey: '',
authDomain: '',
projectId: '',
storageBucket: '',
messagingSenderId: '',
appId: '',
measurementId: ''
};
firebase.initializeApp(firebaseConfig);
const messaging = firebase.messaging();
messaging.onBackgroundMessage(function (payload) {
if (payload ) {
const notificationTitle = payload.data.title;
const notificationOptions = {
body: payload.data.body,
data: payload.data.clickEvent
};
return self.registration.showNotification(notificationTitle, notificationOptions);
}
});
Related
I have a problem with my Service Worker.
I'm currently implementing offline functionality with an offline.html site to be shown in case of network failure. I have implemented Navigation Preloads as described here: https://developers.google.com/web/updates/2017/02/navigation-preload#activating_navigation_preload
Here is my install EventListener were skipWaiting() and initialize new cache
const version = 'v.1.2.3'
const CACHE_NAME = '::static-cache'
const urlsToCache = ['index~offline.html', 'favicon-512.png']
self.addEventListener('install', function(event) {
self.skipWaiting()
event.waitUntil(
caches
.open(version + CACHE_NAME)
.then(function(cache) {
return cache.addAll(urlsToCache)
})
.then(function() {
console.log('WORKER: install completed')
})
)
})
Here is my activate EventListener were I feature-detect navigationPreload and enable it. Afterwards I check for old caches and delete them
self.addEventListener('activate', event => {
console.log('WORKER: activated')
event.waitUntil(
(async function() {
// Feature-detect
if (self.registration.navigationPreload) {
// Enable navigation preloads!
console.log('WORKER: Enable navigation preloads')
await self.registration.navigationPreload.enable()
}
})().then(
caches.keys().then(function(cacheNames) {
cacheNames.forEach(function(cacheName) {
if (cacheName !== version + CACHE_NAME) {
caches.delete(cacheName)
console.log(cacheName + ' CACHE deleted')
}
})
})
)
)
})
This is my fetch eventListener
self.addEventListener('fetch', event => {
const { request } = event
// Always bypass for range requests, due to browser bugs
if (request.headers.has('range')) return
event.respondWith(
(async function() {
// Try to get from the cache:
const cachedResponse = await caches.match(request)
if (cachedResponse) return cachedResponse
try {
const response = await event.preloadResponse
if (response) return response
// Otherwise, get from the network
return await fetch(request)
} catch (err) {
// If this was a navigation, show the offline page:
if (request.mode === 'navigate') {
console.log('Err: ',err)
console.log('Request: ', request)
return caches.match('index~offline.html')
}
// Otherwise throw
throw err
}
})()
)
})
Now my Problem:
On my local machine on localhost everything just works as it should. If network is offline the index~offline.html page is delivered to the user. If I deploy to my test server everything works as well as expected, except for a strange error-message in Chrome on normal browsing(not offline mode):
The service worker navigation preload request failed with network error: net::ERR_INTERNET_DISCONNECTED.
I logged the error and the request to get more information
Error:
DOMException: The service worker navigation preload request failed with a network error.
Request:
Its strange because somehow index.html is requested no matter which site is loaded.
Additional Information this is happening in Chrome 89, in chrome 88 everything seems fine(I checked in browserstack). I just saw there was a change in pwa offline detection in Chrome 89...
https://developer.chrome.com/blog/improved-pwa-offline-detection/
anybody has an idea what the problem might be?
Update
I rebuild the problem here so everybody can check it out: https://dreamy-leavitt-bd4f0e.netlify.app/
This error is directly caused by the improved pwa offline detection you linked to:
https://developer.chrome.com/blog/improved-pwa-offline-detection/
The browser fakes an offline context and tries to request the start_url of your manifest, e.g. the index.html specified in your https://dreamy-leavitt-bd4f0e.netlify.app/site.webmanifest
This is to make sure that your service worker is actually returning a valid 200 response in this situation, i.e. the valid cached response for your index~offline.html page.
The error you're asking about specifically is from the await event.preloadResponse part and it apparently can't be suppressed.
The await fetch call produces a similar error but that can be suppressed, just don't console.log in the catch section.
Hopefully chrome won't show this error from preload responses in future when doing offline pwa detection as it's needlessly confusing.
I'm developing a website that supports desktop notifications. I'm utilizing GCM and Azure Notification Hub to send push messages to end users. I followed this codelab tutorial. When testing I have found that the push notifications do get displayed on the screen, but the message that I have written in the payload is not shown in the notification. So I again went through the codelab, and they have mentioned a body key in the showNotification method.
Code
self.addEventListener('push', function(event) {
console.log('Push message', event);
var title = 'Push message';
event.waitUntil(
self.registration.showNotification(title, {
body: 'The Message',
icon: 'images/icon.png',
tag: 'my-tag'
}));
});
They have hardcoded "The Message" in the showNotification function. I don't want to hardcode my message in the function since my message won't be the same always and will vary time to time. I want to know how to make the function take the message in payload and display it. Thanks in advance!
Chrome doesn't support push payloads yet, for now you could use them only in Firefox.
The solution is to request the payload from the server (using fetch) once you get a notification.
Something like this:
self.addEventListener('push', function(event) {
event.waitUntil(
fetch('./getPayload?someUniqueID=' + someUniqueID)
.then(function(response) {
return response.text();
})
.then(function(payload) {
self.registration.showNotification('Title', {
body: payload,
});
})
);
});
Obviously, the response can also be in JSON, so that you can specify everything (title, body, icon, etc.).
You can see an example here: https://serviceworke.rs/push-get-payload.html.
Another possible solution is to use the payload when it's available and use fetch otherwise. This way you can take advantage of the feature in Firefox (and in Chrome once available), and have a fallback for browsers that don't support payloads yet.
There's an example in Mercurius.
i am implementing TAG based push notification in windowsphone8 but my app is not able to register the TAG when i execute following code.
i have tried putting subscribeTag outside onReadyToSubscribe too but i am not getting anything no success , no failure , no nothing.
if (WL.Client.Push) {
WL.Client.Push.onReadyToSubscribe = function() {
WL.Client.Push.subscribeTag("RRNEWS", {
onSuccess: function () {
alert("Tag registered");
},
onFailure: function (e) {
alert("Tag registered failed" + JSON.stringify(e));
}
});
};
}else{
alert("not supported");
}
i have register tag in application-descriptor.xml as follows
<tags>
<tag>
<name>RRNEWS</name>
<description>News</description>
</tag>
</tags>
here is the windowsphone8 entry
<windowsPhone8 version="1.0">
<uuid>6e043ba2-d382-4894-965f-47e08c24bd1e</uuid>
<pushSender/>
</windowsPhone8>
I run a tag based notification sample from Tag Notification github and deployed it on my mobile first server 6.3
when i generated the windowspone8 build and run it into a device , it shows following results
1) isPushSupported -> True
2) isSubscribed -> sample-tag1: false , sample-tag2: false
rest of the buttons are disabled
1) Subscribe to sample-tag2
2) Unsubscribe from sample-tag1
3) Unsubscribe from sample-tag2
then i tried removing disabled properties from html tag and try pressing
subscribe to sample-tag2 but nothing is happening.
Change is adapter code
function sendTagNotificationToWindows(applicationId, notificationText,notificationTags){
var notificationOptions = {};
var tags = notificationTags.split(",");
var notificationOptions = {};
var notification = WL.Server.createDefaultNotification(notificationText, 10);
notification.MPNS.raw = {};
notification.MPNS.raw.payload = {"custom":"data"} ;
notificationOptions.message = {};
notificationOptions.target = {};
notificationOptions.message.alert = JSON.stringify(notification);
notificationOptions.target.tagNames = tags;
// i have tried it with a notificationOptions too
WL.Server.sendMessage(applicationId, notification);
return {
result : "Notification sent to users subscribed to the tag(s): '" + JSON.stringify(notification) + "'."
};
}
Error
when i pass only notification param it throws following error
"Push Works Bad Request: FPWSE0005E: Invalid value was provided. Check the 'message' parameter value."
The Tag notifications sample project does not take into account the Windows Phone 8 environment. Here's what you need to add to the sample:
application-descriptor.xml
After adding the Windows Phone 8 environment, add an empty pushSender tag:
<windowsPhone8 version="1.0">
<uuid>AUTOGENERATED-GUID-NUMBER-HERE</uuid>
<pushSender/>
</windowsPhone8>
PushAdater-impl.js
Add the following to the function used for sending the notification.
For example, something like this:
function sendTagNotification(applicationId, notificationText, notificationTags) {
var tags = notificationTags.split(",");
var notificationOptions = {};
notificationOptions.message = {};
notificationOptions.message.alert = notificationText;
notificationOptions.target = {};
notificationOptions.target.tagNames = tags;
notificationOptions.settings = {};
notificationOptions.settings.mpns = {};
notificationOptions.settings.mpns.toast = {};
notificationOptions.settings.mpns.toast.text1 = "New notification";
notificationOptions.settings.mpns.toast.text2 = "You have a new notification";
notificationOptions.settings.mpns.toast.param = "/MainPage.xaml?value1=54321";
WL.Server.sendMessage(applicationId, notificationOptions);
return {
result : "Notification sent to users subscribed to the tag(s): '" + notificationTags + "'."
};
}
notificationOptions.settings.mpns.toast is needed so that when the application is closed (quit) or in the background, "toast" notification type will appear in the device.
Tapping on a toast notification launches the application. optional "param" field is used to pass on values that will be displayed once the application launches. The values set in the "param" of the toast can be retrieved natively and displayed within the application.
Additional clarifications:
When the app is open the raw notification type and the props and payload values can be seen on screen if used.
When the app is in the background and tapping the notification there are two notification types that can be receive: toast or tile. Both open the application.
When the app is in the background and tapping the app icon:
This means clicking the application tile. Application launches
When the app is closed and tapping the notification: Tapping on the toast launches the application. Tapping on the application tile launches the application.
When the app is closed and tapping the app icon: This means clicking the application tile. Application launches
I recommend to read the following MS documentation to understand what are the available notification types (Toast, Tile and Raw) and when to use them: https://msdn.microsoft.com/en-us/library/hh221549.aspx
Then search for "MPNS", "toast", "tile" or "raw" in the following user documentation topic (for onMessage) to see an example of how to set them up in your application: http://www-01.ibm.com/support/knowledgecenter/SSHS8R_7.1.0/com.ibm.worklight.apiref.doc/html/refjavascript-server/html/WL.Server.html?cp=SSHS8R_7.1.0%2F10-1-0-1-6&lang=en
While implementing the chrome push notification, we were fetching the latest change from our server. While doing so, the service-worker is showing an extra notification with the message
This site has been updated in the background
Already tried with the suggestion posted here
https://disqus.com/home/discussion/html5rocks/push_notifications_on_the_open_web/
But could not find anything useful till now. Is there any suggestion ?
Short Answer: You should use event.waitUntil and pass a promise to it, which returns showNotification eventually. (if you have any other nested promises, you should also return them.)
I was expriencing the same issue but after a long research I got to know that this is because delay happen between PUSH event and self.registration.showNotification(). I only missed return keyword before self.registration.showNotification()`
So you need to implement following code structure to get notification:
var APILINK = "https://xxxx.com";
self.addEventListener('push', function(event) {
event.waitUntil(
fetch(APILINK).then(function(response) {
return response.json().then(function(data) {
console.log(data);
var title = data.title;
var body = data.message;
var icon = data.image;
var tag = 'temp-tag';
var urlOpen = data.URL;
return self.registration.showNotification(title, {
body: body,
icon: icon,
tag: tag
})
});
})
);
});
Minimal senario:
self.addEventListener('push', event => {
const data = event.data.json();
event.waitUntil(
// in here we pass showNotification, but if you pass a promise, like fetch,
// then you should return showNotification inside of it. like above example.
self.registration.showNotification(data.title, {
body: data.content
})
);
});
I've run into this issue in the past. In my experience the cause is generally one of three issues:
You're not showing a notification in response to the push
message. Every time you receive a push message on the device, when
you finish handling the event a notification must be left visible on
the device. This is due to subscribing with the userVisibleOnly:
true option (although note this is not optional, and not setting it
will cause the subscription to fail.
You're not calling event.waitUntil() in response to handling the event. A promise should be passed into this function to indicate to the browser that it should wait for the promise to resolve before checking whether a notification is left showing.
For some reason you're resolving the promise passed to event.waitUntil before a notification has been shown. Note that self.registration.showNotification is a promise and async so you should be sure it has resolved before the promise passed to event.waitUntil resolves.
Generally as soon as you receive a push message from GCM (Google Cloud Messaging) you have to show a push notification in the browser. This is mentioned on the 3rd point in here:
https://developers.google.com/web/updates/2015/03/push-notificatons-on-the-open-web#what-are-the-limitations-of-push-messaging-in-chrome-42
So it might happen that somehow you are skipping the push notification though you got a push message from GCM and you are getting a push notification with some default message like "This site has been updated in the background".
This works, just copy/paste/modify. Replace the "return self.registration.showNotification()" with the below code. The first part is to handle the notification, the second part is to handle the notification's click. But don't thank me, unless you're thanking my hours of googling for answers.
Seriously though, all thanks go to Matt Gaunt over at developers.google.com
self.addEventListener('push', function(event) {
console.log('Received a push message', event);
var title = 'Yay a message.';
var body = 'We have received a push message.';
var icon = 'YOUR_ICON';
var tag = 'simple-push-demo-notification-tag';
var data = {
doge: {
wow: 'such amaze notification data'
}
};
event.waitUntil(
self.registration.showNotification(title, {
body: body,
icon: icon,
tag: tag,
data: data
})
);
});
self.addEventListener('notificationclick', function(event) {
var doge = event.notification.data.doge;
console.log(doge.wow);
});
I was trying to understand why Chrome has this requirement that the service worker must display a notification when a push notification is received. I believe the reason is that push notification service workers continue to run in the background even after a user closes the tabs for the website. So in order to prevent websites from secretly running code in the background, Chrome requires that they display some message.
What are the limitations of push messaging in Chrome?
...
You have to show a notification when you receive a push message.
...
and
Why not use Web Sockets or Server-Sent Events (EventSource)?
The advantage of using push messages is that even if your page is closed, your service worker will be woken up and be able to show a notification. Web Sockets and EventSource have their connection closed when the page or browser is closed.
If you need more things to happen at the time of receiving the push notification event, the showNotification() is returning a Promise. So you can use the classic chaining.
const itsGonnaBeLegendary = new Promise((resolve, reject) => {
self.registration.showNotification(title, options)
.then(() => {
console.log("other stuff to do");
resolve();
});
});
event.waitUntil(itsGonnaBeLegendary);
i was pushing notification twice, once in the FCM's onBackgroundMessage()
click_action: "http://localhost:3000/"
and once in self.addEventListener('notificationclick',...
event.waitUntil(clients.matchAll({
type: "window"
}).then...
so i commented click_action, ctrl+f5 to refresh browsers and now it works normal
I have an extension to the chrome devtools panel. I can send messages to the page using chrome.devtools.inspectedWindow.eval ... but how do I receive messages in the dev panel? Specifically, I need my devpanel to hook into events that happen on the page. I can't get it to listen to events on my content script, nor the background page.
I've tried chrome.extension.sendMessage in the content script, along with chrome.extension.onMessage.addListener in the dev panel script. But sendMessage complains with Port error: Could not establish connection. Receiving end does not exist.
The issue persists with long-lived connections:
In content script or background page:
var port = chrome.extension.connect({name: "test"});
port.postMessage({msg: "testing"});
In dev tools panel javascript:
chrome.extension.onConnect.addListener(function(port) {
port.onMessage.addListener(function(msg) {
// never gets here
});
});
How can I listen for events that are triggered in my content script-- in my dev tool panel? A diagram like this from Firefox's Add-On SDK would be great: https://addons.mozilla.org/en-US/developers/docs/sdk/latest/static-files/media/content-scripting-overview.png
The goal is to create a channel ("port") for communication. It does not matter how the port is created, as long as the connection is correctly maintained.
The devtools script has to initiate the port, because the background script does not know when a devtools panel is created.
Here's a basic example, which shows a bidirectional communication method:
devtools.js
chrome.devtools.panels.create('Test', '/icon.png', '/panel.html', function(extensionPanel) {
var _window; // Going to hold the reference to panel.html's `window`
var data = [];
var port = chrome.runtime.connect({name: 'devtools'});
port.onMessage.addListener(function(msg) {
// Write information to the panel, if exists.
// If we don't have a panel reference (yet), queue the data.
if (_window) {
_window.do_something(msg);
} else {
data.push(msg);
}
});
extensionPanel.onShown.addListener(function tmp(panelWindow) {
extensionPanel.onShown.removeListener(tmp); // Run once only
_window = panelWindow;
// Release queued data
var msg;
while (msg = data.shift())
_window.do_something(msg);
// Just to show that it's easy to talk to pass a message back:
_window.respond = function(msg) {
port.postMessage(msg);
};
});
});
Now, the panel is capable of sending/receiving messages over a port. The panel's script (external script file, because of the CSP) may look like:
panel.js
function do_something(msg) {
document.body.textContent += '\n' + msg; // Stupid example, PoC
}
document.documentElement.onclick = function() {
// No need to check for the existence of `respond`, because
// the panel can only be clicked when it's visible...
respond('Another stupid example!');
};
Now, the background page's script:
background.js
var ports = [];
chrome.runtime.onConnect.addListener(function(port) {
if (port.name !== "devtools") return;
ports.push(port);
// Remove port when destroyed (eg when devtools instance is closed)
port.onDisconnect.addListener(function() {
var i = ports.indexOf(port);
if (i !== -1) ports.splice(i, 1);
});
port.onMessage.addListener(function(msg) {
// Received message from devtools. Do something:
console.log('Received message from devtools page', msg);
});
});
// Function to send a message to all devtools.html views:
function notifyDevtools(msg) {
ports.forEach(function(port) {
port.postMessage(msg);
});
}
To test, simply run notifyDevtools('Foo'); on the background page (e.g. via the console). In this demo, the message will be sent to all devtools. Upon receipt, the devtools panel will contain the received message.
Put the extension together using:
manifest.json
{
"name": "Test",
"manifest_version": 2,
"version": "1",
"devtools_page": "devtools.html",
"background":{"scripts":["background.js"]}
}
panel.html
<script src="panel.js"></script> <!-- Doctype etc not added for conciseness-->
devtools.html
<script src="devtools.js"></script>
See also
How to modify content under a devtools panel in a Chrome extension?
chrome.devtools API
Message passing: Long-lived connections
Content Security Policy in Chrome extensions ("Inline JavaScript (...) will not be executed. This restriction bans both inline <script> blocks and inline event handlers.")