service worker file:
registration.onupdatefound = () => {
const installingWorker = registration.installing;
if (installingWorker == null) {
return;
}
installingWorker.onstatechange = () => {
if (installingWorker.state === 'installed') {
if (navigator.serviceWorker.controller) {
// At this point, the updated precached content has been fetched,
// but the previous service worker will still serve the older
// content until all client tabs are closed.
console.log(
'New content is available and will be used when all ' +
'tabs for this page are closed.
);
// Execute callback
registration.postMessage({action: 'skipWaiting'})
if (config && config.onUpdate) {
config.onUpdate(registration);
}
} else {
// At this point, everything has been precached.
// It's the perfect time to display a
// "Content is cached for offline use." message.
console.log('Content is cached for offline use.');
// Execute callback
if (config && config.onSuccess) {
config.onSuccess(registration);
}
}
}
};
};
}`
self.addEventListener('message', function(event) {
if (event.data.action === 'skipWaiting') {
self.skipWaiting();
}
});
//listening in app.js controller change event
// reload once when the new Service Worker starts activating
var refreshing;
navigator.serviceWorker.addEventListener('controllerchange',
function() {
if (refreshing) return;
refreshing = true;
window.location.reload();
}
);
what it actually does:
If it finds any update, sending a postMessage as skipWating
2.Once the message recieved by listener , it calls the skipWaiting()
3.then controllerchange event gets called where we refreshing the page.
Why it gets into infinite loop of reload()
Your first bit of code, everything under registration.onupdatefound, shouldn't be in the service worker file. It should be in your web app's window context.
The bit that should be in your service worker file is:
self.addEventListener('message', function(event) {
if (event.data.action === 'skipWaiting') {
self.skipWaiting();
}
}
There's documentation for another "recipe" that accomplishes this at https://developers.google.com/web/tools/workbox/guides/advanced-recipes#offer_a_page_reload_for_users. It uses the workbox-window library to simplify things a bit.
Related
I am trying to create a Chrome Extension but I am having som trouble.
I want to use thing function to listen to updates in my Firebase Database:
firebase
.database()
.ref(`${URLOfCurrentTab}/messages`)
.endAt()
.limitToLast(1)
.on("child_added", function (snapshot) {)}
So I have added this function in my background.js file so i can consistently listen for updates, to do that I need the current URL of the active tab. But the problem is that the firebase "function" runs before the URL is retrieved.
This is how I retrieve the URL. I have added these functions in the background.js file just above the firebase stuff:
let activeTabId, lastUrl, lastTitle;
function getTabInfo(tabId) {
console.log("first");
chrome.tabs.get(tabId, function (tab) {
if (lastUrl != tab.url || lastTitle != tab.title) {
console.log((lastUrl = tab.url));
lastUrl = tab.url;
}
});
}
// When tab is clicked on
chrome.tabs.onActivated.addListener(function (activeInfo) {
console.log("second");
getTabInfo((activeTabId = activeInfo.tabId));
});
// When update in already clicked tab happens
chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
console.log("third");
if (activeTabId == tabId) {
getTabInfo(tabId);
}
});
Any ideas on what to do?
What I want to do:
opening an popup and send the postMessage when the popup is ready.
Problem:
I ran into Race Condition which the popup is not ready but the message is sent. I tried to listen to the "INIT" message and in the popup send back message. But the problem is when network latency or some slow computer will not receive the initial message.
The setTimeout obviously not a good solution
Code I have problem with:
Parent Window
const windowFeatures = "height=800,width=1280,toolbar=1,menubar=1,location=1";
const printWindow = window.open("/print", "Print_Me", windowFeatures);
setTimeout(() => {printWindow.postMessage({printPageStatus: "INIT"}, window.origin)}, 1000)
window.addEventListener("message", (event) => {
if(event.origin !== window.origin) return;
if(event.data.printPageStatus === "READY") {
printWindow.postMessage({message: "from parent", window.origin);
return;
}
});
The popup window
constructor() {
window.addEventListener("message", event => {
if(event.origin !== window.origin) return;
if(event.data.printPageStatus === "INIT")
this.sendReadyConfirmation(event);
if(event.data.message === "from parent") {
this.processMessages(event.data);
}
}, false);
}
sendReadyConfirmation(e): void {
e.source.postMessage({printPageStatus: "READY"}, e.origin);
}
Thank you
What you need to do is send the message when the window has loaded successfully :
const printWindow = window.open("/print", "Print_Me", windowFeatures);
printWindow.onload = () => {
printWindow.postMessage({printPageStatus: "INIT"}, window.origin)
};
I want to disconnect all my users when the tab is closing or the browser is getting closed, so far so good. But when I refresh the page, all my users get disconnected too, this should not happen on refresh. Is it possible to avoid to execute this event on refresh? I saw some users doing with localStorage, but I still didn't get the point.
componentDidMount() {
this.beforeUnloadListener();
}
beforeUnloadListener = () => {
window.addEventListener("beforeunload", (ev) => {
ev.preventDefault();
// code to logout user
});
};
The way beforeunload works, you can not differentiate weather it's a page refresh or a browser close. beforeunload it is a quite confusing event avoid using this.
Hence for cases where you are dealing with the session you should use session storage. The sessionStorage object stores data for only one session (the data is deleted when the browser tab is closed).
Have done this on react application and its work for me on index.html file write this in script tag.
<script type="text/javascript">
window.addEventListener("beforeunload", (event) => {
window.localStorage.isMySessionActive = "true";
});
window.onunload = function (e) {
const newTabCount = localStorage.getItem("tabsOpen");
if (newTabCount !== null) {
localStorage.setItem("tabsOpen", newTabCount - 1);
}
};
</script>
Then go on main file and write this code.
useEffect(() => {
// define increment counter part
const tabsOpen = localStorage.getItem("tabsOpen");
if (tabsOpen == null) {
localStorage.setItem("tabsOpen", 1);
} else {
localStorage.setItem("tabsOpen", parseInt(tabsOpen) + parseInt(1));
}
// define decrement counter part
window.onunload = function (e) {
const newTabCount = localStorage.getItem("tabsOpen");
if (newTabCount !== null) {
localStorage.setItem("tabsOpen", newTabCount - 1);
}
};
if (performance.navigation.type == performance.navigation.TYPE_RELOAD) {
window.localStorage.isMySessionActive = "false";
} else {
const newTabCount2 = localStorage.getItem("tabsOpen");
let value = localStorage.getItem("isMySessionActive");
if (value == "true") {
if (newTabCount2 - 1 == 0) {
localStorage.clear();
window.localStorage.isMySessionActive = "false";
} else {
window.localStorage.isMySessionActive = "false";
}
}
}
}, []);
Looks like Service Worker runs in a worker context and has no access to the DOM. However, once Service Worker installed, I want my users to know that the app will now work offline. How can I do that?
When the Service Worker is in activated state, this is the perfect time to display the toast 'Content is cached for offline use'. Try something below code while registering your service worker.
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/service-worker.js').then(function(reg) {
// updatefound is fired if service-worker.js changes.
reg.onupdatefound = function() {
var installingWorker = reg.installing;
installingWorker.onstatechange = function() {
switch (installingWorker.state) {
case 'installed':
if (navigator.serviceWorker.controller) {
// At this point, the old content will have been purged and the fresh content will
// have been added to the cache.
// It's the perfect time to display a "New content is available; please refresh."
// message in the page's interface.
console.log('New or updated content is available.');
} else {
// At this point, everything has been precached.
// It's the perfect time to display a "Content is cached for offline use." message.
console.log('Content is now available offline!');
}
break;
case 'redundant':
console.error('The installing service worker became redundant.');
break;
}
};
};
}).catch(function(e) {
console.error('Error during service worker registration:', e);
});
}
After testing #Prototype Chain's answer above, I wanted to use named functions as opposed to nested anonymous functions as event handlers to make code more pleasant to look at for my taste, and hopefully easier to understand later/for others.
But only after spending some time sorting docs, I managed to listen correct events on correct objects. So sharing my working example here in hopes of saving someone else from tedious process.
// make sure that Service Workers are supported.
if (navigator.serviceWorker) {
navigator.serviceWorker.register('/sw.js')
.then(function (registration) {
console.log("ServiceWorker registered");
// updatefound event is fired if sw.js changed
registration.onupdatefound = swUpdated;
}).catch(function (e) {
console.log("Failed to register ServiceWorker", e);
})
}
function swUpdated(e) {
console.log('swUpdated');
// get the SW which being installed
var sw = e.target.installing;
// listen for installation stage changes
sw.onstatechange = swInstallationStateChanged;
}
function swInstallationStateChanged(e) {
// get the SW which being installed
var sw = e.target;
console.log('swInstallationStateChanged: ' + sw.state);
if (sw.state == 'installed') {
// is any sw already installed? This function will run 'before' 'SW's activate' handler, so we are checking for any previous sw, not this one.
if (navigator.serviceWorker.controller) {
console.log('Content has updated!');
} else {
console.log('Content is now available offline!');
}
}
if (sw.state == 'activated') {
// new|updated SW is now activated.
console.log('SW is activated!');
}
}
I'm writing a chat webapp that needs to be able to trigger desktop push notifications through the notifications API: https://developer.mozilla.org/en-US/docs/Web/API/Notifications_API
Unfortunately, it seems that the notifications don't show up until I flush it all out by making another notification with this fiddle: https://jsfiddle.net/yoshi6jp/Umc9A/
This is the code that I am using:
function triggerDesktopNotification() {
function makeNotification() {
var notification = new Notification('AppName', {
body: 'You have a new message!',
icon: '/favicon.ico',
});
notification.onclick = () => {
notification.close();
};
}
if (Notification.permission === 'granted') {
makeNotification();
}
else if (Notification.permission !== 'denied') {
Notification.requestPermission(function (permission) {
if (permission === 'granted') {
makeNotification();
}
});
}
}
I can confirm that the code is executing properly by placing console.log() immediately after the new Notification call. Interestingly, if I put an alert() there instead, the notification shows up when I see the alert box (after navigating back into my tab).
If I understood you right ;
Alert interrupts program stack where it placed i think.Why dont you try to fire it async with setTimeout function like that ?
setTimeout( function(){
alert("asd");
})
edited js fiddle here