All files state.ts

100% Statements 30/30
100% Branches 6/6
100% Functions 10/10
100% Lines 26/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 110x             2x 50x                 2x 9x                   4x   4x   4x 13x 13x 4x 4x       4x       2x 2x 2x 52x     2x                   26x 18x 15x 15x   1x      
import * as Immutable from 'seamless-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)
        if (selection !== select(previousState, selector)) {
          previousState = state
          subscriber.next(selection)
        }
      }, undefined, undefined)
 
      return subscription
    }).share()
  }
 
  constructor() {
    this.currentState = Immutable.from<any>({})
    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
  if (selector == undefined) return state
  try {
    return selector(state)
  } catch (error) {
    return undefined
  }
}