@applicaster/zapp-react-native-bridge
Version:
Applicaster Zapp React Native modules
213 lines (173 loc) • 5.49 kB
text/typescript
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
);
}
};
}