import { Lens , Prism , PropExpr } from './../lens' ; import { structEq , Option } from './../utils' ; import { Observable , Subscriber , Subscription , BehaviorSubject , combineLatest } from 'rxjs' ; template T export interface ReadOnlyAtom < T > extends Observable < T > { example returns get ( ) : T ; example returns view ( ) : ReadOnlyAtom < T > ; example param getter returns view < U > ( getter ) : ReadOnlyAtom < U > ; param lens returns view < U > ( lens ) : ReadOnlyAtom < U > ; param prism returns view < U > ( prism ) : ReadOnlyAtom < Option < U > > ; /**
     * View this atom at a property of given name.
     */ view < K extends keyof T > ( k ) : ReadOnlyAtom < T [ K ] > ; /**
     * View this atom at a give property path.
     */ view < K1 extends keyof T , K2 extends keyof T [ K1 ] > ( k1 , k2 ) : ReadOnlyAtom < T [ K1 ] [ K2 ] > ; /**
     * View this atom at a give property path.
     */ view < K1 extends keyof T , K2 extends keyof T [ K1 ] , K3 extends keyof T [ K1 ] [ K2 ] > ( k1 , k2 , k3 ) : ReadOnlyAtom < T [ K1 ] [ K2 ] [ K3 ] > ; /**
     * View this atom at a give property path.
     */ view < K1 extends keyof T , K2 extends keyof T [ K1 ] , K3 extends keyof T [ K1 ] [ K2 ] , K4 extends keyof T [ K1 ] [ K2 ] [ K3 ] > ( k1 , k2 , k3 , k4 ) : ReadOnlyAtom < T [ K1 ] [ K2 ] [ K3 ] [ K4 ] > ; /**
     * View this atom at a give property path.
     */ view < K1 extends keyof T , K2 extends keyof T [ K1 ] , K3 extends keyof T [ K1 ] [ K2 ] , K4 extends keyof T [ K1 ] [ K2 ] [ K3 ] , K5 extends keyof T [ K1 ] [ K2 ] [ K3 ] [ K4 ] > ( k1 , k2 , k3 , k4 , k5 ) : ReadOnlyAtom < T [ K1 ] [ K2 ] [ K3 ] [ K4 ] [ K5 ] > ; } template T export interface Atom < T > extends ReadOnlyAtom < T > { param updateFn modify ( updateFn ) : void ; param newValue set ( newValue ) : void ; example template U param propExpr returns lens < U > ( propExpr ) : Atom < U > ; template U param lens returns lens < U > ( lens ) : Atom < U > ; /**
     * Create a lensed atom that's focused on a property of given name.
     */ lens < K extends keyof T > ( k ) : Atom < T [ K ] > ; /**
     * Create a lensed atom that's focused on a given property path.
     */ lens < K1 extends keyof T , K2 extends keyof T [ K1 ] > ( k1 , k2 ) : Atom < T [ K1 ] [ K2 ] > ; /**
     * Create a lensed atom that's focused on a given property path.
     */ lens < K1 extends keyof T , K2 extends keyof T [ K1 ] , K3 extends keyof T [ K1 ] [ K2 ] > ( k1 , k2 , k3 ) : Atom < T [ K1 ] [ K2 ] [ K3 ] > ; /**
     * Create a lensed atom that's focused on a given property path.
     */ lens < K1 extends keyof T , K2 extends keyof T [ K1 ] , K3 extends keyof T [ K1 ] [ K2 ] , K4 extends keyof T [ K1 ] [ K2 ] [ K3 ] > ( k1 , k2 , k3 , k4 ) : Atom < T [ K1 ] [ K2 ] [ K3 ] [ K4 ] > ; /**
     * Create a lensed atom that's focused on a given property path.
     */ lens < K1 extends keyof T , K2 extends keyof T [ K1 ] , K3 extends keyof T [ K1 ] [ K2 ] , K4 extends keyof T [ K1 ] [ K2 ] [ K3 ] , K5 extends keyof T [ K1 ] [ K2 ] [ K3 ] [ K4 ] > ( k1 , k2 , k3 , k4 , k5 ) : Atom < T [ K1 ] [ K2 ] [ K3 ] [ K4 ] [ K5 ] > ; } export abstract class AbstractReadOnlyAtom < T > extends BehaviorSubject < T > implements ReadOnlyAtom < T > { abstract get ( ) ; view ( ) ; view < U > ( getter ) ; view < U > ( lens ) ; view < U > ( prism ) ; view < K extends keyof T > ( k ) ; view < U > ( ... args ) { return args [ 0 ] !== undefined ? typeof args [ 0 ] === 'function' ? new AtomViewImpl < T , U > ( this , args [ 0 ] as ( x : T ) => U ) : typeof args [ 0 ] === 'string' ? new AtomViewImpl < T , U > ( this , Lens . compose < T , U > ( ... args . map ( Lens . key ( ) ) ) . get ) : new AtomViewImpl < T , U > ( this , x => ( args [ 0 ] as Lens < T , U > ) . get ( x ) ) : this as ReadOnlyAtom < T > ; } } export abstract class AbstractAtom < T > extends AbstractReadOnlyAtom < T > implements Atom < T > { abstract modify ( updateFn ) ; set ( x ) { this . modify ( ( ) => x ) ; } lens < U > ( propExpr ) ; lens < U > ( lens ) ; lens < K extends keyof T > ( k ) ; lens < U > ( arg1 , ... args ) { return typeof arg1 === 'function' ? new LensedAtom < T , U > ( this , Lens . prop ( arg1 as ( x : T ) => U ) , structEq ) : typeof arg1 === 'string' ? new LensedAtom ( this , Lens . compose ( Lens . key ( arg1 ) , ... args . map ( k => Lens . key ( k ) ) ) , structEq ) : new LensedAtom < T , U > ( this , arg1 as Lens < T , U > , structEq ) ; } } export class JsonAtom < T > extends AbstractAtom < T > { constructor ( initialValue ) { super ( initialValue ) ; } get ( ) { return this . getValue ( ) ; } modify ( updateFn ) { const prevValue = this . getValue ( ) ; const next = updateFn ( prevValue ) ; if ( ! structEq ( prevValue , next ) ) this . next ( next ) ; } set ( x ) { const prevValue = this . getValue ( ) ; if ( ! structEq ( prevValue , x ) ) this . next ( x ) ; } } class LensedAtom < TSource , TDest > extends AbstractAtom < TDest > { constructor ( private _source , private _lens , private _eq = structEq ) { super ( undefined ! ) ; } get ( ) { return this . _subscription ? this . getValue ( ) : this . _lens . get ( this . _source . get ( ) ) ; } modify ( updateFn ) { this . _source . modify ( x => this . _lens . modify ( updateFn , x ) ) ; } set ( newValue ) { this . _source . modify ( x => this . _lens . set ( newValue , x ) ) ; } private _onSourceValue ( x ) { const prevValue = this . getValue ( ) ; const next = this . _lens . get ( x ) ; if ( ! this . _eq ( prevValue , next ) ) this . next ( next ) ; } private _subscription : Subscription | null = null ; private _refCount = 0 ; _subscribe ( subscriber ) { if ( ! this . _subscription ) { this . _subscription = this . _source . subscribe ( x => this . _onSourceValue ( x ) ) ; } this . _refCount ++ ; const sub = new Subscription ( ( ) => { if ( -- this . _refCount <= 0 && this . _subscription ) { this . _subscription . unsubscribe ( ) ; this . _subscription = null ; } } ) ; sub . add ( super . _subscribe ( subscriber ) ) ; return sub ; } unsubscribe ( ) { if ( this . _subscription ) { this . _subscription . unsubscribe ( ) ; this . _subscription = null ; } this . _refCount = 0 ; super . unsubscribe ( ) ; } } class AtomViewImpl < TSource , TDest > extends AbstractReadOnlyAtom < TDest > { constructor ( private _source , private _getter , private _eq = structEq ) { super ( undefined ! ) ; } get ( ) { return this . _subscription ? this . getValue ( ) : this . _getter ( this . _source . get ( ) ) ; } private _onSourceValue ( x ) { const prevValue = this . getValue ( ) ; const next = this . _getter ( x ) ; if ( ! this . _eq ( prevValue , next ) ) this . next ( next ) ; } private _subscription : Subscription | null = null ; private _refCount = 0 ; _subscribe ( subscriber ) { if ( ! this . _subscription ) { this . _subscription = this . _source . subscribe ( x => this . _onSourceValue ( x ) ) ; } this . _refCount ++ ; const sub = new Subscription ( ( ) => { if ( -- this . _refCount <= 0 && this . _subscription ) { this . _subscription . unsubscribe ( ) ; this . _subscription = null ; } } ) ; sub . add ( super . _subscribe ( subscriber ) ) ; return sub ; } unsubscribe ( ) { if ( this . _subscription ) { this . _subscription . unsubscribe ( ) ; this . _subscription = null ; } this . _refCount = 0 ; super . unsubscribe ( ) ; } } export class CombinedAtomViewImpl < TResult > extends AbstractReadOnlyAtom < TResult > { constructor ( private _sources , private _combineFn , private _eq = structEq ) { super ( undefined ! ) ; } get ( ) { return this . _subscription ? this . getValue ( ) : this . _combineFn ( this . _sources . map ( x => x . get ( ) ) ) ; } private _onSourceValues ( xs ) { const prevValue = this . getValue ( ) ; const next = this . _combineFn ( xs ) ; if ( ! this . _eq ( prevValue , next ) ) this . next ( next ) ; } private _subscription : Subscription | null = null ; private _refCount = 0 ; _subscribe ( subscriber ) { if ( ! this . _subscription ) { this . _subscription = combineLatest ( this . _sources ) . subscribe ( xs => this . _onSourceValues ( xs ) ) ; } this . _refCount ++ ; const sub = new Subscription ( ( ) => { if ( -- this . _refCount <= 0 && this . _subscription ) { this . _subscription . unsubscribe ( ) ; this . _subscription = null ; } } ) ; sub . add ( super . _subscribe ( subscriber ) ) ; return sub ; } unsubscribe ( ) { if ( this . _subscription ) { this . _subscription . unsubscribe ( ) ; this . _subscription = null ; } this . _refCount = 0 ; super . unsubscribe ( ) ; } }