import {IdbLocalStorage} from "../../Web/IdbLocalStorage" import {UIEventSource} from "../../UIEventSource" /** * A class which allows to read/write a tile to local storage. * * Does the heavy lifting for LocalStorageFeatureSource and SaveFeatureToLocalStorage. * * Note: OSM-features with a negative id are ignored */ export default class TileLocalStorage { private static perLayer: Record> = {} private static readonly useIndexedDb = typeof indexedDB !== "undefined" private readonly _layername: string private readonly inUse = new UIEventSource(false) private readonly cachedSources: Record & { flush: () => void }> = {} private readonly _maxAgeSeconds: number; private constructor(layername: string, maxAgeSeconds: number) { this._layername = layername this._maxAgeSeconds = maxAgeSeconds; } public static construct(backend: string, layername: string, maxAgeS: number): TileLocalStorage { const key = backend + "_" + layername const cached = TileLocalStorage.perLayer[key] if (cached) { return cached } const tls = new TileLocalStorage(key, maxAgeS) TileLocalStorage.perLayer[key] = tls return tls } /** * Constructs a UIEventSource element which is synced with localStorage. * Supports 'flush' */ public getTileSource(tileIndex: number): UIEventSource & { flush: () => void } { const cached = this.cachedSources[tileIndex] if (cached) { return cached } const src = & { flush: () => void }>( UIEventSource.FromPromise(this.GetIdb(tileIndex)) ) src.flush = () => this.SetIdb(tileIndex, src.data) src.addCallbackD((data) => this.SetIdb(tileIndex, data)) this.cachedSources[tileIndex] = src return src } private async SetIdb(tileIndex: number, data: any): Promise { if (!TileLocalStorage.useIndexedDb) { return } try { await this.inUse.AsPromise((inUse) => !inUse) this.inUse.setData(true) await IdbLocalStorage.SetDirectly(this._layername + "_" + tileIndex, data) await IdbLocalStorage.SetDirectly(this._layername + "_" + tileIndex + "_date", Date.now()) this.inUse.setData(false) } catch (e) { console.error( "Could not save tile to indexed-db: ", e, "tileIndex is:", tileIndex, "for layer", this._layername, "data is", data ) } } private async GetIdb(tileIndex: number): Promise { if (!TileLocalStorage.useIndexedDb) { return undefined } const date = await IdbLocalStorage.GetDirectly(this._layername + "_" + tileIndex + "_date") const maxAge = this._maxAgeSeconds const timeDiff = Date.now() - date if (timeDiff >= maxAge) { console.debug("Dropping cache for", this._layername, tileIndex, "out of date") await IdbLocalStorage.SetDirectly(this._layername + "_" + tileIndex, undefined) return undefined } const data = await IdbLocalStorage.GetDirectly(this._layername + "_" + tileIndex) return data } invalidate(zoomlevel: number, tileIndex) { this.getTileSource(tileIndex).setData(undefined) } }