@mobileaction/ui-modules
Version:
Mobile Action common modules for Vue projects
163 lines (146 loc) • 4.92 kB
JavaScript
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
});
});
}