import DataProvider from "../../utils/PublisherProxy"; import { ConnectedComponent, setSubscribable } from "./common"; import { extractDynamicDependencies, resolveDynamicPath } from "./dynamicPath"; import { onAssignDynamicWatchKeys, registerDynamicPropertyWatcher, } from "./dynamicPropertyWatch"; import { getPublisherFromPath } from "./publisherPath"; type Callback = (...values: unknown[]) => void; type PathConfiguration = { originalPath: string; dynamicDependencies: string[]; isDynamic: boolean; }; type Configuration = { callbacks: Set; publisher: DataProvider | null; onAssign: (value: unknown) => void; unsubscribePublisher: (() => void) | null; pathConfig: PathConfiguration; index: number; }; export function onAssign(...values: Array) { const pathConfigs: PathConfiguration[] = values.map((path) => { const dynamicDependencies = extractDynamicDependencies(path); return { originalPath: path, dynamicDependencies, isDynamic: dynamicDependencies.length > 0, }; }); return function ( target: unknown, _propertyKey: string, descriptor: PropertyDescriptor, ) { setSubscribable(target); const stateKey = `__onAssign_state_${_propertyKey}__`; let callback: Callback; (target as ConnectedComponent).__onConnected__((component) => { const state = (component as any)[stateKey] || ((component as any)[stateKey] = { cleanupWatchers: [] as Array<() => void>, configurations: [] as Configuration[], }); // Nettoyage des watchers et configurations précédentes state.cleanupWatchers.forEach((cleanup: () => void) => cleanup()); state.cleanupWatchers = []; state.configurations.forEach((conf: Configuration) => { if (conf.unsubscribePublisher) { conf.unsubscribePublisher(); } }); state.configurations = []; const onAssignValues: unknown[] = []; const confs: Configuration[] = []; // Initialisation des configurations for (let i = 0; i < values.length; i++) { const pathConfig = pathConfigs[i]; const callbacks: Set = new Set(); const onAssign = (assignedValue: unknown) => { onAssignValues[i] = assignedValue; if ( onAssignValues.filter((v) => v !== null && v !== undefined) .length === values.length ) { callbacks.forEach((callback) => callback(...onAssignValues)); } }; confs.push({ publisher: null, onAssign, callbacks, unsubscribePublisher: null, pathConfig, index: i, }); } const subscribeToPath = ( conf: Configuration, resolvedPath: string | null, ) => { // Désabonnement de l'ancien publisher if (conf.unsubscribePublisher) { conf.unsubscribePublisher(); conf.unsubscribePublisher = null; } // Réinitialiser la valeur pour ce chemin lors du changement onAssignValues[conf.index] = null; conf.publisher = null; if (!resolvedPath) { return; } const publisher = getPublisherFromPath(resolvedPath); if (!publisher) { return; } publisher.onAssign(conf.onAssign); conf.unsubscribePublisher = () => { publisher.offAssign(conf.onAssign); if (conf.publisher === publisher) { conf.publisher = null; } }; conf.publisher = publisher; }; const refreshSubscriptions = () => { for (const conf of confs) { if (conf.pathConfig.isDynamic) { const resolution = resolveDynamicPath( component, conf.pathConfig.originalPath, ); if (!resolution.ready) { subscribeToPath(conf, null); continue; } subscribeToPath(conf, resolution.path); } else { subscribeToPath(conf, conf.pathConfig.originalPath); } } }; // Enregistrement des watchers pour les chemins dynamiques for (const conf of confs) { if (conf.pathConfig.isDynamic) { for (const dependency of conf.pathConfig.dynamicDependencies) { const unsubscribe = registerDynamicPropertyWatcher( onAssignDynamicWatchKeys.watcherStore, onAssignDynamicWatchKeys.hooked, component, dependency, () => refreshSubscriptions(), ); state.cleanupWatchers.push(unsubscribe); } } } // Initialisation du callback callback = descriptor.value.bind(component); for (const conf of confs) { conf.callbacks.add(callback); } // Initialisation des abonnements refreshSubscriptions(); state.configurations = confs; }); (target as ConnectedComponent).__onDisconnected__((component) => { const state = (component as any)[stateKey]; if (!state) return; state.cleanupWatchers.forEach((cleanup: () => void) => cleanup()); state.cleanupWatchers = []; state.configurations.forEach((conf: Configuration) => { if (conf.unsubscribePublisher) { conf.unsubscribePublisher(); } conf.callbacks.delete(callback); }); state.configurations = []; }); }; }