All files / core state.ts

93.33% Statements 28/30
66.67% Branches 4/6
100% Functions 10/10
96.15% Lines 25/26
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109                                                                2x   2x         2x 108x             2x 45x                 2x 6x                   3x   3x   3x 18x 18x 18x 18x       3x       2x 2x 2x 47x     2x                   36x 33x 33x 33x          
import Immutable from './immutable'
 
import 'rxjs/add/operator/share'
import { BehaviorSubject } from 'rxjs/BehaviorSubject'
import { Observable } from 'rxjs/Observable'
import { StateSelector } from './state-selector'
import { Subscription } from 'rxjs/Subscription'
 
/**
 * Defines a stream for changing state in a statex application
 *
 * @example
 *
 * // replace state
 * State.next(state)
 *
 * // subscribe to state stream
 * State.subscribe((state: State) => {
 *   // do your action here
 * })
 *
 * // or listen to a portion of the state
 * State
 *   .select((state: State) => state.application.pageContainer)
 *   .subscribe((state: State) => {
 *     // do your action here
 *   })
 *
 * @export
 * @class StateStream
 * @extends {BehaviorSubject}
 */
export class State {
 
  private static state: State = new State()
 
  private currentState: any
  private subject: BehaviorSubject<any>
 
  static get current() {
    return State.state.currentState
  }
 
  /**
   * Publish next state
   * @param state
   */
  static next(state) {
    State.state.subject.next(state)
  }
 
  /**
   * Subscribe to the stream
   * @param onNext
   * @param onError
   * @param onComplete
   */
  static subscribe(onNext, onError, onComplete): Subscription {
    return State.state.subject.subscribe(onNext, onError, onComplete)
  }
 
  /**
   * Fires 'next' only when the value returned by this function changed from the previous value.
   *
   * @template T
   * @param {StateSelector<T>} selector
   * @returns {Observable<T>}
   */
  static select(selector: StateSelector): Observable<any> {
 
    return Observable.create(subscriber => {
      let previousState: any
      let subscription = this.subscribe(state => {
        let selection = select(state, selector)
        Eif (selection !== select(previousState, selector)) {
          previousState = state
          subscriber.next(selection)
        }
      }, undefined, undefined)
 
      return subscription
    }).share()
  }
 
  constructor() {
    this.currentState = Immutable.from({})
    this.subject = new BehaviorSubject(this.currentState)
    this.subject.subscribe(state => this.currentState = state)
  }
 
}
 
/**
 * Run selector function on the given state and return it's result. Return undefined if an error occurred
 *
 * @param {*} state
 * @param {StateSelector} selector
 * @returns The value return by the selector, undefined if an error occurred.
 */
function select(state: any, selector: StateSelector) {
  if (state == undefined) return
  Iif (selector == undefined) return state
  try {
    return selector(state)
  } catch (error) {
    return undefined
  }
}