Progressive Web App and caching UI - javascript

I'm developing a PWA with all functionalities (as offline, add to home screen, notification,..) but I have some problem when I try to refresh the UI.
In other words, I have my PWA with the UI defined in the index.html file, the thing that I want to do is caching the PWA for use it offline (and I'm able to do this) and if the device is online check if there are some update of UI (in index.html file, or in the file which it depends), download this changes and refresh the UI on the device and inside the cache.
With the data I haven't any problem with the cache.
For example if I have this page index.html:
<!DOCTYPE html>
<html>
<head >
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title class="title">PWA</title>
<link rel="manifest" href="manifest.json">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="PWA">
<link rel="apple-touch-icon" href="imgs/icons/Icon-152.png">
<meta name="msapplication-TileImage" content="imgs/icons/Icon-144.png">
<meta name="msapplication-TileColor" content="#2F3BA2">
<link rel="shortcut icon" sizes="32x32" href="imgs/icons/Icon-32.png">
<link rel="shortcut icon" sizes="196x196" href="imgs/icons/Icon-196.png">
<link rel="apple-touch-icon-precomposed" href="imgs/icons/Icon-152.png">
</head>
<body>
<div class="container">
<div class="row">
<div class="col-12">
<img src="imgs/images.png">
</div>
</div>
</div>
<script src="js/jquery.js"></script>
<script src="js/bootstrap.js"></script>
<script src="js/app.js"></script>
</body>
</html>
app.js:
if('serviceWorker' in navigator) {
navigator.serviceWorker
.register('service-worker.js')
.then(function() { console.log('Service Worker Registered'); });
}
service-worker.js:
var dataCacheName = 'dataCache-v2';
var cacheName = 'cache-v2';
var filesToCache = [
'index.html',
'imgs/images.png',
'src/app.js'
];
self.addEventListener('install', function(e) {
console.log('[ServiceWorker] Install');
e.waitUntil(
caches.open(cacheName).then(function(cache) {
console.log('[ServiceWorker] Caching App Shell');
return cache.addAll(filesToCache);
})
);
});
self.addEventListener('activate', function(e) {
console.log('[ServiceWorker] Activate');
e.waitUntil(
caches.keys().then(function(keyList) {
return Promise.all(keyList.map(function(key) {
console.log('[ServiceWorker] Removing old cache', key);
if (key !== cacheName && key !== dataCacheName) {
return caches.delete(key);
}
}));
})
);
return self.clients.claim();
});
self.addEventListener('fetch', function(e) {
console.log('[ServiceWorker] Fetch', e.request.url);
var dataUrl = 'URL-WHERE-FIND-DATA';
if (e.request.url.indexOf(dataUrl) === 0) {
e.respondWith(
fetch(e.request)
.then(function(response) {
return caches.open(dataCacheName).then(function(cache) {
cache.put(e.request.url, response.clone());
console.log('[ServiceWorker] Fetched&Cached Data');
return response;
});
})
);
} else {
e.respondWith(
caches.match(e.request).then(function(response) {
return response || fetch(e.request);
})
);
}
});
Suppose that I replace the images.png with another image but with the same name, how can I show the new one to the user?
If I refresh the page the data is get from the network (if it is avaiable) but the image still catch from the cache.
I hope that I have explained my problem well. Thanks a lot to everyone that will help me
UPDATE #1:
I tried to implements the "Cache then network" strategy as Arnelle Balane suggested to me, but the problem is always here, the browser show always the cached images (in the code below I try to update an image called 'demo.jpg').
Probably I'm doing something wrong.
This is my code:
service-worker.js:
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.open("my-cache").then(function(cache) {
return fetch(event.request).then(function(response) {
console.log('Fetch: ' + response.url);
cache.put(event.request, response.clone());
return response;
});
})
);
});
app.js:
var networkDataReceived = false;
var networkUpdate = fetch('https://website.com/app/images/demo.jpg').then(function(response) {
return response.blob();
}).then(function(data) {
networkDataReceived = true;
updatePage(data);
});
caches.match('https://website.com/app/images/demo.jpg').then(function(response) {
if (!response) throw Error("No data");
return response.blob();
}).then(function(data) {
if (!networkDataReceived) {
updatePage(data);
}
}).catch(function() {
return networkUpdate;
}).catch(showErrorMessage);
function showErrorMessage(response){
console.log("Error: " + response);
}
function updatePage(response) {
var img = document.getElementById('demo');
var imageUrl = URL.createObjectURL(response);
img.src = imageUrl;
}
Any new suggestions? Thanks
UPDATE #2:
Now I’m trying to do everything from the beginning.
I have copy the service worker from this google’s example: https://developers.google.com/web/fundamentals/getting-started/codelabs/your-first-pwapp/
That implements the "Cache then network" strategy.
The code of service-worker.js is this:
var dataCacheName = 'dataCache1';
var cacheName = 'cache1';
var filesToCache = [
'/',
'index.html',
'css/main.css',
'src/app.js'
];
self.addEventListener('install', function(e) {
console.log('[ServiceWorker] Install');
e.waitUntil(
caches.open(cacheName).then(function(cache) {
console.log('[ServiceWorker] Caching app shell');
return cache.addAll(filesToCache);
})
);
});
self.addEventListener('activate', function(e) {
console.log('[ServiceWorker] Activate');
e.waitUntil(
caches.keys().then(function(keyList) {
return Promise.all(keyList.map(function(key) {
if (key !== cacheName && key !== dataCacheName) {
console.log('[ServiceWorker] Removing old cache', key);
return caches.delete(key);
}
}));
})
);
/*
* Fixes a corner case in which the app wasn't returning the latest data.
* You can reproduce the corner case by commenting out the line below and
* then doing the following steps: 1) load app for first time so that the
* initial New York City data is shown 2) press the refresh button on the
* app 3) go offline 4) reload the app. You expect to see the newer NYC
* data, but you actually see the initial data. This happens because the
* service worker is not yet activated. The code below essentially lets
* you activate the service worker faster.
*/
return self.clients.claim();
});
self.addEventListener('fetch', function(e) {
console.log('[Service Worker] Fetch', e.request.url);
var dataUrl = 'https:/mywebsite.it/service/images/demo.jpg';
if (e.request.url.indexOf(dataUrl) > -1) {
/*
* When the request URL contains dataUrl, the app is asking for fresh
* weather data. In this case, the service worker always goes to the
* network and then caches the response. This is called the "Cache then
* network" strategy:
* https://jakearchibald.com/2014/offline-cookbook/#cache-then-network
*/
e.respondWith(
caches.open(dataCacheName).then(function(cache) {
return fetch(e.request).then(function(response){
cache.put(e.request.url, response.clone());
return response;
});
})
);
} else {
/*
* The app is asking for app shell files. In this scenario the app uses the
* "Cache, falling back to the network" offline strategy:
* https://jakearchibald.com/2014/offline-cookbook/#cache-falling-back-to-network
*/
e.respondWith(
caches.match(e.request).then(function(response) {
return response || fetch(e.request);
})
);
}
});
And the result is always the same, the image won’t to get update.
With the fetch(url) method, the service worker should get the image from the network, right?
The result of the code is shown in the image below.
When I reload the page the only fetched request is the folder “/service/”
If I try to “force” the loading of the image that I want to update with an explicit request to browser (https://mywebsite.com/service/images/demo.jpg) the service worker correclty fetch the request but shown always the old image.
I think that I’m doing something foolishly wrong but I not understand what.

The reason why the image is being served from the cache is because that's what the service worker is coded to do:
e.respondWith(
caches.match(e.request).then(function(response) {
return response || fetch(e.request);
})
);
This checks your cache first if a response for the request is already stored, and only fetches the resource from the network if not.
As I see it, you have two possible ways of dealing with that:
Make sure that the file names of the resources (including images) change when the actual content of that resource changes. There are several build tools out there that can do this for you by appending the resource's hash to its file name. A change in the resource's contents will result into a different hash, and thus a different file name.
You can use the Cache then network strategy as described in this article by Jake Archibald. The idea is that you serve the cached resource if it is available, while at the same time you request for that resource over the network. Once the network request completes, you replace the previously-served content with the one you got from the network, as well as update the cached version of the resource. This way, you can be sure that the user is seeing the latest version of a resource, while still not breaking the offline experience by having an updated cached version of that resource.

Related

How do you retrieve a value from IndexedDB when using serviceworkers?

I am new to IndexedDB and serviceworkers and am having a very difficult time understanding how to turn these into a funcitonal application. I've done extensive reading on both, but even the "complete" examples don't incorporate the two.
I am tasked with creating an application that will allow users to work offline. The first time they connect to the site, I want to pull specific information from the database and store it in IndexedDB. When they go offline, I need to use that data to display information on the page. Certain interactions will cause the data to update, then to be synced later once an internet connection is reestablished. From a high-level, I udnerstand how this works.
It is my understanding that we cannot call functions from the serviceworker.js file due to the asynchronous nature of serviceworkers. Additionally, serviceworkers.js cannot directly update the DOM. However, the examples I have seen are creating and managing the IndexedDB data within the serviceworkers.js file.
So let's say I have a file:
<!-- index.html -->
<html>
<body>
Hello <span id="name"></span>
</body>
</html>
And a serviceworker.js:
var CACHE_NAME = 'my-cache-v1';
var urlsToCache = [
'/'
// More to be added later
];
self.addEventListener('install', function(event) {
// Perform install steps
event.waitUntil(
caches.open(CACHE_NAME)
.then(function(cache) {
console.log('Opened cache');
return cache.addAll(urlsToCache);
})
);
});
self.addEventListener('activate', function(event) {
event.waitUntil(
createDB() //Use this function to create or open the database
);
});
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request)
.then(function(response) {
// Cache hit - return response
if (response) {
return response;
}
return fetch(event.request).then(
function(response) {
// Check if we received a valid response
if(!response || response.status !== 200 || response.type !== 'basic') {
return response;
}
var responseToCache = response.clone();
caches.open(CACHE_NAME)
.then(function(cache) {
cache.put(event.request, responseToCache);
});
return response;
}
);
})
);
});
function createDB() {
idb.open('mydata', 1, function(upgradeDB) {
var store = upgradeDB.createObjectStore('user', {
keyPath: 'id'
});
store.put({id: 1, name: 'John Doe'}); //This can be updated with an AJAX call to the database later
});
}
How do I now update the element "name" with the value for key = 1 from the "user" objectstore in the "mydata" database?
Depending on your use case, you've got several options :
You dont need the service worker. Just pull your data from iDB directly from the page. The DOM has access to iDB.
Set a template for your index.html. At the activate step in service worker, pre-render the page with the value from iDB and cache it.

