Service worker is integrated in the app. When CSS requested Poppins-Regular.ttf file service worker doesn't send the cached file. Console "throws" error
The FetchEvent for [url] resulted in a network error response
But the files are getting cached along with other files when I run the app for the first time
Here is the code of the CSS file where I have added the font files
#font-face {
font-family: Poppins-Regular;
src: url('../fonts/Poppins-Regular.ttf');
}
#font-face {
font-family: Poppins-Medium;
src: url('../fonts/Poppins-Medium.ttf');
}
Here is the service worker code
const __cacheName = 'MyCache';
const __precacheResources = [
//fonts
'/inv/fonts/Segoe-UI.ttf',
'/inv/fonts/Segoe-UI-Bold.ttf',
'/inv/fonts/Poppins-Medium.ttf',
'/inv/fonts/Poppins-Regular.ttf',
];
var isFileLoadFinished = false;
self.addEventListener('install', event => {
isFileLoadFinished = false;
console.log('Service worker :', __sw_version, 'is installed!');
self.skipWaiting();
caches.delete(__cacheName);
event.waitUntil(
caches.open(__cacheName)
.then(cache => {
return cache.addAll(__precacheResources)
.then(() => {
isFileLoadFinished = true;
})
})
);
});
/*
this will send the object to the client via a message
#param {msg_} is an object to send to
#return null
*/
function sendMessagetoClients(msg_) {
console.log('sending msg to client. msg id is:', msg_.id)
self.clients.matchAll({
includeUncontrolled: true, //returns only the service worker clients controlled by the current service worker. The default is false.
type: "all"// "window"
}
).then(clients => {
if (clients.length == 0) {
console.log('No clients');
}
clients.forEach(client => {
console.log('the client is ', client);
client.postMessage(msg_);
});
});
}
self.addEventListener('activate', event => {
console.log('%s : Service worker :', (new Date()).toISOString(), __sw_version, ' is active! ');
sendMessagetoClients({
id: 002,
msgText: 'All items loaded',
data: isFileLoadFinished
});
});
self.addEventListener('fetch', event => {
event.respondWith(caches.match(event.request)
.then(cachedResponse => {
if (cachedResponse) {
return cachedResponse;
}
return fetch(event.request).catch(err => {
console.error(err);
});
})
);
});
self.addEventListener('message', event => {
console.log('%s : message received. msg id : %s', (new Date()).toISOString(), event.data.id);
//process the msg
if (event.data.id) {
if (event.data.id == 001) {
sendMessagetoClients({
id: 002,
data: isFileLoadFinished
})
} else if (event.data.id == 003) {
sendMessagetoClients({
id: 004,
data: __sw_version
})
}
}
return;
});
What should i do to fix those errors? Any help would be appreciated.
Change caches.match(event.request) to caches.match(event.request, { ignoreSearch: true })
Make sure if the requested URL is the same as the URL in the cache ('/inv/fonts/Poppins-Medium.ttf')
From developer.mozilla.org
ignoreSearch: A Boolean that specifies whether to ignore the query string in the URL. For example, if set to true the ?value=bar part of http://example.com/?value=bar would be ignored when performing a match. It defaults to false.
Related
When the following service worker code runs I get a bunch of these errors:
serv-worker.js:38 Uncaught (in promise) TypeError: Failed to execute 'fetch' on 'WorkerGlobalScope': Cannot construct a Request with a Request object that has already been used.
Question also asked here and unanswered: [https://stackoverflow.com/questions/55979921/error-request-object-that-has-already-been-used]
Appreciations for any help/explanation!
console.log('Start serv-worker.js');
const cacheName = '3.2121';
var urlsToCache = [
'home.html',
'home-js.js',
'web-bg.js',
'css/main.css',
'css/edit-menus.css'
];
self.addEventListener('install', event => {
console.log('Install event...', urlsToCache);
event.waitUntil(
caches.open(cacheName)
.then(function(cache) {
console.log('Opened cache', cacheName);
return cache.addAll(urlsToCache);
})
);
});
// Network first.
self.addEventListener('fetch', (event) => {
// Check the cache first
// If it's not found, send the request to the network
// event.respondWith(
// caches.match(event.request).then(function (response) {
// return response || fetch(event.request).then(function (response) {
// return response;
// });
// })
// );
event.respondWith(async function() {
try {
console.log('aPull from network...', event.request);
return await fetch(event.request);
} catch (err) {
console.log('aPull from cache...', event.request);
return caches.match(event.request);
}
}());
});
self.addEventListener('message', function (event) {
console.log('ServiceWorker cache version: ', cacheName, event);
console.log('Received msg1: ', event.data);
if (event.data.action === 'skipWaiting') {
console.log('ccClearing cache: ', cacheName);
// caches.delete('1.9rt1'); // hardcode old one
// caches.delete(cacheName); // actually removes cached versions
caches.keys().then(function(names) {
for (let name of names)
caches.delete(name);
});
self.skipWaiting();
}
});
I'm trying to get my PWA to work in offline mode. So far, it's serving all the files from localhost, but the CSS doesn't render. All files requested from the cache are getting status 200. (javascript and html are fully functional) Here's my service-worker code.
self.addEventListener("install", (event) => {
event.waitUntil(
caches.open("v1").then((cache) => {
return cache.addAll([
"list of assets",
]);
})
);
});
self.addEventListener("fetch", function (event) {
event.respondWith(
caches.open("v1").then((cache) => {
if (event.request.url == "http://localhost:3000/") {
return cache
.match("http://localhost:3000/index.html")
.then((response) => {
console.log({ response });
return response;
});
} else {
try {
return cache.match(event.request).then((response) => {
console.log(response);
if (response != undefined) {
console.log({ response: "Loading asset from cache." });
return response;
} else {
let asset = fetch(event.request);
cache.add(asset);
return asset;
}
});
} catch (error) {
console.error(error);
}
}
})
);
});
Did you try listing your stylesheet where you have 'list of assets'?
return cache.addAll([
‘./css/styles.css'
]);
I'm trying push notification with service worker in PWA. After registering service worker. I have added event listener for install and activate and inside the activate event listener added logic for registering to push service through push manager subscription but the problem is no logs are present in console regarding the activate and install events whereas trying to test push through chrome dev tools and not working here is my code:
index.js
serviceWorker.register()
worker.js
const publicVapidKey = "BOdud_KdO16dL40D56LbksLa6ElXFAJu-2XEdIKQUmehzomo6FES2jJ1niaUYrCobfI8U5rkqeQNjPB03mKZMvY"
export const register = () => {
console.log('register called');
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/worker.js')
.then((responseRegister) => {
console.log('register object', responseRegister);
window.registration = responseRegister;
})
.catch((err) => {
console.log('error is ', err);
})
}
}
export const unregister = () => {
console.log("unregister called");
if ('serviceWorker' in navigator) {
navigator.serviceWorker.getRegistrations()
.then(function (registrations) {
console.log('registrations', registrations);
for (let registration of registrations) {
registration.unregister()
}
}).catch(function (err) {
console.log('Service Worker registration failed: ', err);
});
}
}
window.self.addEventListener('install', (event) => {
console.log('service worker installed', event);
})
window.self.addEventListener('activate', (event) => {
console.log('service worker activated', event);
console.log('window', window);
window.registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(publicVapidKey)
}).then((subscription) => {
console.log('subscription', subscription);
})
.catch((err) => {
console.log('error in subscribing to push', err);
});
})
window.self.addEventListener("push", e => {
const data = e.data.json();
console.log("Push Recieved...");
window.registration.showNotification(
data.title, {
body: "Notified by Traversy Media!",
});
});
function urlBase64ToUint8Array(base64String) {
const padding = "=".repeat((4 - base64String.length % 4) % 4);
const base64 = (base64String + padding)
.replace(/\-/g, "+")
.replace(/_/g, "/");
const rawData = window.atob(base64);
const outputArray = new Uint8Array(rawData.length);
for (let i = 0; i < rawData.length; ++i) {
outputArray[i] = rawData.charCodeAt(i);
}
return outputArray;
}
clicking on push doesn't fire any push event
I am trying to establish an RTCPeerConnection and using pusher for handshaking through the following code
var pc = new RTCPeerConnection({
"iceServers": [
{url: 'stun:stun.l.google.com:19302'},
{url: 'stun:stunserver.org'},
]
}
);
pc.onicecandidate = event => {
if (!event.candidate) return;
console.log("icecandidate: ", event);
presenceChannel.trigger("client-ice", {data: event.candidate});
};
pc.onaddstream = event => {
console.warn("stream added");
vidEl.srcObject = event.stream;
};
//for when pusher channel subscription is successful from the other end
presenceChannel.bind("client-subscribed", () => {
console.warn(pc);
pc.createOffer().then(offer => {
return pc.setLocalDescription(new RTCSessionDescription(offer), () => {
console.warn("offer created: ", offer);
presenceChannel.trigger("client-offer", {data: offer});
});
}).catch(error => {
console.warn("error on offer: ", error);
});
});
presenceChannel.bind("client-answer", function (data) {
pc.setRemoteDescription(new RTCSessionDescription(data.data), () => {
console.warn("received an answer");
pc.addStream(currentStream);
setTimeout(() => {
console.warn(pc);
}, 5000);
}, error => {
console.warn("error in answer: ", error)
});
});
presenceChannel.bind("client-ice", function (data) {
pc.addIceCandidate(new RTCIceCandidate(data.data)).catch(error => {
console.warn("ice error: ", error);
});
});
//and the remote peer
presenceChannel.bind("pusher:subscription_succeeded", () => {
if (currentStream === undefined) {
presenceChannel.trigger("client-recipient-error");
pusher.unsubscribe(channel);
return false;
}
var pc = new RTCPeerConnection({
"iceServers": [
{url: 'stun:stun.l.google.com:19302'},
{url: 'stun:stunserver.org'},
]
}
);
pc.onicecandidate = event => {
if (!event.candidate) return;
console.log("icecandidate: ", event);
presenceChannel.trigger("client-ice", {data: event.candidate});
};
pc.onaddstream = event => {
console.warn("stream added");
vidEl.srcObject = event.stream;
};
presenceChannel.trigger("client-subscribed", {});
console.warn(pc);
presenceChannel.bind("client-ice", function (data) {
pc.addIceCandidate(new RTCIceCandidate(data.data)).catch(error => {
console.warn("ice error: ", error);
});
});
presenceChannel.bind("client-offer", signalData => {
// console.warn("client-signal", signalData);
console.warn("received an offer");
pc.setRemoteDescription(new RTCSessionDescription(signalData.data), () => {
return pc.createAnswer().then(answer => {
pc.setLocalDescription(new RTCSessionDescription(answer), () => {
presenceChannel.trigger("client-answer", {data: answer});
console.warn("created answer");
pc.addStream(currentStream);
setTimeout(() => {
console.warn(pc);
}, 7000);
});
}).then(() => {
}).catch(error => {
console.warn("error on answer: ", error);
});
});
});
});
Am not getting any errors the rtcpeerconnection variable on both ends have both local and remote description so am assuming the connection was well established right? Or this doesn't mean the connection was established, (i need help on this too to know if the connection is established or not and how to know), however everything runs, all the events and functions execute (except the onicecandidate event which i know its just not being triggered am not worried about this though) except for onaddstream event, so am guessing if this is not being triggered and am adding a stream then is the connection really on, wasted alot of time on this and frankly its just driving me nuts, any help AND I MEAN ANY HELP would be highly appreciated cause it's my first time handling RTCPeerConnection and maybe there is something am doing wrong or taking a wrong approach, please point this out too if its the case and show me the right way of doing it. Thanks in advance.
EDIT: Am using chrome and it does not yet support addTrack, gave me an error earlier that addTrack is not a function so fell back to addStream.
Service Worker Message Handler:
let angularClient;
self.addEventListener('message', function(event) {
// store the client which sent the message into angularClient variable
angularClient = event.ports[0];
});
Service Worker Notification Click Handler, Which sends data to angularClient
self.addEventListener('notificationclick', function(event) {
event.notification.close();
var url = /localhost:8080|example.com|https:\/\/www.example.com/;
var newurl = "/" + event.notification.data.url;
if (event.notification.data.url) {
newurl = event.notification.data.url;
}
function endsWith(str, suffix) {
console.log(str);
console.log(suffix);
return str.indexOf(suffix, str.length - suffix.length) !== -1;
}
event.waitUntil(
clients.matchAll({
type: 'window'
})
.then(function(windowClients) {
for (var i = 0; i < windowClients.length; i++) {
var client = windowClients[i];
if (url.test(client.url) && 'focus' in client) {
if (endsWith(client.url, newurl)) {
console.log("URL matched");
console.log("sending 1");
angularClient.postMessage(sendToAngularPayload);
return client.focus();
}
return client.navigate(newurl)
.then(client => client.focus());
}
}
if (clients.openWindow) {
console.log("sending 2");
angularClient.postMessage(sendToAngularPayload); //sendToAngularPayload is defined when notification is received in firebase's messaging.setBackgroundMessageHandler.
return clients.openWindow('https://www.example.com/#/' +
event.notification.data.url);
}
})
);
},true);
AngularJs Controller with functions
Function to send message to service worker so that it stores this client
$scope.checkServiceWorker = function() {
if ('serviceWorker' in navigator) {
// ensure service worker is ready
navigator.serviceWorker.ready.then(function(reg) {
console.log("Send message");
// PING to service worker, later we will use this ping to
//identify our client.
sendMessage().then(function(event) {
console.log(event);
}).catch(function(error) {
console.log("error", error);
location.reload();
});
}).catch(function() {
console.log('SW not ready');
$scope.checkServiceWorker();
});
}
}
sendMessage function with onMessage handler
function sendMessage() {
return new Promise(function(resolve, reject) {
var messageChannel = new MessageChannel();
messageChannel.port1.onmessage = function(event) {
console.log("on message handler", event);
if (event.data.error) {
reject(event.data.error);
} else {
console.log('inside resolve', event.data);
console.log("Ping received from SW");
console.log(event);
resolve(event.data);
}
};
console.log("Sending");
navigator.serviceWorker.controller.postMessage("ping",
[messageChannel.port2]);
console.log("sent");
});
}
The problem is that onMessage Handler inside angularjs controller gets fired 90% of the times, but sometimes it does not. As I can see in the developer console, the execution stops in serviceworker.js after I print "sending 1" in the notification click handler, and does not show rest of the logs inside the controller's onMessage handler.
worker.ts
self.addEventListener("push", e => {
const data = e.data.json();
console.log("Push received");
console.log("data ", data);
self.registration.showNotification(data.title, {
body: "Notified",
})
// Broadcasting from a ServiceWorker to every client
self.clients.matchAll().then(all => all.map(client => client.postMessage(data)));
})
The listener is added on navigator.serviceWorker and not on a specific
worker
AngularJs controller:
constructor() {
navigator.serviceWorker.addEventListener('message', e => console.log(e.data));
}