UNPKG

@applicaster/zapp-react-native-bridge

Version:

Applicaster Zapp React Native modules

213 lines (173 loc) 5.49 kB
import { parseJsonIfNeeded } from "@applicaster/zapp-react-native-utils/functionUtils"; import { throwBridgeNotFoundError, throwNativeStorageError, throwUndefinedKeyError, throwUndefinedValueError, getItemError, setItemError, removeItemError, } from "./errors"; import { DEFAULT_NAMESPACE } from "./consts"; import { logger } from "./logger"; const UNDEFINED_KEY_MESSAGE = "Requested key is null or undefined"; const UNDEFINED_VALUE_MESSAGE = "Value to set in storage cannot be null or undefined"; const ALL_KEYS = "ALL_KEYS"; export function stringifyIfNeeded(value) { if (typeof value === "string") return value; return JSON.stringify(value); } export abstract class Storage { private nativeStorageModule: NativeStorageModule; private storageName: string; private listeners: Record<string, Array<StorageListener>>; constructor(storage: string, nativeStorageModule: NativeStorageModule) { if (!nativeStorageModule) throwBridgeNotFoundError(storage); this.nativeStorageModule = nativeStorageModule; this.storageName = storage; this.listeners = {}; } private getListenerKey = (key = ALL_KEYS, namespace = DEFAULT_NAMESPACE) => { return `${key}:${namespace}`; }; // Storage Listeners addListener = ( { key = ALL_KEYS, namespace = DEFAULT_NAMESPACE }, listener: StorageListener ) => { const listenerKey = this.getListenerKey(key, namespace); if (!this.listeners[listenerKey]) this.listeners[listenerKey] = []; this.listeners[listenerKey].push(listener); return () => this.removeListener(key, namespace, listener); }; removeListener = ( key = ALL_KEYS, namespace = DEFAULT_NAMESPACE, listener ) => { const listenerKey = this.getListenerKey(key, namespace); const listenersForKey = this.listeners[listenerKey]; this.listeners[listenerKey] = listenersForKey.filter( (_listener) => _listener !== listener ); }; private invokeListenerFunction = ({ listener, key, namespace = DEFAULT_NAMESPACE, value, }) => { if (listener) { try { listener({ key, namespace, value }); } catch (error) { logger.error({ message: `invokeListenerFunction: throw error: ${ error.message || "no error message" } inStorage: ${ this.storageName } for key: ${key}, namespace: ${namespace} with value: ${value}`, }); } } }; invokeListener = (key, namespace = DEFAULT_NAMESPACE, value) => { const listenersKey = this.getListenerKey(key, namespace); const listenersAllKeys = this.getListenerKey(ALL_KEYS, namespace); const specificListeners = this.listeners[listenersKey] || []; const allListeners = this.listeners[listenersAllKeys] || []; if (allListeners.length === 0 && specificListeners.length === 0) { return; } const listeners = specificListeners.concat(allListeners); listeners.forEach((listener) => { if (listener) { this.invokeListenerFunction({ listener, key, namespace, value }); } }); }; // Storage methods getItem = async ( key, namespace = DEFAULT_NAMESPACE, skipJsonParse = false ) => { if (!key) throwUndefinedKeyError(this.storageName, UNDEFINED_KEY_MESSAGE); try { const result = await this.nativeStorageModule.getItem(key, namespace); if (skipJsonParse) { return result; } return parseJsonIfNeeded(result); } catch (error) { throwNativeStorageError( this.storageName, getItemError(error), key, namespace ); } }; setItem = async (key: string, value: any, namespace = DEFAULT_NAMESPACE) => { if (!key) throwUndefinedKeyError(this.storageName, UNDEFINED_KEY_MESSAGE); if (typeof value === "undefined" || value === null) { throwUndefinedValueError(this.storageName, UNDEFINED_VALUE_MESSAGE, key); } try { const result = await this.nativeStorageModule.setItem( key, stringifyIfNeeded(value), namespace ); if (result) { this.invokeListener(key, namespace, value); } return !!result; } catch (error) { throwNativeStorageError( this.storageName, setItemError(error), key, namespace, value ); } }; removeItem = async (key, namespace = DEFAULT_NAMESPACE) => { if (!key) throwUndefinedKeyError(this.storageName, UNDEFINED_KEY_MESSAGE); try { const result = await this.nativeStorageModule.removeItem(key, namespace); if (result) { this.invokeListener(key, namespace, null); } return !!result; } catch (error) { throwNativeStorageError( this.storageName, removeItemError(error), key, namespace ); } }; getAllItems = async (namespace = DEFAULT_NAMESPACE) => { try { const result = await this.nativeStorageModule.getAllItems(namespace); if (!result) return {}; if (typeof result === "string") return parseJsonIfNeeded(result); return Object.entries(result).reduce((obj, [key, value]) => { if (key === ".namespace") return obj; obj[key] = parseJsonIfNeeded(value); return obj; }, {}); } catch (error) { throwNativeStorageError( this.storageName, getItemError(error), "GET_ALL", namespace ); } }; }