import { from, fromEvent, combineLatest } from 'rxjs' import { map, startWith, tap } from 'rxjs/operators' import isObservable from 'is-observable' import isPromise from 'p-is-promise' import { createBoolean } from '../../to-boolean' const toBool = createBoolean({undefined:true}) class Ready extends Map { constructor(opts) { super(opts.observables) // TODO support setting registration in options this.emitter = typeof opts.emitter.on ==='function' ? opts.emitter : null this.condition = opts.condition || ( (ev) => toBool(ev) ) this.subscriptions = new Map() this._state = {} this._updateObserversList() this.handler = opts.handler || ( ([r,s]) => {console.log('ready:',r,'states:',s,'\n---------------------------')}) } get state() {return this._state} addObserver(name, obs, condition ) { if (!name) return false // name required if (!(obs || this.emitter)) return false // some observable requried if (typeof (obs ||{}).on ==='function') obs = fromEvent(obs,name) // it's an emitter if (isPromise(obs)) obs = from(obs) // it's a promise if (obs && !isObservable(obs) && typeof obs==='function' && arguments.length===2) { condition = obs obs = null } if (!obs && this.emitter) obs = fromEvent(this.emitter,name) if (!obs || !isObservable(obs)) return false this.set(name, obs .pipe( tap((ev) => console.log(name,'emitted/resolved=>',ev)), map(condition||this.condition), tap((b) => console.log('boolean:',b)), startWith(false), ) ) this._updateObserversList() return true } removeObserver(names) { if (!names) this.clear else { if (!Array.isArray(names)) names = [names] names.forEach(name => { this.delete(name) }) } this._updateObserversList() } subscribe(name, handler) { if (typeof name ==='function') { handler=name name = null } this.subscriptions.set(name||'_primary', (name ? this.get(name):this._state).subscribe(handler||this.handler)) } unsubscribe(name) { this.subscriptions.get(name||'_primary').unsubscribe() } _updateObserversList() { this._state = combineLatest(Array.from(this.values())).pipe( // tap((states)=>console.log('states',states)), map(states=> { return [states.reduce((res,state) => {return res && state},true), Array.from(this.keys()).map((name,index) => [name,states[index]])] }) ) } } // const toBoolean = (cond) => // pipe( // map(cond), // startWith(false), // ) const rTrue=['t','true','y','yes','on','positive','up','enabled','affirmative','yea'] const rFalse=['f','false','n','no','off','negative','down','disabled','nope'] function castBoolean (opts={}) { return (value => { if (value===undefined) return opts.undefined if (value===null) return opts.null if (!isNaN(Number(value))) return Number(value) <1 ? false :true if (typeof value==='string') { value = value.trim() value = value.toLowerCase() if ((opts.rTrue || rTrue).includes(value)) return true if ((opts.rFalse || rFalse).includes(value)) return false } return !!value }) } export default Ready export { Ready }