How to schedule periodic task in PWA service worker - notifications

I would like to schedule a periodic task in my PWA, then I can display notifications at some specific time each day.
I tried the Notification API: https://web.dev/notification-triggers/, it seems working. However, according to the document, it still requires to setInterval in the App's page context, e.g
setInterval(() => navigator.serviceWorker.getRegistration().then(reg=>{
if (!reg) {
return
}
reg.showNotification('test', {
tag: 'test-tag',
body: 'This notification was scheduled 1min ago',
showTrigger: new TimestampTrigger(new Date().getTime() + 60 * 1000),
});
}), 60 * 1000)
It is not my ideal solution, if I close my app, the notifications will not be triggered.
Then I tried the periodicSync API: https://web.dev/periodic-background-sync/, e.g
await registration.periodicSync.register('periodic-notification', {
minInterval: 60*1000
});
according to my test, the service worker can receive the periodicSync event if I manually trigger them in dev-tool->Application->Service Workers->Periodic Sync button. But what I am expecting is getting the periodicSync event every 1min. According to the doc, seems it is related with the site-engagement, then it is not a real periodic task.
Any ideas to resolve this issue?
Thank you!

Related

Network-polling in Cypress

I have a webapp that processes some data in the background which can take quite some time (~5 minutes). The status of the task can be checked using a simple GET request.
I would like Cypress to send said get request, then wait in case the response does not contain the proper data and repeat this process until a timeout is reached or a maximum number of retries was performed. In case I get the proper reply, I would like to stop retrying and keep on going with my tests.
Does anybody know how that can be achieved?
You can either used recurse function or use cypress-recuse which allows some nifty configs.
import { recurse } from 'cypress-recurse'
recurse ({
() => {
return cy.request('https://www.your-url.com/')
.its('response')
},
(response) => {
// whatever checks need to complete before proceeding
},
{
// can alter configurations
timeout: 30_000,
delay: 3_000,
limit: 7
}
)

Check for new messages on react native

I'm doing a react native chat app. I'm having problems on the chatg screen to get the messages from other users...
this.intervalID = setInterval(() => {
this.setState({ count: this.state.count + 1 });
}, 1000);
Currently I have a setInterval every second to check for new messages, but that makes the app go really slow also the server is also suffering.
Is there a better way of doing that?
Thanks
Instead of checking new message every second you can you socket.io or another web socket service

Is it possible to log a firebase event inside Headless js?

I'm trying to log a firebase event when a user is receiving a remote push notification when the app is in the background/killed. My notification displays juste fine but I can't log analytics events.
I have tried initializing my firebase app with firebase.initializeApp(config), not knowing if it was initialized in a headless js task but it didn't seem to make a difference.
In-app events log just fine using the Debug View in the firebase console, as well as the notification_receive one automatically
export default async (message: RemoteMessage) => {
const localNotification = new firebase.notifications.Notification()
// setting notification props
const displayNotification = await firebase.notifications().displayNotification(localNotification);
firebase.analytics().logEvent('notification_test', { test: 'test123' }); // this doesn't work
return Promise.resolve(displayNotification);
};
Is there a way to log an event here?
Also, any information on how Headless js works (besides official doc that I've already read of course) would be appreciated.
Thank you!

pouchdb + vuex update live with changes

I have an app that takes updates into VUEX store and syncs those change from pouchdb to couchdb. Which is great but now I need to have two clients connected and see the change in near realtime.
So I have the https://pouchdb.com/guides/changes.html API which I can use to listen for changes to the DB and when that happens call a action which mutates the vuex state on Client 2. Code below.
However the bit I cannot seem to work out is this code is not just listening all the time ? So where should I put this in Vue to ensure that it hears any changes. I can call it when I make a state change and I see that it hears the change but ofcourse I want to trigger a state change on client 2, without them having to make change. Do I need a timer ? The pouch docs seem to suggest this changes api should be able to update UI based on a change to the data, which I can probably call with a button press to check for changes ...but I want to listen in near realtime ?
pouchdb
.changes({
since: 'now',
include_docs: true
})
.on('change', function(change) {
// received a change
store.commit('CHANGE_STATE', change.doc.flavour)
})
.on('error', function(err) {
// handle errors
console.log(err)
})
Your explanation is a bit fuzzy in that you talk about client 2 without ever mentioning client 1. I assume client 2 is a passive listener and client 1 is where data is changed. If I remember correctly from when I was building my Vue / PouchDB project last year I looked into how to coordinate the Store and the Database, and then thought, "Why bother? They're just two kinds of local storage". As long as changes in client 1 replicate to your Couch server and client 2 detects those server side changes and writes them into reactive variables, they'll propagate to the UI.
I used replicate.to() for storing client-side changes and replicate.from() to detect server-side changes. The replicate() functions have their own timer, constantly monitoring the changes queue, so you don't need to roll your own.
This is what I ended up doing !
actions: {
SYNC_DB() {
// do one way, one-off sync from the server until completion
pouchdb.replicate.from(remote).on('complete', function(info) {
// then two-way, continuous, retriable sync
pouchdb
.sync(remote, { live: true, retry: true })
.on('change', function(info) {
store.commit('CHANGE_STATE', info.change.docs[0].flavour)
})
.on('paused', function(err) {
// replication paused (e.g. replication up to date, user went offline)
})
.on('active', function() {
// replicate resumed (e.g. new changes replicating, user went back online)
})
.on('denied', function(err) {
// a document failed to replicate (e.g. due to permissions)
})
.on('complete', function(info) {
// handle complete
})
.on('error', function(err) {
// handle error
})
})
},

Service Worker - Wait for clients.openWindow to complete before postMessage

I am using service worker to handle background notifications. When I receive a message, I'm creating a new Notification using self.registration.showNotification(title, { icon, body }). I'm watching for the click event on the notification using self.addEventListener('notificationclick', ()=>{}). On click I'm checking to see if any WindowClient is open, if it is, I'm getting one of those window clients and calling postMessage on it to send the data from the notification to the app to allow the app to process the notification. Incase there is no open window I'm calling openWindow and once that completes I'm sending the data to that window using postMessage.
event.waitUntil(
clients.matchAll({ type: 'window' }).then((windows) => {
if (windows.length > 0) {
const window = windows[0];
window.postMessage(_data);
window.focus();
return;
}
return clients.openWindow(this.origin).then((window) => {
window.postMessage(_data);
return;
});
})
);
The issue I am facing is that the postMessage call inside the openWindow is never delivered. I'm guessing this is because the postMessage call on the WindowClient happens before the page has finished loading, so the eventListener is not registered to listen for that message yet? Is that right?
How do I open a new window from the service worker and postMessage to that new window.
I stumble this issue as well, using timeout is anti pattern and also might cause delay larger then the 10 seconds limit of chrome that could fail.
what I did was checking if I need to open a new client window.
If I didn't find any match in the clients array - which this is the bottle neck, you need to wait until the page is loaded, and this can take time and postMessage will just not work.
For that case I created in the service worker a simple global object that is being populated in that specific case for example:
const messages = {};
....
// we need to open new window
messages[randomId] = pushData.message; // save the message from the push notification
await clients.openWindow(urlToOpen + '#push=' + randomId);
....
In the page that is loaded, in my case React app, I wait that my component is mounted, then I run a function that check if the URL includes a '#push=XXX' hash, extracting the random ID, then messaging back to the service worker to send us the message.
...
if (self.location.hash.contains('#push=')) {
if ('serviceWorker' in navigator && 'Notification' in window && Notification.permission === 'granted') {
const randomId = self.locaiton.hash.split('=')[1];
const swInstance = await navigator.serviceWorker.ready;
if (swInstance) {
swInstance.active.postMessage({type: 'getPushMessage', id: randomId});
}
// TODO: change URL to be without the `#push=` hash ..
}
Then finally in the service worker we add a message event listener:
self.addEventListener('message', function handler(event) {
if (event.data.type === 'getPushMessage') {
if (event.data.id && messages[event.data.id]) {
// FINALLY post message will work since page is loaded
event.source.postMessage({
type: 'clipboard',
msg: messages[event.data.id],
});
delete messages[event.data.id];
}
}
});
messages our "global" is not persistent which is good, since we just need this when the service worker is "awaken" when a push notification arrives.
The presented code is pseudo code, to point is to explain the idea, which worked for me.
clients.openWindow(event.data.url).then(function(windowClient) {
// do something with the windowClient.
});
I encountered the same problem. My error was that I registered event handler on the window. But it should be registered on service worker like this:
// next line doesn't work
window.addEventListener("message", event => { /* handler */ });
// this one works
navigator.serviceWorker.addEventListener('message', event => { /* handler */ });
See examples at these pages:
https://developer.mozilla.org/en-US/docs/Web/API/Clients
https://developer.mozilla.org/en-US/docs/Web/API/Client/postMessage
UPD: to clarify, this code goes into the freshly opened window. Checked in Chromium v.66.