diff --git a/lib/index.js b/lib/index.js index faca5ad..442bde8 100644 --- a/lib/index.js +++ b/lib/index.js @@ -4,8 +4,9 @@ var Store = require('./store'); var Mixin = require('./mixin'); var Dispatcher = require('./dispatcher'); var Configs = require('./configs'); +var Progress = require('./progress'); -var dispatcher = new Dispatcher(); +var dispatcher = new Dispatcher(); module.exports = { @@ -45,6 +46,11 @@ module.exports = { dispatcher.dispatch(constant, payload); }, + /** + * Global progress store for tracking the progress of the dispatcher's actions + */ + progress: new Progress(dispatcher), + /** * Mixin */ diff --git a/lib/progress.js b/lib/progress.js new file mode 100644 index 0000000..a489457 --- /dev/null +++ b/lib/progress.js @@ -0,0 +1,76 @@ +var Store = require('./store'); + +var Progress = function ( dispatcher ) { + return new Store(dispatcher, { + _trackProgressFor: function ( constant ) { + this.state = this.state.set(constant, true); + + this.addActionHandler(constant, { + getInitialState: function () { + return { + isInProgress: false + }; + }, + before: function () { + this.setState({ isInProgress: true }); + this.parent.emit('CHANGE$' + constant); + }, + after: function () { + this.setState({ isInProgress: false }); + this.parent.emit('CHANGE$' + constant); + } + }); + }, + + _isTrackingProgress: function ( constant ) { + return !!this.get(constant); + }, + + of: function ( constant ) { + if (!this._isTrackingProgress(constant)) { + this._trackProgressFor(constant); + } + return this.getActionState(constant, 'isInProgress'); + }, + + any: function ( constants ) { + for (var i = 0; i < constants.length; i++) { + if (this.of(constants[i])) { + return true; + } + } + return false; + }, + + mixinFor: function ( constants ) { + var self = this; + return { + componentWillMount: function () { + if (typeof this._react_flux_onChange == "undefined") { + this._react_flux_onChange = function () { + if (!this.isMounted()) { + return; + } + this.setState(this.getStateFromStores()); + }.bind(this); + } + this.setState(this.getStateFromStores()); + }, + + componentDidMount: function () { + for (var i = 0; i < constants.length; i++) { + self[i].on('CHANGE$' + constant, this._react_flux_onChange); + } + }, + + componentWillUnmount: function () { + for (var i = 0; i < constants.length; i++) { + self[i].off('CHANGE$' + constant, this._react_flux_onChange); + } + } + } + } + }); +}; + +module.exports = Progress; diff --git a/test/progress.js b/test/progress.js new file mode 100644 index 0000000..d5d2563 --- /dev/null +++ b/test/progress.js @@ -0,0 +1,55 @@ +var ReactFlux = require('../'); +var assert = require('chai').assert; +var Promise = require('promise'); + +var constants = ReactFlux.createConstants(['ONE', 'TWO'], 'STORE'); + +var actions = ReactFlux.createActions({ + one: [constants.ONE, function () { + return new Promise(function (resolve, reject) { + setTimeout(resolve, 10); + }); + }], + two: [constants.TWO, function () { + return new Promise(function (resolve, reject) { + setTimeout(resolve, 0); + }); + }] +}); + +describe("progress", function () { + + it("should be false by default", function () { + assert.isFalse(ReactFlux.progress.of(constants.ONE)); + assert.isFalse(ReactFlux.progress.of(constants.TWO)); + }); + + it("should be true when in progress", function ( done ) { + assert.isFalse(ReactFlux.progress.of(constants.ONE)); + actions.one(); + assert.isTrue(ReactFlux.progress.of(constants.ONE)); + setTimeout(function () { + try { + assert.isTrue(ReactFlux.progress.of(constants.ONE)); + done(); + } catch ( e ) { + done(e); + } + }, 0); + + }); + + it("should be false when progress is done", function ( done ) { + actions.two(); + assert.isTrue(ReactFlux.progress.of(constants.TWO)); + setTimeout(function () { + try { + assert.isFalse(ReactFlux.progress.of(constants.TWO)); + done(); + } catch ( e ) { + done(e); + } + }, 10); + }); + +});