import { bridgeLogger } from "../../zapp-react-native-bridge/logger"; import { BehaviorSubject } from "rxjs"; import { localStorage } from "@applicaster/zapp-react-native-bridge/ZappStorage/LocalStorage"; import { getNamespaceAndKey } from "../appUtils/contextKeysManager/utils"; import { createLogger } from "../logger"; export const { log_verbose, log_debug, log_warning, log_info, log_error } = createLogger({ category: "StorageSingleValueProvider", subsystem: "zapp-react-native-bridge", parent: bridgeLogger, }); export interface SingleValueProvider { getObservable(): BehaviorSubject; setValue(value: string): Promise; getValue(): string | null; getValueAsync(): Promise; clearValue(): Promise; } interface StorageListenerArgs { value: string | null; } export class StorageSingleValueProvider implements SingleValueProvider { // Static cache of providers by namespace private static singleValueProviders: Record< string, StorageSingleValueProvider > = {}; public static getProvider(keyNamespace: string): SingleValueProvider { if (!this.singleValueProviders[keyNamespace]) { this.singleValueProviders[keyNamespace] = new StorageSingleValueProvider( keyNamespace ); } return this.singleValueProviders[keyNamespace]; } private valueSubject: BehaviorSubject; private readonly key: string; private readonly namespace: string; private constructor(keyNamespace: string) { const { namespace, key } = getNamespaceAndKey(keyNamespace); if (!key) { throw new Error("StorageSingleValueProvider: Key is required"); } this.key = key; this.namespace = namespace; localStorage.addListener?.({ key, namespace }, this.reloadValue); this.valueSubject = new BehaviorSubject(null); void this.getValueAsync(); log_debug("StorageSingleValueProvider: Initializing"); } private reloadValue = async ({ value }: StorageListenerArgs) => { log_debug(`reloadValue: request to reload value: ${value}`); this.valueSubject.next(value); }; public getObservable = (): BehaviorSubject => this.valueSubject; private async updateValueAndNotifyObservers(value: string | null) { if (value === null) { await localStorage.removeItem(this.key, this.namespace); } else { await localStorage.setItem(this.key, value, this.namespace); } this.valueSubject.next(value); } public setValue = async (value: string): Promise => { log_debug(`setValue: Setting new value: ${value}`); await this.updateValueAndNotifyObservers(value); }; public clearValue = async (): Promise => { await localStorage.removeItem(this.key, this.namespace); log_debug("clearValue: Removing value"); this.valueSubject.next(null); }; public getValueAsync = async (): Promise => { const value = await localStorage.getItem(this.key, this.namespace); this.valueSubject.next(value); return value; }; public getValue = (): string | null => { return this.valueSubject.getValue(); }; }