import { ConsoleLogger, Logger, NullLogger } from './Logger'; const NAMESPACE = 'travian:v1'; const storage = localStorage; function join(...parts: Array) { return parts.map(p => p.replace(/[:]+$/g, '').replace(/^[:]+/g, '')).join(':'); } interface EmptyObjectFactory { (): T; } interface ObjectMapper { (item: any): T; } interface ObjectMapperOptions { factory?: EmptyObjectFactory; mapper?: ObjectMapper; } function createMapper(options: ObjectMapperOptions): ObjectMapper { const { mapper, factory } = options; if (mapper) { return mapper; } if (factory) { return plain => { let item = factory(); if (typeof plain === 'object' && typeof item === 'object') { return Object.assign(item, plain) as T; } else { return item; } }; } throw new Error('Factory or mapper must be specified'); } export class DataStorage { private readonly logger: Logger; private readonly name: string; constructor(name: string) { this.name = name; // this.logger = new ConsoleLogger(this.constructor.name); this.logger = new NullLogger(); } static onChange(handler: (key: string) => void) { window.addEventListener('storage', ({ key, storageArea }) => { if (storageArea === storage) { handler(key || ''); } }); } get(key: string): any { const fullKey = join(NAMESPACE, this.name, key); try { const serialized = storage.getItem(fullKey); this.logger.log('GET', fullKey, serialized); return JSON.parse(serialized || 'null'); } catch (e) { if (e instanceof SyntaxError) { return null; } throw e; } } getTyped(key: string, options: ObjectMapperOptions = {}): T { let plain = this.get(key); const mapper = createMapper(options); return mapper(plain); } getTypedList(key: string, options: ObjectMapperOptions = {}): Array { let plain = this.get(key); if (!Array.isArray(plain)) { return []; } const mapper = createMapper(options); return (plain as Array).map(mapper); } has(key: string): boolean { const fullKey = join(NAMESPACE, this.name, key); return storage.getItem(fullKey) !== null; } set(key: string, value: any) { const fullKey = join(NAMESPACE, this.name, key); let serialized = JSON.stringify(value); this.logger.log('SET', fullKey, serialized); storage.setItem(fullKey, serialized); } }