Clear Workbox cache of all content

Using Workbox in a service worker in a javascript webapp.
Want to clear the entire workbox/application cache of all content... basically go back to a state as similar as possible to the state before first load of the app into a browser, potentially to be followed by refreshing via window.location.href = '/'.
Googling and looking on SO, I have found various recipes for clearing various things from the cache. I have not been able to figure out a simple way to just 'clear everything and start over'.
I tried this in server code in sw.js:
var logit = true;
if (logit) console.log('Service Worker Starting Up for Caching... Hello from sw.js');
importScripts('https://storage.googleapis.com/workbox-cdn/releases/3.6.1/workbox-sw.js');
if (workbox) {
if (logit) console.log(`Yay! Workbox is loaded 🎉`);
} else {
if (logit) console.log(`Boo! Workbox didn't load 😬`);
}
workbox.routing.registerRoute(
// Cache image files
/.*\.(?:mp3|flac|png|gif|jpg|jpeg|svg|mp4)/,
// Use the cache if it's available
workbox.strategies.cacheFirst({
// Use a custom cache name
cacheName: 'asset-cache',
plugins: [
new workbox.expiration.Plugin({
// Cache only 20 images
maxEntries: 20,
// Cache for a maximum of x days
maxAgeSeconds: 3 * 24 * 60 * 60,
})
],
})
);
self.addEventListener('message', function(event){
msg = event.data;
console.log("SW Received Message: " + msg);
if (msg==='clearCache') {
console.log('Clearing Workbox Cache.');
WorkBoxCache = new workbox.expiration.Plugin;
WorkBoxCache.expirationPlugin.deleteCacheAndMetadata();
//WorkBoxCacheServer.clear();
}
});
paired with this on the client:
navigator.serviceWorker.controller.postMessage("clearCache");
This didn't work, though the message was apparently passed. Also, this seems an inelegant solution and I presume there is a simpler one.
How can this be done?
How can it be initiated from the client side in client side js on the browser? What does this require in server side code (eg in sw.js).
Thank you
CacheStorage is accessible in the client code (where you register the SW) so you can delete it from there.
caches.keys().then(cacheNames => {
cacheNames.forEach(cacheName => {
caches.delete(cacheName);
});
});
If we only delete the cache then it damage service worker ,service worker will not work properly, so we have to unregister service worker then have to delete cache and then reregister service worker.
refreshCacheAndReload = () => {
if ('serviceWorker' in navigator) {
serviceWorkerRegistration.unregister();
caches.keys().then(cacheNames => {
cacheNames.forEach(cacheName => {
caches.delete(cacheName);
});
}).then(() => {
serviceWorkerRegistration.register();
})
}
setTimeout(function () { window.location.replace(""); }, 300)
}

