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