Sindbad~EG File Manager
// Service Worker for Church Management System PWA
const CACHE_NAME = 'church-app-v1.0.0';
const OFFLINE_URL = '/copmadinaarea/offline.html';
// Assets to cache on install
const STATIC_CACHE = [
'/copmadinaarea/',
'/copmadinaarea/index.php',
'/copmadinaarea/manifest.json',
'/copmadinaarea/offline.html',
// Add critical CSS/JS if you have them as separate files
];
// Dynamic cache for pages
const DYNAMIC_CACHE = 'church-app-dynamic-v1.0.0';
// Cache size limits
const CACHE_LIMITS = {
pages: 50,
images: 100
};
// Install event - cache static assets
self.addEventListener('install', (event) => {
console.log('[Service Worker] Installing...');
event.waitUntil(
caches.open(CACHE_NAME)
.then((cache) => {
console.log('[Service Worker] Caching static assets');
return cache.addAll(STATIC_CACHE);
})
.then(() => self.skipWaiting())
);
});
// Activate event - clean up old caches
self.addEventListener('activate', (event) => {
console.log('[Service Worker] Activating...');
event.waitUntil(
caches.keys().then((cacheNames) => {
return Promise.all(
cacheNames.map((cacheName) => {
if (cacheName !== CACHE_NAME && cacheName !== DYNAMIC_CACHE) {
console.log('[Service Worker] Deleting old cache:', cacheName);
return caches.delete(cacheName);
}
})
);
}).then(() => self.clients.claim())
);
});
// Fetch event - serve from cache, fallback to network
self.addEventListener('fetch', (event) => {
const { request } = event;
const url = new URL(request.url);
// Skip cross-origin requests
if (url.origin !== location.origin) {
return;
}
// Skip API calls from caching (always fetch fresh)
if (url.pathname.includes('/api/')) {
event.respondWith(
fetch(request).catch(() => {
return new Response(JSON.stringify({
success: false,
message: 'You are offline. Please check your internet connection.'
}), {
headers: { 'Content-Type': 'application/json' }
});
})
);
return;
}
// Network-first strategy for PHP pages
if (request.url.endsWith('.php') || request.url.includes('?')) {
event.respondWith(
fetch(request)
.then((response) => {
// Only cache GET requests (POST requests can't be cached)
if (request.method === 'GET' && response.status === 200) {
// Clone the response
const responseClone = response.clone();
// Cache successful GET responses
caches.open(DYNAMIC_CACHE).then((cache) => {
cache.put(request, responseClone);
limitCacheSize(DYNAMIC_CACHE, CACHE_LIMITS.pages);
});
}
return response;
})
.catch(() => {
// Try to get from cache
return caches.match(request).then((cachedResponse) => {
if (cachedResponse) {
return cachedResponse;
}
// Show offline page for navigation requests
if (request.mode === 'navigate') {
return caches.match(OFFLINE_URL);
}
return new Response('Offline - content not available', {
status: 503,
statusText: 'Service Unavailable'
});
});
})
);
return;
}
// Cache-first strategy for static assets (images, CSS, JS)
event.respondWith(
caches.match(request).then((cachedResponse) => {
if (cachedResponse) {
return cachedResponse;
}
return fetch(request).then((response) => {
// Don't cache non-successful responses
if (!response || response.status !== 200 || response.type === 'error') {
return response;
}
const responseClone = response.clone();
caches.open(DYNAMIC_CACHE).then((cache) => {
cache.put(request, responseClone);
// Limit cache size based on file type
if (request.url.match(/\.(jpg|jpeg|png|gif|svg|webp)$/)) {
limitCacheSize(DYNAMIC_CACHE, CACHE_LIMITS.images);
}
});
return response;
});
})
);
});
// Background Sync - for offline form submissions
self.addEventListener('sync', (event) => {
console.log('[Service Worker] Background sync:', event.tag);
if (event.tag === 'sync-forms') {
event.waitUntil(syncForms());
}
});
// Push Notification
self.addEventListener('push', (event) => {
console.log('[Service Worker] Push received');
let notificationData = {
title: 'Church App',
body: 'You have a new notification',
icon: '/copmadinaarea/assets/icons/icon-192x192.png',
badge: '/copmadinaarea/assets/icons/icon-72x72.png',
vibrate: [200, 100, 200],
data: {
url: '/copmadinaarea/'
}
};
if (event.data) {
try {
notificationData = event.data.json();
} catch (e) {
notificationData.body = event.data.text();
}
}
event.waitUntil(
self.registration.showNotification(notificationData.title, notificationData)
);
});
// Notification click
self.addEventListener('notificationclick', (event) => {
console.log('[Service Worker] Notification clicked');
event.notification.close();
const urlToOpen = event.notification.data?.url || '/copmadinaarea/';
event.waitUntil(
clients.matchAll({ type: 'window', includeUncontrolled: true })
.then((clientList) => {
// Check if app is already open
for (let client of clientList) {
if (client.url === urlToOpen && 'focus' in client) {
return client.focus();
}
}
// Open new window
if (clients.openWindow) {
return clients.openWindow(urlToOpen);
}
})
);
});
// Helper function to limit cache size
function limitCacheSize(cacheName, maxItems) {
caches.open(cacheName).then((cache) => {
cache.keys().then((keys) => {
if (keys.length > maxItems) {
cache.delete(keys[0]).then(() => {
limitCacheSize(cacheName, maxItems);
});
}
});
});
}
// Helper function to sync offline forms
async function syncForms() {
const cache = await caches.open('offline-forms');
const requests = await cache.keys();
return Promise.all(
requests.map(async (request) => {
try {
const response = await fetch(request.clone());
if (response.ok) {
await cache.delete(request);
}
return response;
} catch (error) {
console.error('Sync failed for:', request.url, error);
}
})
);
}
// Message handler for communication with main app
self.addEventListener('message', (event) => {
console.log('[Service Worker] Message received:', event.data);
if (event.data.action === 'skipWaiting') {
self.skipWaiting();
}
if (event.data.action === 'clearCache') {
event.waitUntil(
caches.keys().then((cacheNames) => {
return Promise.all(
cacheNames.map((cacheName) => caches.delete(cacheName))
);
})
);
}
});
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists