git.fiddlerwoaroof.com
Raw Blame History
import * as R from 'ramda';
import {
    Map,
    fromJS
} from 'immutable';
import { makeLens, lensTransformer, fireListeners } from './lens';

let common_state = {
    get(key) {
        return this.lensFor(key).get();
    },

    set(key, value) {
        this.lensFor(key).set(value);
    },

    getState() {
        return this._currentState.toJS();
    },

    lensFor(key) {
        return makeLens(key instanceof Array ? key : [key], this);
    },

    getRecorder() {
        return new RecordingStateContainer(this);
    },

    commit(recorder) {
        recorder.replay(this);
    },

    onUpdate(listener) {
        this.listeners.push(listener);
        return () => {
            this.listeners.splice(this.listeners.indexOf(listener), 1);
        };
    },

    [fireListeners](oldState, newState) {
        this.listeners.forEach((listener) => {
            listener(oldState, newState);
        });
    },
};

function RecordingStateContainer(container) {
    this.localStore = Map();
    this.container = container;
    this.listeners = [];
    this.actions = [];
}

RecordingStateContainer.prototype = Object.assign({
    setState(newState) {
        const oldState = this._currentState;
        this.localStore = this.localStore.merge(newState);
        this.actions = [[['setState', this.localStore]]];
        this[fireListeners](oldState, this._currentState);
    },

    get _currentState() {
        const con = new StateContainer(this.container.getState());
        this._replay(con);
        return con._currentState;
    },
    set _currentState(value) {
        this.setState(value);
    },

    _replay(container) {
        R.forEach(
            R.reduce(
                (cur, [key, ...args]) => cur[key](...args),
                container,
                R.__
            ),
            this.actions
        );
    },
    replay(container) {
        if (container !== this.container) {
            throw new Error("Trying to replay into wrong parent container");
        }
        this._replay(container);
        this.localStore = Map();
        this.actions = [];
    },
}, common_state);

export { lensTransformer };

export default function StateContainer(data = {}) {
    var state = fromJS(data);

    return Object.assign({
        listeners: [],

        get _currentState() {
            return state;
        },
        set _currentState(value) {
            state = value;
        },

        /**
         * setState merges state shallowly
         * */
        setState(newState) {
            const oldState = this._currentState;
            this._currentState = this._currentState.merge(newState);
            this[fireListeners](oldState, this._currentState);
        },
    }, common_state);
}