UNPKG

@applicaster/zapp-react-native-utils

Version:

Applicaster Zapp React Native utilities package

209 lines (167 loc) 5.7 kB
import { BehaviorSubject } from "rxjs"; import { SingleValueProvider } from "./StorageSingleSelectProvider"; import { createLogger } from "../logger"; import { bridgeLogger } from "../../zapp-react-native-bridge/logger"; import { useScreenStateStore } from "../reactHooks/navigation/useScreenStateStore"; export const { log_debug, log_error } = createLogger({ category: "ScreenSingleValueProvider", subsystem: "zapp-react-native-bridge", parent: bridgeLogger, }); export class ScreenSingleValueProvider implements SingleValueProvider { // @ts-ignore private valueSubject: BehaviorSubject<string | null>; private screenStateStoreUnsubscribe: (() => void) | null = null; private static providers: Record< string, Record<string, SingleValueProvider> > = {}; public static getProvider( key: string, screenRoute: string, screenStateStore: ReturnType<typeof useScreenStateStore> ): SingleValueProvider { if (!key) { throw new Error("ScreenSingleValueProvider: Key is required"); } if (!screenRoute) { throw new Error("ScreenSingleValueProvider: Screen route is required"); } if (!screenStateStore) { throw new Error( "ScreenSingleValueProvider: screen state store is required" ); } if (!this.providers[screenRoute]) { this.providers[screenRoute] = {}; } try { if (!this.providers[screenRoute][key]) { this.providers[screenRoute][key] = new ScreenSingleValueProvider( key, screenRoute, screenStateStore ); } else { ( this.providers[screenRoute][key] as ScreenSingleValueProvider ).updateStore(screenStateStore); } } catch (error) { log_error("Failed to create provider", { key, screenRoute, error }); throw error; } return this.providers[screenRoute][key]; } // @ts-ignore private constructor( private key: string, route: string, private screenStateStore: ReturnType<typeof useScreenStateStore> ) { if (!key) { throw new Error("ScreenSingleValueProvider: Key is required"); } if (!screenStateStore) { throw new Error( "ScreenSingleValueProvider: screen state store is required" ); } this.key = key; this.valueSubject = new BehaviorSubject<string | null>(null); this.setupScreenStateSubscription(); void this.getValueAsync(); log_debug("ScreenSingleValueProvider: Initializing", { key, route }); } private updateStore( screenStateStore: ReturnType<typeof useScreenStateStore> ): void { this.cleanup(); this.screenStateStore = screenStateStore; this.setupScreenStateSubscription(); void this.getValueAsync(); } private setupScreenStateSubscription(): void { this.screenStateStoreUnsubscribe = this.screenStateStore.subscribe( (state) => ({ value: state.data[this.key], exists: this.key in state.data, }) // (current, previous) => { // if (!current.exists && previous.exists) { // log_debug( // `ScreenSingleValueProvider: Key deleted from store: ${this.key}` // ); // // TODO: If we need to handle deletion, we can do it here // } // if (current.value !== previous.value) { // this.valueSubject.next(current.value || null); // log_debug(`ScreenSingleValueProvider: Key updated: ${this.key}`, { // previous: previous.value, // current: current.value, // }); // } // } ); log_debug( `ScreenSingleValueProvider: screen state store subscription setup for key: ${this.key}` ); } private cleanup(): void { if (this.screenStateStoreUnsubscribe) { this.screenStateStoreUnsubscribe(); this.screenStateStoreUnsubscribe = null; log_debug( `ScreenSingleValueProvider: Unsubscribed from screen state store for key: ${this.key}` ); } } clearValue(): Promise<void> { this.screenStateStore.getState().removeValue(this.key); this.valueSubject.next(null); log_debug(`clearValue: value cleared for key: ${this.key}`); return Promise.resolve(); } getObservable(): BehaviorSubject<string | null> { return this.valueSubject; } getValue(): string | null { return this.valueSubject.value; } getValueAsync(): Promise<string | null> { const currentValue = this.screenStateStore.getState().data[this.key]; const value = currentValue || null; const selected = this.getValue(); const valuesAreEqual = value === selected; const bothEmpty = (value === null || value === "") && (selected === null || selected === ""); if (!this.valueSubject.closed && !valuesAreEqual && !bothEmpty) { this.valueSubject.next(value); } return Promise.resolve(value); } setValue(value: string): Promise<void> { this.screenStateStore.getState().setValue(this.key, value); this.valueSubject.next(value); log_debug(`setValue: value set for key: ${this.key}`, { value }); return Promise.resolve(); } // TODO: remove if not needed public static cleanupRoute(screenRoute: string): void { if (this.providers[screenRoute]) { Object.values(this.providers[screenRoute]).forEach((provider) => { (provider as ScreenSingleValueProvider).destroy(); }); delete this.providers[screenRoute]; } } // TODO: remove if not needed public destroy(): void { log_debug( `ScreenSingleValueProvider: Destroying provider for key: ${this.key}` ); this.valueSubject.complete(); this.cleanup(); } }