Chrome Push Notification Updating Service-Worker.js - javascript

I am using Google Chrome Push Notification in my site.
Currently I have more than 1,00,000 subscribed users.
I'm facing this following issue.
- My users started using the notifications.
- I need to change the logic of the service-worker but could not able update it.
- I have not given any cache based installation with my previous Service-Wroker.js
- I have not used any fetch event with the previous Service-Worker.js
Changes done in new Service-Worker.js
- Landing URL (clickUrl) variable is added into the self.addEventListener function
My Existing Service-Wroker.js
'use strict';
var port;
var pushMessage;
var clickUrl;
var imgUrl;
self.addEventListener('push', function(event) {
var obj = event.data;
pushMessage = event.data ? event.data.text() : '';
var pushData = pushMessage.split('####');
clickUrl = pushData[2];
imgUrl = pushData[1];
if (port) {
port.postMessage(pushMessage);
}
event.waitUntil(self.registration.showNotification(pushData[3], {
requireInteraction: true,
body: pushData[0],
icon: pushData[1]
}));
});
self.addEventListener('notificationclick', function(event) {
if (Notification.prototype.hasOwnProperty('data')) {
event.notification.close();
event.waitUntil(clients.openWindow(clickUrl));
}
});
self.onmessage = function(e) {
port = e.ports[0];
if (pushMessage) {
port.postMessage(pushMessage);
}
};
The new / updated Service-Worker.js [Changes I need to update / implement]
'use strict';
var port;
var pushMessage;
var clickUrl;
var imgUrl;
self.addEventListener('push', function(event) {
var obj = event.data;
pushMessage = event.data ? event.data.text() : '';
var pushData = pushMessage.split('####');
clickUrl = pushData[2];
imgUrl = pushData[1];
if (port) {
port.postMessage(pushMessage);
}
event.waitUntil(self.registration.showNotification(pushData[3], {
requireInteraction: true,
body: pushData[0],
icon: pushData[1],
data:{
url : clickUrl
}
}));
});
self.addEventListener('notificationclick', function(event) {
var landingUrl = event.notification.data.url;
if (Notification.prototype.hasOwnProperty('data')) {
event.notification.close();
event.waitUntil(clients.openWindow(landingUrl));
}
});
self.onmessage = function(e) {
port = e.ports[0];
if (pushMessage) {
port.postMessage(pushMessage);
}
};
self.addEventListener('install', function(event) {
console.log('[ServiceWorker] Installed version', version);
event.waitUntil(
caches.open('my-cache').then(function(cache) {
// Important to `return` the promise here to have `skipWaiting()`
// fire after the cache has been updated.
return cache.addAll([/* file1.jpg, file2.png, ... */]);
}).then(function() {
// `skipWaiting()` forces the waiting ServiceWorker to become the
// active ServiceWorker, triggering the `onactivate` event.
// Together with `Clients.claim()` this allows a worker to take effect
// immediately in the client(s).
return self.skipWaiting();
})
);
});
// Activate event
// Be sure to call self.clients.claim()
self.addEventListener('activate', function(event) {
// `claim()` sets this worker as the active worker for all clients that
// match the workers scope and triggers an `oncontrollerchange` event for
// the clients.
return self.clients.claim();
});

An update is triggered:
On navigation to an in-scope page.
On functional events such as push and sync, unless there's been an update check within the previous 24 hours.
On calling .register() only if the service worker URL has changed.
more here https://developers.google.com/web/fundamentals/instant-and-offline/service-worker/lifecycle

Related

Why is my form is POSTING multiple times and Socket emitting multiple events?

