local conversion
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* BincioActivity Service Worker
|
||||
*
|
||||
* Intercepts requests for /data/* and serves from IndexedDB when local
|
||||
* activities are present, merging them with the bundled static index.
|
||||
*
|
||||
* IndexedDB schema:
|
||||
* db: 'bincio', store: 'files'
|
||||
* key: file path (e.g. '/data/activities/2024-01-01T120000Z-ride.json')
|
||||
* value: { path, data } — data is the parsed JSON object
|
||||
*
|
||||
* Local activity summaries are kept under the special key '/data/local-index'.
|
||||
* The SW merges these with the static index.json at request time so that
|
||||
* activities from the server and from the device appear together in the feed.
|
||||
*/
|
||||
|
||||
const DB_NAME = 'bincio';
|
||||
const DB_VERSION = 1;
|
||||
const STORE = 'files';
|
||||
|
||||
// ── IndexedDB helpers ─────────────────────────────────────────────────────────
|
||||
|
||||
function openDB() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const req = indexedDB.open(DB_NAME, DB_VERSION);
|
||||
req.onupgradeneeded = e => {
|
||||
e.target.result.createObjectStore(STORE, { keyPath: 'path' });
|
||||
};
|
||||
req.onsuccess = e => resolve(e.target.result);
|
||||
req.onerror = e => reject(e.target.error);
|
||||
});
|
||||
}
|
||||
|
||||
async function idbGet(path) {
|
||||
const db = await openDB();
|
||||
return new Promise((resolve, reject) => {
|
||||
const req = db.transaction(STORE, 'readonly').objectStore(STORE).get(path);
|
||||
req.onsuccess = e => resolve(e.target.result?.data ?? null);
|
||||
req.onerror = e => reject(e.target.error);
|
||||
});
|
||||
}
|
||||
|
||||
// ── Fetch intercept ───────────────────────────────────────────────────────────
|
||||
|
||||
self.addEventListener('install', () => self.skipWaiting());
|
||||
self.addEventListener('activate', e => e.waitUntil(self.clients.claim()));
|
||||
|
||||
self.addEventListener('fetch', event => {
|
||||
const url = new URL(event.request.url);
|
||||
|
||||
if (url.pathname === '/data/index.json') {
|
||||
event.respondWith(handleIndex(event.request));
|
||||
return;
|
||||
}
|
||||
|
||||
if (url.pathname.startsWith('/data/activities/')) {
|
||||
event.respondWith(handleActivity(url.pathname, event.request));
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
// Merge local summaries into the server/static index.json
|
||||
async function handleIndex(request) {
|
||||
try {
|
||||
const localSummaries = (await idbGet('/data/local-index')) ?? [];
|
||||
|
||||
if (localSummaries.length === 0) {
|
||||
return fetch(request); // nothing local — pass straight through
|
||||
}
|
||||
|
||||
// Fetch the bundled static index (may fail if offline with no prior cache)
|
||||
let remoteMeta = {};
|
||||
let remoteActivities = [];
|
||||
try {
|
||||
const r = await fetch(request);
|
||||
const raw = await r.json();
|
||||
remoteActivities = raw.activities ?? [];
|
||||
const { activities: _a, ...rest } = raw;
|
||||
remoteMeta = rest;
|
||||
} catch (_) {}
|
||||
|
||||
// Local overrides remote for same ID; new local entries appended
|
||||
const merged = new Map();
|
||||
for (const a of remoteActivities) merged.set(a.id, a);
|
||||
for (const a of localSummaries) merged.set(a.id, a);
|
||||
|
||||
const sorted = [...merged.values()].sort(
|
||||
(a, b) => (b.started_at ?? '').localeCompare(a.started_at ?? '')
|
||||
);
|
||||
|
||||
return jsonResponse({ ...remoteMeta, activities: sorted });
|
||||
} catch (_) {
|
||||
return fetch(request);
|
||||
}
|
||||
}
|
||||
|
||||
// Serve an individual activity file from IDB if present
|
||||
async function handleActivity(path, request) {
|
||||
try {
|
||||
const local = await idbGet(path);
|
||||
if (local !== null) return jsonResponse(local);
|
||||
} catch (_) {}
|
||||
return fetch(request);
|
||||
}
|
||||
|
||||
function jsonResponse(data) {
|
||||
return new Response(JSON.stringify(data), {
|
||||
headers: { 'Content-Type': 'application/json', 'Cache-Control': 'no-store' },
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user