Service worker issue: page never loads when page is refreshed with "update on reload" checked

Demo video: https://www.youtube.com/watch?v=UBfnvx6jC_A
I followed along with Udacity's Offline Web Applications course in order to get my app working offline. Here is my code:
main.js
// other stuff above
if (navigator.serviceWorker) {
navigator.serviceWorker.register('/service-worker.js').catch(function() {
console.log('Service worker registration failed.');
});
}
service-worker.js
let currCacheName = 'premium-poker-tools-1';
self.addEventListener('install', function(event) {
event.waitUntil(
caches.open(currCacheName).then(function(cache) {
let promise = cache.addAll([
'/',
'app.js',
// 'c7d016677eb7e912bc40.worker.js',
// 'f328c7e2b379df12fa4c.worker.js',
'static/logo.png',
'static/favicon.png',
'static/loading.svg',
'static/cards/ace-of-clubs.png',
'static/cards/king-of-clubs.png',
'static/cards/queen-of-clubs.png',
'static/cards/jack-of-clubs.png',
'static/cards/ten-of-clubs.png',
'static/cards/nine-of-clubs.png',
'static/cards/eight-of-clubs.png',
'static/cards/seven-of-clubs.png',
'static/cards/six-of-clubs.png',
'static/cards/five-of-clubs.png',
'static/cards/four-of-clubs.png',
'static/cards/three-of-clubs.png',
'static/cards/two-of-clubs.png',
'static/cards/ace-of-spades.png',
'static/cards/king-of-spades.png',
'static/cards/queen-of-spades.png',
'static/cards/jack-of-spades.png',
'static/cards/ten-of-spades.png',
'static/cards/nine-of-spades.png',
'static/cards/eight-of-spades.png',
'static/cards/seven-of-spades.png',
'static/cards/six-of-spades.png',
'static/cards/five-of-spades.png',
'static/cards/four-of-spades.png',
'static/cards/three-of-spades.png',
'static/cards/two-of-spades.png',
'static/cards/ace-of-hearts.png',
'static/cards/king-of-hearts.png',
'static/cards/queen-of-hearts.png',
'static/cards/jack-of-hearts.png',
'static/cards/ten-of-hearts.png',
'static/cards/nine-of-hearts.png',
'static/cards/eight-of-hearts.png',
'static/cards/seven-of-hearts.png',
'static/cards/six-of-hearts.png',
'static/cards/five-of-hearts.png',
'static/cards/four-of-hearts.png',
'static/cards/three-of-hearts.png',
'static/cards/two-of-hearts.png',
'static/cards/ace-of-diamonds.png',
'static/cards/king-of-diamonds.png',
'static/cards/queen-of-diamonds.png',
'static/cards/jack-of-diamonds.png',
'static/cards/ten-of-diamonds.png',
'static/cards/nine-of-diamonds.png',
'static/cards/eight-of-diamonds.png',
'static/cards/seven-of-diamonds.png',
'static/cards/six-of-diamonds.png',
'static/cards/five-of-diamonds.png',
'static/cards/four-of-diamonds.png',
'static/cards/three-of-diamonds.png',
'static/cards/two-of-diamonds.png',
'static/feedback/1.png',
'static/feedback/2.png',
'static/feedback/3.png',
'static/feedback/4.png',
'static/feedback/flop-selector.png',
'static/feedback/green-grid-squares.png',
'static/feedback/user-set-range-to-simulate-to-street.png',
'static/guides/beginners-guide/1.png',
'static/guides/beginners-guide/2.png',
'static/guides/beginners-guide/3.png',
'static/guides/beginners-guide/4.png',
'static/guides/beginners-guide/5.png',
'static/guides/beginners-guide/6.png',
'static/guides/beginners-guide/7.png',
'static/guides/beginners-guide/8.png',
'static/guides/beginners-guide/9.png',
'static/guides/beginners-guide/10.png',
'static/guides/beginners-guide/11.png',
'static/guides/beginners-guide/12.png',
'static/guides/beginners-guide/13.png',
'static/guides/beginners-guide/14.png',
'static/guides/beginners-guide/15.png',
'static/guides/beginners-guide/16.png',
'static/guides/beginners-guide/17.png',
'static/guides/beginners-guide/18.png',
'static/guides/beginners-guide/19.png',
'static/guides/beginners-guide/20.png',
'static/guides/beginners-guide/21.png',
'static/guides/faq/double-counting/1.png',
'static/guides/faq/hit-percentage-calculation/1.png',
'static/guides/faq/hit-percentage-calculation/2.png',
'static/guides/faq/hit-percentage-calculation/3.png',
'static/guides/faq/insights/1.png',
'static/guides/faq/insights/2.png',
'static/guides/faq/insights/3.png',
'static/guides/faq/insights/4.png',
'static/guides/faq/insights/5.png',
'static/guides/faq/insights/6.png',
'static/guides/faq/insights/7.png',
'static/guides/faq/insights/8.png',
'static/guides/faq/set-checks-to-default/1.png',
'static/guides/quick-guide/1.png',
'static/guides/quick-guide/2.png',
'static/guides/quick-guide/3.png',
'static/guides/quick-guide/4.png',
'static/guides/quick-guide/5.png',
'static/guides/quick-guide/6.png',
'static/guides/quick-guide/7.png',
'static/guides/quick-guide/8.png',
'static/guides/quick-guide/save-load-scenario.png',
'static/home/1.png',
'static/home/2.png',
'static/home/3.png',
'static/settings/equity-calculator-insights-not-visible.png',
'static/settings/equity-calculator-insights-visible.png',
'static/settings/outcome-analyzer-checkboxes-collapsed-1.png',
'static/settings/outcome-analyzer-checkboxes-collapsed-2.png',
'static/settings/outcome-analyzer-checkboxes-included-1.png',
'static/settings/outcome-analyzer-checkboxes-included-2.png',
'static/settings/outcome-analyzer-hands.png',
'static/settings/outcome-analyzer-insights-not-visible.png',
'static/settings/outcome-analyzer-insights-visible.png',
'static/settings/saved-ranges-1.png',
'static/settings/saved-ranges-2.png',
'static/settings/saved-ranges-3.png',
'static/settings/saved-ranges-4.png',
'static/settings/included-selectors/double-slider-selector.png',
'static/settings/included-selectors/log-double-slider-selector.png',
'static/settings/included-selectors/saved-ranges-selector.png',
'static/settings/included-selectors/single-slider-selector.png',
'static/settings/included-selectors/tier-and-category-selector.png',
'static/settings/tiers/tiers.png',
'static/settings/visual/dont-show-num-combos-in-range.png',
'static/settings/visual/green-grid-squares.png',
'static/settings/visual/multicolored-grid-squares.png',
'static/settings/visual/show-num-combos-in-range.png',
]).then(function () {
console.log('Successfully cached everything.')
}).catch(function (error) {
console.log('Problem caching: ', error);
});
return promise;
}).catch(function () {
console.error('Error with caches.open or cache.addAll');
})
);
});
self.addEventListener('activate', function(event) {
console.log('activate');
event.waitUntil(
caches.keys()
.then(function getOldCachesThatBeginWithPremiumPokerToolsDash (cacheNames) {
console.log(cacheNames);
return cacheNames.filter(function (cacheName) {
return cacheName.startsWith('premium-poker-tools-') && (cacheName !== currCacheName);
});
})
.then(function removeOldCachesThatBeginWithPremiumPokerToolsDash (oldCachesThatBeginWithPremiumPokerToolsDash) {
console.log(oldCachesThatBeginWithPremiumPokerToolsDash)
let removeCachePromises = [];
oldCachesThatBeginWithPremiumPokerToolsDash.forEach(function (oldCacheThatBeginsWithPremiumPokerToolsDash) {
removeCachePromises.push(caches.delete(oldCacheThatBeginsWithPremiumPokerToolsDash));
});
console.log(removeCachePromises);
return Promise.all(removeCachePromises);
})
);
});
self.addEventListener('fetch', function(event) {
console.log('fetch');
event.respondWith(
caches.match(event.request).then(function (response) {
if (response) {
return response;
}
return fetch(event.request);
}).catch(function () {
console.error('Error trying to match event request to cache.');
})
);
});
The issue comes when I am in a state where I have a service worker installed and active, and I have all of my stuff cached. When I have the dev tools open and have "update on reload" checked in Chrome, if I reload:
The page looks the same, but has the spinner indicating that it is still loading.
In the dev tools, it shows that a new service worker is "waiting to activate".
In the network tab, it shows that the request to http://localhost:8080/ is continually pending.
"Successfully cached everything." is the only thing that gets logged to the console. "activate" doesn't get logged, and neither does "fetch".
But if I press the "x" in Chrome to tell it to stop loading, and then refresh again, it loads perfectly.
I can't seem to figure out what is wrong. / is in the premium-poker-tools-1 cache, so shouldn't the request hit the service worker and return the cached HTML? And even if it doesn't find it there, shouldn't it be sending a request out to the server to get the response? How is it getting hung up?
Edit: I now understand that the service worker is replaced when "Update on reload" is checked even if the service worker hasn't changed.
This answer is moved from the bottom of the question, to make it clearer what the underlying issue is:
I now
understand
that the service worker is replaced when "Update on reload" is checked
even if the service worker hasn't changed.

