Spaces:
Running
Running
File size: 7,132 Bytes
264ecd0 d7493ec 1455aca 264ecd0 d06b99c 264ecd0 1455aca 264ecd0 1455aca 264ecd0 1455aca 264ecd0 1455aca 264ecd0 1455aca 264ecd0 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 |
// TreeTrack Service Worker - PWA and Offline Support
const VERSION = 1755116236; // Cache busting bump - force clients to fetch new static assets and header image change
const CACHE_NAME = `treetrack-v${VERSION}`;
const STATIC_CACHE = `static-v${VERSION}`;
const API_CACHE = `api-v${VERSION}`;
// Check if we're in development mode
const isDevelopment = self.location.hostname === 'localhost' || self.location.hostname === '127.0.0.1';
const urlsToCache = [
'/static/',
'/static/index.html',
'/static/map.html',
'/static/js/tree-track-app.js',
'/static/js/modules/auth-manager.js',
'/static/js/modules/api-client.js',
'/static/js/modules/ui-manager.js',
'/static/js/modules/form-manager.js',
'/static/js/modules/autocomplete-manager.js',
'/static/js/modules/media-manager.js',
'/static/map.js',
'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css',
'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js',
'https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap'
];
// Install event - cache resources
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => {
console.log('Opened cache');
return cache.addAll(urlsToCache.map(url => new Request(url, {cache: 'reload'})));
})
.catch(error => {
console.log('Cache install failed:', error);
})
);
});
// Fetch event - serve cached content when offline
self.addEventListener('fetch', event => {
// Skip non-GET requests
if (event.request.method !== 'GET') {
return;
}
// Skip requests to API endpoints - let them fail gracefully
if (event.request.url.includes('/api/') || event.request.url.includes('/trees')) {
return;
}
// In development mode, always fetch fresh content for static files
// Also detect Hugging Face Spaces development environment
const isHFDevelopment = self.location.hostname.includes('hf.space') ||
self.location.hostname.includes('huggingface.co');
if ((isDevelopment || isHFDevelopment) && event.request.url.includes('/static/')) {
console.log('Development mode: bypassing cache for', event.request.url);
event.respondWith(
fetch(event.request, {
cache: 'no-cache',
headers: {
'Cache-Control': 'no-cache, no-store, must-revalidate',
'Pragma': 'no-cache'
}
})
.catch(() => {
console.log('Network failed, using cache fallback for', event.request.url);
return caches.match(event.request);
})
);
return;
}
event.respondWith(
caches.match(event.request)
.then(response => {
// In production, use cache-first strategy
if (response && !isDevelopment) {
return response;
}
// Fetch with cache busting in development
const fetchRequest = isDevelopment ?
new Request(event.request.url + '?_=' + Date.now()) :
event.request;
return fetch(fetchRequest).then(response => {
// Check if we received a valid response
if (!response || response.status !== 200 || response.type !== 'basic') {
return response;
}
// Clone the response
const responseToCache = response.clone();
// Only cache in production or for offline fallbacks
if (!isDevelopment) {
caches.open(CACHE_NAME)
.then(cache => {
cache.put(event.request, responseToCache);
});
}
return response;
}).catch(() => {
// If fetch fails, try to return a cached fallback
if (event.request.destination === 'document') {
return caches.match('/static/index.html');
}
});
})
);
});
// Activate event - clean up old caches
self.addEventListener('activate', event => {
const cacheWhitelist = [CACHE_NAME];
event.waitUntil(
caches.keys().then(cacheNames => {
return Promise.all(
cacheNames.map(cacheName => {
if (cacheWhitelist.indexOf(cacheName) === -1) {
return caches.delete(cacheName);
}
})
);
})
);
});
// Background sync for offline data submission
self.addEventListener('sync', event => {
if (event.tag === 'background-sync') {
event.waitUntil(doBackgroundSync());
}
});
async function doBackgroundSync() {
// Handle any queued tree submissions when back online
try {
const cache = await caches.open('offline-data');
const requests = await cache.keys();
for (const request of requests) {
if (request.url.includes('offline-tree-')) {
const response = await cache.match(request);
const treeData = await response.json();
// Try to submit the data
try {
const submitResponse = await fetch('/api/trees', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(treeData)
});
if (submitResponse.ok) {
// Remove from cache if successful
await cache.delete(request);
console.log('Offline tree data synced successfully');
}
} catch (error) {
console.log('Failed to sync offline data:', error);
}
}
}
} catch (error) {
console.log('Background sync failed:', error);
}
}
// Push notifications (for future enhancement)
self.addEventListener('push', event => {
const options = {
body: event.data ? event.data.text() : 'New tree data available!',
icon: '/static/icon-192x192.png',
badge: '/static/badge-72x72.png',
tag: 'treetrack-notification',
data: {
url: '/static/map.html'
}
};
event.waitUntil(
self.registration.showNotification('TreeTrack', options)
);
});
// Handle notification clicks
self.addEventListener('notificationclick', event => {
event.notification.close();
if (event.notification.data && event.notification.data.url) {
event.waitUntil(
clients.openWindow(event.notification.data.url)
);
}
});
|