I want to build a pwa website that will host offline geogebra applets without need the user to have geogebra installed. I have already built a working local website but it needs internet for the geogebra applets to work. I want the Geogebra applets to work offline, not to require internet connection.
I have already built a locally hosted website and also converted it to an apk using cordova. The website or the app works work well when online.
I have tried creating a pwa website version and it works well. The service works are fine and I score above 80% on lighthouse but the problem is that the pwa doesn't back or store the geogebra applets so that they work when offline.
How do I achieve what I want. Below is my Service Workers Code:
// sw.js - Service Worker
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open('app-cache').then((cache) => {
return cache.addAll([
'/',
'index.html',
'styles.css',
'script.js',
'manifest.json',
'offline.html',
'/STEAM/steamApp1.html',
'https://www.geogebra.org/m/kkdqjbxa',
// Add other resources here as needed
]);
})
);
});
self.addEventListener('fetch', (event) => {
// Handle GeoGebra applets separately using a proxy server
if (event.request.url.startsWith('https://www.geogebra.org/')) {
event.respondWith(
fetch(event.request)
.then((fetchResponse) => {
// Check if the response is valid and cacheable
if (!fetchResponse || fetchResponse.status !== 200 || fetchResponse.type !== 'basic') {
return fetchResponse;
}
const responseToCache = fetchResponse.clone();
caches.open('app-cache').then((cache) => {
cache.put(event.request, responseToCache);
});
return fetchResponse;
})
.catch((error) => {
console.error('Fetch error:', error);
// Respond with the offline page as a fallback
return caches.match('offline.html'); // Update with the correct path
})
);
} else {
// Handle other requests using cache
event.respondWith(
caches.match(event.request).then((response) => {
if (response) {
return response;
}
return fetch(event.request)
.then((fetchResponse) => {
// Check if the response is valid and cacheable
if (!fetchResponse || fetchResponse.status !== 200 || fetchResponse.type !== 'basic') {
return fetchResponse;
}
const responseToCache = fetchResponse.clone();
caches.open('app-cache').then((cache) => {
cache.put(event.request, responseToCache);
});
return fetchResponse;
})
.catch((error) => {
console.error('Fetch error:', error);
// Respond with the offline page as a fallback
return caches.match('offline.html'); // Update with the correct path
});
})
);
}
});
self.addEventListener('message', event => {
if (event.data.type === 'my-message') {
event.waitUntil(
// Perform asynchronous operations here
// and then send a response back to the main thread
self.clients.matchAll().then(clients => {
// Respond to the main thread
clients.forEach(client => {
client.postMessage({ type: 'my-response', data: 'response-data' });
});
})
);
}
});
// service-worker.js
self.addEventListener('message', event => {
if (event.data.type === 'perform-async-operation') {
event.waitUntil(
performAsyncOperation(event.data.payload)
.then(result => {
// Respond to the main thread with a success message
self.clients.matchAll().then(clients => {
clients.forEach(client => {
client.postMessage({ type: 'async-operation-result', success: true, data: result });
});
});
})
.catch(error => {
// Respond to the main thread with an error message
self.clients.matchAll().then(clients => {
clients.forEach(client => {
client.postMessage({ type: 'async-operation-result', success: false, error: error.message });
});
});
})
);
}
});
function performAsyncOperation(payload) {
return new Promise((resolve, reject) => {
// Simulate an async operation, e.g., fetching data from a remote server
setTimeout(() => {
const randomValue = Math.random();
if (randomValue > 0.5) {
resolve(`Async operation successful with payload: ${JSON.stringify(payload)}`);
} else {
reject(new Error('Async operation failed'));
}
}, 2000); // Simulate a delay
});
}
JavaScript:
// script.js - Handling info button click and service worker registration
document.addEventListener("DOMContentLoaded", function () {
const infoButtons = document.querySelectorAll(".info-button");
infoButtons.forEach(button => {
button.addEventListener("click", function (event) {
event.preventDefault();
const applet = button.closest(".applet");
applet.classList.toggle("info-open");
});
});
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('sw.js')
.then((registration) => {
console.log('Service Worker registered with scope:', registration.scope);
})
.catch((error) => {
console.log('Service Worker registration failed:', error);
});
}
});
// Service worker fetch event handling
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then((response) => {
if (response) {
return response;
}
return fetch(event.request).then((response) => {
if (!response || response.status !== 200 || response.type !== 'basic') {
return response;
}
const responseToCache = response.clone();
caches.open('app-cache').then((cache) => {
cache.put(event.request, responseToCache);
});
return response;
});
}).catch((error) => {
console.error('Fetch error:', error);
// You can provide a fallback response here if needed.
})
);
});
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then((response) => {
if (response) {
return response;
}
return fetch(event.request)
.then((response) => {
// Check if the response is valid and cacheable
if (!response || response.status !== 200 || response.type !== 'basic') {
return response;
}
const responseToCache = response.clone();
caches.open('app-cache').then((cache) => {
cache.put(event.request, responseToCache);
});
return response;
})
.catch((error) => {
console.error('Fetch error:', error);
// Respond with the offline page as a fallback
return caches.match('offline.html'); // Update with the correct path
});
})
);
});
// script.js - Handling info button click and service worker registration
document.addEventListener("DOMContentLoaded", function () {
const infoButtons = document.querySelectorAll(".info-button");
infoButtons.forEach(button => {
button.addEventListener("click", function (event) {
event.preventDefault();
const applet = button.closest(".applet");
applet.classList.toggle("info-open");
});
});
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('sw.js')
.then((registration) => {
console.log('Service Worker registered with scope:', registration.scope);
})
.catch((error) => {
console.log('Service Worker registration failed:', error);
});
}
});