Spotify web player gives Websocket error on raspberry pi

I am trying to set up spotify player to my raspberry pi. I have codes that work's like a charm on my macbook/chrome but the same codes wont work on raspbian/chromium.
I am getting following errors:
Error while parsing the 'allow' attribute: 'encrypted-media' is an invalid feature name.
{message: "Failed to initialize player"} script.js:44
WebSocket connection to 'wss://gew-dealer.spotify.com/?access_token=removed_my_token'
failed: Error in connection establishment: net:: ERR_NOT_IMPLEMENTED
{message: "Authentication failed"} script.js:45
Here is my script.js:
const hash = window.location.hash
.substring(1)
.split('&')
.reduce(function (initial, item) {
if (item) {
var parts = item.split('=');
initial[parts[0]] = decodeURIComponent(parts[1]);
}
return initial;
}, {});
window.location.hash = '';
// Set token
let _token = hash.access_token;
const authEndpoint = 'https://accounts.spotify.com/authorize';
// Replace with your app's client ID, redirect URI and desired scopes
const clientId = 'removed_my_client_id';
const redirectUri = 'http://localhost:1337';
const scopes = [
'streaming',
'user-read-birthdate',
'user-read-private',
'user-read-email',
'user-modify-playback-state'
];
// If there is no token, redirect to Spotify authorization
if (!_token) {
window.location = `${authEndpoint}?client_id=${clientId}&redirect_uri=${redirectUri}&scope=${scopes.join('%20')}&response_type=token&show_dialog=true`;
}
// Set up the Web Playback SDK
window.onSpotifyPlayerAPIReady = () => {
const player = new Spotify.Player({
name: 'Web Playback SDK Template',
getOAuthToken: cb => { cb(_token); }
});
// Error handling
player.on('initialization_error', e => console.error(e));
player.on('authentication_error', e => console.error(e));
player.on('account_error', e => console.error(e));
player.on('playback_error', e => console.error(e));
// Playback status updates
player.on('player_state_changed', state => {
console.log(state)
$('#current-track').attr('src', state.track_window.current_track.album.images[0].url);
$('#current-track-name').text(state.track_window.current_track.name);
});
// Ready
player.on('ready', data => {
console.log('Ready with Device ID', data.device_id);
// Play a track using our new device ID
play(data.device_id);
});
// Connect to the player!
player.connect();
}
// Play a specified track on the Web Playback SDK's device ID
function play(device_id) {
$.ajax({
url: "https://api.spotify.com/v1/me/player/play?device_id=" + device_id,
type: "PUT",
data: '{"uris": ["spotify:track:5ya2gsaIhTkAuWYEMB0nw5"]}',
beforeSend: function(xhr){xhr.setRequestHeader('Authorization', 'Bearer ' + _token );},
success: function(data) {
console.log(data)
}
});
}
Index.html
<!DOCTYPE html>
<html>
<head>
<title>Spotify Web Playback SDK Template</title>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="https://sp-bootstrap.global.ssl.fastly.net/8.0.0/sp-bootstrap.min.css" rel="stylesheet" />
<script
src="https://code.jquery.com/jquery-3.2.1.min.js"
integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4="
crossorigin="anonymous"></script>
<!-- Include the Web Playback SDK -->
<script src="https://sdk.scdn.co/spotify-player.js"></script>
<!-- Include our Javascript -->
<script src="script.js" defer></script>
</head>
<body class="container">
<h1 class="text-salmon">Spotify Web Playback SDK Template</h1>
<h4>This app uses the implicit grant authorization flow to get an access token and initialise the Web Playback SDK. It then uses the Spotify Connect Web API to play a song.</h4>
<p>If everything is set up properly, you should hear some music!</p>
<img id="current-track"/>
<h3 id="current-track-name"></h3>
<a class="btn btn-salmon btn-lg" href="https://glitch.com/edit/#!/spotify-web-playback">Get started!</a>
</body>
</html>
This code should do:
Initialize the web player
Select one hard coded song
Play the song
Show the image
I have absolutely no idea what could be the problem here. I have been trying a lot of things to fix this but the problem is that i don't know what causes this so i have been just doing stuff without any idea is it going to help. So my question is how i can make this work on raspberry?

