mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-19 21:31:46 +00:00
fix(pwa): persist offline storage + Mapbox offline policy (H8, H9) (#1184)
H8: prefetched tiles and file blobs could be evicted under storage pressure (worsened by opaque tile responses inflating the quota ~7MB each), blanking the offline map right when a traveler needs it. Request persistent storage at app init so the browser exempts our caches from eviction. We deliberately keep tile requests no-cors (a cors switch would break self-hosted/custom tile providers without CORS headers), so persistence is the safe mitigation rather than de-opaquing responses. H9: Mapbox GL users had no offline map at all — no runtimeCaching matched the Mapbox hosts. Add a StaleWhileRevalidate rule for api.mapbox.com / *.tiles.mapbox.com so visited areas are available offline (best-effort; full pre-download still requires the Leaflet renderer, now documented). - new sync/persistentStorage.ts requestPersistentStorage(), called from main.tsx - vite.config: mapbox-tiles SW cache rule - MapViewAuto / tilePrefetcher comments document the offline-maps policy - tests for the persist helper (granted / already-persisted / absent / rejects)
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* requestPersistentStorage (H8 / M6) — best-effort persistent storage request
|
||||
* so prefetched tiles / file blobs / IndexedDB aren't evicted under pressure.
|
||||
*/
|
||||
import { describe, it, expect, afterEach, vi } from 'vitest';
|
||||
import { requestPersistentStorage } from '../../../src/sync/persistentStorage';
|
||||
|
||||
const original = (navigator as Navigator & { storage?: StorageManager }).storage;
|
||||
|
||||
afterEach(() => {
|
||||
Object.defineProperty(navigator, 'storage', { value: original, configurable: true });
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
function stubStorage(storage: unknown) {
|
||||
Object.defineProperty(navigator, 'storage', { value: storage, configurable: true });
|
||||
}
|
||||
|
||||
describe('requestPersistentStorage', () => {
|
||||
it('requests persistence when not already granted', async () => {
|
||||
const persist = vi.fn().mockResolvedValue(true);
|
||||
const persisted = vi.fn().mockResolvedValue(false);
|
||||
stubStorage({ persist, persisted });
|
||||
|
||||
expect(await requestPersistentStorage()).toBe(true);
|
||||
expect(persist).toHaveBeenCalledOnce();
|
||||
});
|
||||
|
||||
it('skips the prompt when already persisted', async () => {
|
||||
const persist = vi.fn().mockResolvedValue(true);
|
||||
const persisted = vi.fn().mockResolvedValue(true);
|
||||
stubStorage({ persist, persisted });
|
||||
|
||||
expect(await requestPersistentStorage()).toBe(true);
|
||||
expect(persist).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('returns false (no throw) when the API is unavailable', async () => {
|
||||
stubStorage(undefined);
|
||||
expect(await requestPersistentStorage()).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false (no throw) when persist rejects', async () => {
|
||||
stubStorage({ persist: vi.fn().mockRejectedValue(new Error('denied')) });
|
||||
expect(await requestPersistentStorage()).toBe(false);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user