UNPKG

@mobileaction/ui-modules

Version:

Mobile Action common modules for Vue projects

163 lines (146 loc) 4.92 kB
import { MaLogger } from './MaLogger.js'; const LAST_CLEARED_KEY = 'MA_LAST_CLEARED'; class FallbackStorage { constructor() { this.storage = {}; } getItem(k) { return k in this.storage ? this.storage[k] : null; } setItem(k, v) { this.storage[k] = v; } } export const FALLBACK_STORAGE = new FallbackStorage(); export class MaStorage { constructor(storageGetter, { cacheTimeout, cachePrefix, keptKeys, keptRawKeys }) { // add prefixes to keys const keptPrefixedKeys = [...(keptKeys || []).map(k => `${ cachePrefix }${ k }`), ...(keptRawKeys || [])]; this.$log = new MaLogger(null, { name: 'MaStorage', isDebug: false, }); // hide storage properties so only reducers show up Object.defineProperties(this, { storage: { get: () => storageGetter() || FALLBACK_STORAGE, enumerable: false, }, cacheTimeout: { value: cacheTimeout, writable: false, enumerable: false, }, cachePrefix: { value: cachePrefix, writable: false, enumerable: false, }, keptKeys: { value: keptPrefixedKeys, writable: false, enumerable: false, }, }); } set(param, data) { try { const item = JSON.stringify(data); this.storage.setItem(param, item); return true; } catch (e) { this.$log.error('Error: cannot set value. Possible circular data'); return false; } } clear(everything = false) { if (everything) { this.storage.clear(); return; } const backup = {}; this.keptKeys.forEach((k) => { // store keyed items backup[k] = this.storage.getItem(k); }); this.storage.clear(); Object.keys(backup).forEach((k) => { this.storage.setItem(k, backup[k]); }); this.set(LAST_CLEARED_KEY, new Date().getTime()); } get(param) { if (this.cacheTimeout > 0) { const lastCleared = this.storage.getItem(LAST_CLEARED_KEY); // put last cleared as now if first time if (lastCleared === null) { this.set(LAST_CLEARED_KEY, new Date().getTime()); } else if (new Date().getTime() - lastCleared > this.cacheTimeout) { // clear if old this.clear(); } } const item = this.storage.getItem(param); try { return JSON.parse(item); } catch (e) { return null; } } del(key) { try { this.storage.removeItem(key); } catch (e) { this.$log.error('Failed to delete key from storage: ', key); } } reduce(key, value) { const maKey = `${ this.cachePrefix }${ key }`; if (typeof value !== 'undefined') { this.set(maKey, value); return value; } return this.get(maKey); } } export default MaStorage; export function insertReducerProps(storageIns, reducers) { // add reducers from options reducers.forEach((_r) => { let r = _r; // if there is a reducer use it otherwise use default reducer if (typeof r === 'string') { // if only name is given convert to object r = { name: r }; } if (typeof r.name !== 'string' || r.name.length === 0) { // name must be given throw new Error(`Invalid reducer name given: ${ r.name }`); } if (!r.reducer) { // use var name as storage key r.reducer = r.name; } if (r.default) { if (r.getter) { this.$log.warn(`MaStorage: "default" property will overwrite "getter" method for ${ r.name }`); } r.getter = v => v || r.default; } if (typeof r.getter !== 'function') { r.getter = v => v; } if (typeof r.setter !== 'function') { r.setter = v => v; } if (typeof r.reducer === 'string') { let reducerKey = r.reducer; // if default is given return default if value is non true r.reducer = v => r.getter(storageIns.reduce(reducerKey, typeof v !== 'undefined' ? r.setter(v) : v)); } if (typeof r.reducer !== 'function') { throw new Error(`Invalid reducer function provided: ${ r.reducer }`); } // add these as magic properties Object.defineProperty(storageIns, r.name, { get: r.reducer, set: r.reducer, enumerable: true, // show up in for ... in ... statements }); }); }