Firefox TypeError: ServiceWorker script encountered an error during installation

I'm developing web push notification on my website. I follow the Web Push Notifications of Google and The Service Worker Cookbook of Mozilla.
I have tested on the Google Chrome v50+ and everything is working but I will get the error below on Firefox 44, 45, 46, 52, latest Firefox (version 57.0.4 64 bit) when calling navigator.serviceWorker.register('./push-service-worker.js') function.
TypeError: ServiceWorker script at http://localhost:9600/push-service-worker.js for scope http://localhost:9600/ encountered an error during installation.
This is my code:
Register ServiceWorker in controller.js
navigator.serviceWorker.register('push-service-worker.js')
.then((registration) => {
return registration.pushManager.getSubscription()
.then((subscription) => {
if (subscription) {
return subscription;
}
var subscribeOptions = {
userVisibleOnly: true,
applicationServerKey: buildApplicationServerKey(),
};
return registration.pushManager.subscribe(subscribeOptions);
});
})
.then((subscription) => {
sendSubscriptionToServer(subscription);
})
.catch((err) => {
console.log('Unable to subscribe to push: ', err);
});
push-service-worker.js
'use strict';
self.addEventListener('push', (event) => {
var payload = event.data.json();
var title = payload.title || 'Title';
event.waitUntil(
self.registration.showNotification(title, {
body: payload.body,
icon: './common/images/notification-icon-192x192.png',
image: payload.image || '',
})
);
});
self.addEventListener('notificationclick', (event) => {
event.notification.close();
var urlToOpen = new URL('/', self.location.origin).href;
event.waitUntil(
clients.matchAll({
type: 'window',
includeUncontrolled: true,
})
.then((windowClients) => {
var matchingClient = null;
for (var i = 0; i < windowClients.length; i++) {
var windowClient = windowClients[i];
if (windowClient.url === urlToOpen) {
matchingClient = windowClient;
break;
}
}
if (matchingClient) {
return matchingClient.focus();
} else {
return clients.openWindow(urlToOpen);
}
})
);
});
Directory structure
./root
---- manifest.json
---- push-service-worker.js
---- src
---- controller.js
Thank for helping!
As wanderview said at here:
FWIW, you should always use a separate profile for each channel (release/beta/dev-edition/nightly). We're working on making it work like that out-of-the-box, but its not ready yet.
This problem is encountered when I use one profile for multiple Firefox version. To fix this issue go to about:support and click Refresh Firefox. If it doesn't work, you can go to about:profiles, click Create new profile, and then Launch profile in new browser.
In my case, this was caused by Firefox not being able to access the serviceworker.js file.
The server I was hosting it on had a permissions check based on cookies, and in this case Firefox was not sending the cookie as I believe it was considered a cross-site script.
On the server, I made the serviceworker.js file accessible publicly, and then Firefox could register the service worker.
It was particularly difficult to notice this, because the Firefox Developer Tools did not show the Forbidden response in the Console, nor did it even show any request for serviceworker.js in the Network tab.
Therefore, presumably the TypeError is in fact a generic error and should be read as 'something went wrong registering the Service Worker'.

Categories