I have a form that starts a stream via router.post() when start
button is clicked. When start button is clicked.
When stop button is clicked, i emit a socket event which stops the
streaming from server.
I use on_stream boolean to set true/false flags for starting/stopping stream.
PROBLEM: When the start/stop button is clicked for FIRST time, the stream starts and stops correctly. The second time, when start button
is clicked the router.post() is posting TWICE to server. And when STOP
button is clicked the socket event emits twice.
From this point, start/stop buttons trigger exponentially multiple post requests (Starting) and socket events (stopping stream). Crashing the code...
Console after starting and stopping second time (multiple post and socket events)
Stoping Stream...
SOCKET DEF: true
Closing stream...
close stream: false
startz undefined
POST DEF: false
startStream DEF: true
starting stream...
POST / 200 12.608 ms - 4
startz undefined
POST DEF: true
startStream DEF: true
starting stream...
Browser Console (events triggered multiple times): https://imgur.com/a/RDGR9mm
index.js
module.exports = function(io) {
let _stream = {};
let on_stream = false;
router.post('/', async (req, res) => {
// console.log("raw obj " + req.body.searchTerm);
console.log("startz " + req.body.startBtn);
console.log("POST DEF:", on_stream);
startStream(req.body.searchTerm);
res.send(on_stream);
});
io.on('connection', function(socket) {
console.log('a user connected index outside routerrr');
// Listen to stop Button being clicked
socket.on('stopStream', function() {
console.log("Stoping Stream...");
if(on_stream) {
console.log("SOCKET DEF:", on_stream);
closeStream();
}
});
});
// start stream
function startStream(term) {
// return new Promise((resolve, reject) => {
// console.log("TERM _" +term);
client.stream('statuses/filter', { track: term }, function(stream) {
_stream = stream;
on_stream = true;
console.log("startStream DEF:", on_stream);
console.log("starting stream...");
_stream.on('data', function(tweet) {
console.log(tweet.text + "Streaming");
// socket.emit('tweet', tweet.text);
});
_stream.on('error', function(error) {
console.log("erorr:: " + error);
throw error;
});
});
}
function closeStream() {
console.log('Closing stream...');
_stream.destroy();
on_stream = false;
console.log("close stream: ", on_stream );
}
return router;
}
script.js
function startSearchForm() {
$("#startBtn").on('click', function() {
let form = $("#search-form");
let query = form.serialize();
console.log(query);
$.post('/', query);
});
}
function stopSearchForm() {
$("#stopBtn").on('click', function() {
let startSearchValue = $("#searchTerm").val("");
console.log("Stop Stream...");
socket.emit('stopStream', function(data) {
console.log("Stream Stop Complete");
});
// let form = $("#searchStop-form");
// let query = form.serialize();
// console.log(query);
// $.post('/', query);
});
}
index.pug
form#search-form(action='javascript:startSearchForm()', method='POST')
input(type="text" id="searchedTerm" name="searchTerm" placeholder="#hastag" required)
button(type="submit" name="startBtn" id="startBtn") Search
form#searchStop-form(action='javascript:stopSearchForm()', method='POST')
input(type="text" id="stopSearch" name="stopSearch" value="stopSearch" hidden)
button(type="submit" id="stopBtn" name="stopBtn") Stop
How about just removing lines $("#startBtn").on('click', function() { and $("#stopBtn").on('click', function() { (and the closing }); for each)? Looks like startSearchForm and stopSearchForm are being called on click already so let them do the work.

notificationclick event service worker

I'm working with service worker to display notification between my users. In my code I include notificationclick event. With this event I'm trying to manage two cases. First case, if in my browser the page of my site is opening, don't open it but focus on it. Second case, if my browser don't show my site, open it and focus on it. But I haven't been succed...
Here is my current code:
self.addEventListener('notificationclick', function (e) {
console.log('notification was clicked')
var notification = e.notification;
var action = e.action;
if (action === 'close') {
notification.close();
} else {
// This looks to see if the current is already open and
// focuses if it is
e.waitUntil(
self.clients.matchAll().then(function(clientList) {
console.log(clientList)
if (clientList.length > 0) {
console.log(clientList[0])
return clientList[0].focus();
}
return self.clients.openWindow('/');
})
);
};
});
self.addEventListener("notificationclick", (event) => {
event.waitUntil(async function () {
const allClients = await clients.matchAll({
includeUncontrolled: true
});
let chatClient;
let appUrl = 'xyz';
for (const client of allClients) {
//here appUrl is the application url, we are checking it application tab is open
if(client['url'].indexOf(appUrl) >= 0)
{
client.focus();
chatClient = client;
break;
}
}
if (!chatClient) {
chatClient = await clients.openWindow(appUrl);
}
}());
});

Windows Store Javascript background audio tassk close() method?

I have created a simple Windows 8.1 store app for just playing internet streaming radio. It looks and works good, but I can't upload my app to Windows Store! After validation a get the message like:
WinJS background task
Error Found:
The WinJS background tasks test encountered the
following errors:
App Radio Skovoroda did not call close() in the
background task JavaScript code found in file default.html.
Impact if not fixed: Apps with background tasks that do not call close() can result in draining the battery.
How to fix: Update the background
task to call close().
The point is I do not need to call close(), because it is a radio player, so, it must play in background for a long time! Any ideas? How to do my app pass the validator?
My script code:
(function () {
"use strict";
var app = WinJS.Application;
var activation = Windows.ApplicationModel.Activation;
app.onactivated = function (args) {
if (args.detail.kind === activation.ActivationKind.launch) {
if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) {
// TODO: This application has been newly launched. Initialize
// your application here.
} else {
// TODO: This application was suspended and then terminated.
// To create a smooth user experience, restore application state here so that it looks like the app never stopped running.
}
args.setPromise(WinJS.UI.processAll());
var buttonMediaControl = document.getElementById("mediaControlButton");
buttonMediaControl.addEventListener("click", playAndStop, false);
//add variables for social buttons
//Facebook
var fbButton = document.getElementById("fbButton");
fbButton.addEventListener("click", goToFbPage, false);
//VKotakte
var vkButton = document.getElementById("vkButton");
vkButton.addEventListener("click", goToVkPage, false);
//Twitter
var twButton = document.getElementById("twButton");
twButton.addEventListener("click", goToTwPage, false);
//Instagram
var igButton = document.getElementById("igButton");
igButton.addEventListener("click", goToIgPage, false);
//YouTube
var ytButton = document.getElementById("ytButton");
ytButton.addEventListener("click", goToYtPage, false);
//TuneIn
var tiButton = document.getElementById("tiButton");
tiButton.addEventListener("click", goToTiPage, false)
// Assign the button object to MediaControls
var systemMediaControls = Windows.Media.SystemMediaTransportControls.getForCurrentView();
systemMediaControls.addEventListener("buttonpressed", systemMediaControlsButtonPressed, false);
systemMediaControls.isPlayEnabled = true;
systemMediaControls.isPauseEnabled = true;
systemMediaControls.isStopEnabled = true;
systemMediaControls.playbackStatus = Windows.Media.MediaPlaybackStatus.closed;
}
};
app.oncheckpoint = function (args) {
};
// Plays the media.
function playMedia() {
var media = document.getElementById("skovorodaStreamingAudio");
media.play();
var statusIcon = document.getElementById("playStopImage");
statusIcon.src = "images/skovoroda_stop_button.png";
var systemMediaControls = Windows.Media.SystemMediaTransportControls.getForCurrentView();
systemMediaControls.playbackStatus = Windows.Media.MediaPlaybackStatus.playing;
}
// Pauses the media.
function pauseMedia() {
var media = document.getElementById("skovorodaStreamingAudio");
media.pause();
var statusIcon = document.getElementById("playStopImage");
statusIcon.src = "images/skovoroda_play_button.png";
var systemMediaControls = Windows.Media.SystemMediaTransportControls.getForCurrentView();
systemMediaControls.playbackStatus = Windows.Media.MediaPlaybackStatus.paused;
}
// Stops the media.
function stopMedia() {
var media = document.getElementById("skovorodaStreamingAudio");
media.pause();
media.currentTime = 0;
}
function playAndStop() {
var myAudio = document.getElementById("skovorodaStreamingAudio");
var statusIcon = document.getElementById("playStopImage");
if (myAudio.paused) {
var systemMediaControls = Windows.Media.SystemMediaTransportControls.getForCurrentView();
systemMediaControls.playbackStatus = Windows.Media.MediaPlaybackStatus.playing;
myAudio.play();
statusIcon.src = "images/skovoroda_stop_button.png";
} else {
var systemMediaControls = Windows.Media.SystemMediaTransportControls.getForCurrentView();
systemMediaControls.playbackStatus = Windows.Media.MediaPlaybackStatus.paused;
myAudio.pause();
statusIcon.src = "images/skovoroda_play_button.png";
}
}
// The media Play event handler.
function mediaPlaying() {
// Update the SystemMediaTransportControl state.
systemMediaControls.playbackStatus = Windows.Media.MediaPlaybackStatus.playing;
}
// The media Pause event handler.
function mediaPaused() {
// Update the SystemMediaTransportControl state.
systemMediaControls.playbackStatus = Windows.Media.MediaPlaybackStatus.paused;
}
// The media Ended event handler.
function mediaEnded() {
// Update the SystemMediaTransportControl state.
systemMediaControls.playbackStatus = Windows.Media.MediaPlaybackStatus.stopped;
}
function goToFbPage() {
window.open("https://www.facebook.com/radioskovoroda?__mref=message_bubble");
}
function goToVkPage() {
window.open("https://vk.com/radioskovoroda");
}
function goToTwPage() {
window.open("https://twitter.com/RadioSkovoroda");
}
function goToIgPage() {
window.open("https://instagram.com/radioskovoroda");
}
function goToYtPage() {
window.open("https://www.youtube.com/channel/UCSgkIdg5MztN_2z9q_8u3Nw")
}
function goToTiPage() {
window.open("http://tunein.com/radio/radio-skovoroda-s248591/")
}
// Event handler for SystemMediaTransportControls' buttonpressed event
function systemMediaControlsButtonPressed() {
if (Windows.Media.MediaControl.isPlaying === true) {
pauseMedia();
} else {
playMedia();
}
}
app.start();
})();

YouTube API - iframe onStateChange events

I'm using the iframe YouTube API and I want to track events, for example, sending data to google analytics, when user start and stop video.
<iframe src="https://www.youtube.com/embed/DjB1OvEYMhY"></iframe>
I looked https://developers.google.com/youtube/iframe_api_reference?csw=1 and did not find an example how to do that. The example creates iframe and defines onReady and onStateChange as well. How would I do same when I've only iframe on page?
This example listens to every play/pause action the user makes, using onPlayerStateChange with its different states, and prints (records) them.
However, you need to create your own record function to do whatever you want with this data.
You also need an ID on your iframe (#player in this case) and to add ?enablejsapi=1 at the end of its URL. And of course, make sure to include the Youtube iframe API.
Note
It's important to declare the API after your code, because it calls onYouTubeIframeAPIReady when it's ready.
<!DOCTYPE html>
<html>
<body>
<iframe id="player" src="https://www.youtube.com/embed/DjB1OvEYMhY?enablejsapi=1"></iframe>
<h5>Record of user actions:</h5>
<script>
var player;
function onYouTubeIframeAPIReady() {
player = new YT.Player( 'player', {
events: { 'onStateChange': onPlayerStateChange }
});
}
function onPlayerStateChange(event) {
switch(event.data) {
case 0:
record('video ended');
break;
case 1:
record('video playing from '+player.getCurrentTime());
break;
case 2:
record('video paused at '+player.getCurrentTime());
}
}
function record(str){
var p = document.createElement("p");
p.appendChild(document.createTextNode(str));
document.body.appendChild(p);
}
</script>
<script src="https://www.youtube.com/iframe_api"></script>
</body>
</html>
JS Fiddle Demo
Here is a version that doesn't use Youtubes iframe API script. The only drawback is that the iframe API might change.
<iframe id="player" src="https://www.youtube.com/embed/dQw4w9WgXcQ?enablejsapi=1"></iframe>
var addYoutubeEventListener = (function() {
var callbacks = [];
var iframeId = 0;
return function (iframe, callback) {
// init message listener that will receive messages from youtube iframes
if(iframeId === 0) {
window.addEventListener("message", function (e) {
if(e.origin !== "https://www.youtube.com" || e.data === undefined) return;
try {
var data = JSON.parse(e.data);
if(data.event !== 'onStateChange') return;
var callback = callbacks[data.id];
callback(data);
}
catch(e) {}
});
}
// store callback
iframeId++;
callbacks[iframeId] = callback;
var currentFrameId = iframeId;
// sendMessage to frame to start receiving messages
iframe.addEventListener("load", function () {
var message = JSON.stringify({
event: 'listening',
id: currentFrameId,
channel: 'widget'
});
iframe.contentWindow.postMessage(message, 'https://www.youtube.com');
message = JSON.stringify({
event: "command",
func: "addEventListener",
args: ["onStateChange"],
id: currentFrameId,
channel: "widget"
});
iframe.contentWindow.postMessage(message, 'https://www.youtube.com');
});
}
})();
addYoutubeEventListener(document.getElementById("player"), function(e) {
switch(e.info) {
case 1:
// playing
break;
case 0:
// ended
break;
}
});
Sometimes the event load is not enough to ensure that the document inside the iframe is ready. If the iframe is in a different domain it is not possible to subscribe to see when it is ready.
A possible workaround is to record when an event is received from the iframe, if after subscribing no event was received try again:
var addYoutubeEventListener = (function() {
var callbacks = [];
var iframeId = 0;
var subscribed = [];
return function (iframe, callback) {
// init message listener that will receive messages from youtube iframes
if(iframeId === 0) {
window.addEventListener("message", function (e) {
if(e.origin !== "https://www.youtube.com" || e.data === undefined) return;
try {
var data = JSON.parse(e.data);
subscribed[data.id] = true;
if(data.event !== 'onStateChange') return;
var callback = callbacks[data.id];
callback(data);
}
catch(e) {}
}, true);
}
// store callback
iframeId++;
callbacks[iframeId] = callback;
subscribed[iframeId] = false;
var currentFrameId = iframeId;
//console.log("adding event listener to iframe id " + iframeId);
// sendMessage to frame to start receiving messages
iframe.addEventListener("load", function () {
var tries = 0;
var checkSubscribed = function()
{
if (subscribed[currentFrameId]) {
//console.log("subscribed succesfully " + currentFrameId)
}
else
{
tries++;
//console.log("Try again " + currentFrameId + " (" + tries + ")");
if (tries < 100) {
doSubscribe();
}
else
{
console.log("Unable to subscribe" + currentFrameId );
}
}
}
var doSubscribe = function()
{
var message = JSON.stringify({
event: 'listening',
id: currentFrameId,
channel: 'widget'
});
iframe.contentWindow.postMessage(message, 'https://www.youtube.com');
message = JSON.stringify({
event: "command",
func: "addEventListener",
args: ["onStateChange"],
id: currentFrameId,
channel: "widget"
});
iframe.contentWindow.postMessage(message, 'https://www.youtube.com');
setTimeout(checkSubscribed, 100);
};
doSubscribe();
}, true);
}
})();

Change hash without triggering Sammy event

function UsersVM(start_page){
var self = this;
console.log('start form ' + start_page);
self.go_to = function(page) {
location.hash = '#Users/' + pageNumber;
}
}
Sammy(function() {
this.get('/app/?#Users/:page', function () {
var vm = new UsersVM(this.params.page);
ko.applyBinding(vm);
});
}).run();
I would like to change the page's hash with the following code:
location.hash = '#Users/' + pageNumber;
But in this case Sammy triggers routing. Say in Backbone we can do it this way:
app.navigate("help/troubleshooting", {trigger: false});
Is it possible to do it in Sammy?
Thanks!
I don't know of a native way to do this in Sammy, but here is a solution that has worked for me:
var sam = $.sammy(function () {
var sammy = this; //get a persistent reference to this
sammy.quiet = false; //set quiet to false by default
//I set quiet to true before running a route
sammy.quietRoute = function (location) {
sammy.quiet = true;
sammy.setLocation(location);
}
//I'm called after every route to reset quiet to false
sammy.after(function () {
sammy.quiet = false;
});
//I'm a 'normal' route that does not have the capability to be 'quiet'
this.get('#normalRoute', function () {
//routing code
});
//I am a route that can be 'quieted' so that when the url or
//hash changes my routing code doesn't run
this.get('#quietableRoute', function () {
if (!sammy.quiet) {
//routing code
} else {
return;
}
});
});
Then call the quietRoute function in your code:
//This will work
sam.quietRoute("#quietableRoute");
//This will not work because the "if(!sammy.quiet)..." code has not been
//implemented on this route
sam.quietRoute("#normalRoute");
Use the following code:
var new_location = '#foo';
app.trigger('redirect', {to: new_location});
app.last_location = ['get', new_location];
app.setLocation(new_location);

Categories