ff-stream-web

git clone git://archive.git.mtrnord.blog/MTRNord/ff-stream-web.git
Log | Files | Refs | README | LICENSE

commit 7580590064a8c1128a2a3486da90dd9d6baced86
parent aabbd38b63088c7bb8c24503fb85c0c858fed245
Author: Konstantin Tarkus <hello@tarkus.me>
Date:   Wed,  1 Oct 2014 02:02:41 +0400

Update routing, implement Flux architecture. Closes #14

Diffstat:
MREADME.md | 8+++++---
Mconfig/webpack.js | 2+-
Mgulpfile.js | 4++--
Mpackage.json | 5+++--
Msrc/App.jsx | 40++++++++++++++++++++++++++--------------
Asrc/AppDispatcher.js | 39+++++++++++++++++++++++++++++++++++++++
Asrc/actions/RouteActions.js | 21+++++++++++++++++++++
Asrc/components/Link.jsx | 30++++++++++++++++++++++++++++++
Msrc/components/Navbar.jsx | 4++--
Msrc/components/NavbarSpec.jsx | 2+-
Asrc/constants/ActionTypes.js | 6++++++
Asrc/constants/PayloadSources.js | 4++++
Msrc/layouts/Default.jsx | 8++++----
Msrc/pages/Home.jsx | 59+++++++++++++++++++++++++++++++----------------------------
Msrc/pages/Privacy.jsx | 89+++++++++++++++++++++++++++++++++++++++++--------------------------------------
15 files changed, 221 insertions(+), 100 deletions(-)

diff --git a/README.md b/README.md @@ -24,16 +24,18 @@ ├── /docs/ # Documentation files ├── /node_modules/ # Node.js-based dev tools and utilities ├── /src/ # The source code of the application +│ ├── /actions/ # Action methods that allow to trigger a dispatch to stores │ ├── /assets/ # Static files which don't require pre-processing -│ ├── /data/ # Data access layer and models -│ ├── /common/ # Utility classes etc. +│ ├── /constants/ # Enumerations used in action methods and stores │ ├── /components/ # React components. E.g. Navbar.jsx │ ├── /images/ # Graphics (.png, .jpg, .svg etc.) │ ├── /layouts/ # Layouts for web pages │ ├── /pages/ # Web pages. E.g. Profile.jsx (or .html, .jade etc.) │ ├── /services/ # Services and business logic +│ ├── /stores/ # Stores contain the application state and logic │ ├── /styles/ # LESS style sheets (or SASS/SCSS, Stylus etc.) -│ └── /app.jsx # Entry point of your web application +│ ├── /App.jsx # Entry point of your web application +│ └── /AppDispatcher.js # The central hub that manages all data flow ├── /test/ # Unit, integration and load tests │ ├── /e2e/ # End-to-end tests │ └── /unit/ # Unit tests diff --git a/config/webpack.js b/config/webpack.js @@ -73,7 +73,7 @@ module.exports = function (release) { loader: 'url-loader?limit=10000&mimetype=image/png' }, { - test: /\.jsx$/, + test: /\.(js|jsx)$/, loader: 'jsx-loader?harmony' } ] diff --git a/gulpfile.js b/gulpfile.js @@ -77,10 +77,10 @@ gulp.task('images', function () { src.images = 'src/images/**'; return gulp.src(src.images) .pipe($.changed(DEST + '/images')) - .pipe($.cache($.imagemin({ + .pipe($.imagemin({ progressive: true, interlaced: true - }))) + })) .pipe(gulp.dest(DEST + '/images')) .pipe($.size({title: 'images'})); }); diff --git a/package.json b/package.json @@ -7,8 +7,9 @@ "license": "MIT", "dependencies": { "bootstrap": "^3.2.0", - "react": "^0.11.2", - "react-router": "^0.7.0" + "director": "^1.2.3", + "flux": "^2.0.1", + "react": "^0.11.2" }, "devDependencies": { "browser-sync": "^1.5.1", diff --git a/src/App.jsx b/src/App.jsx @@ -1,21 +1,33 @@ -/** - * @jsx React.DOM - */ - 'use strict'; var React = require('react'); -var {Routes, Route} = require('react-router'); +var {Router} = require('director'); +var AppDispatcher = require('./AppDispatcher'); +var ActionTypes = require('./constants/ActionTypes'); // Export React so the dev tools can find it (window !== window.top ? window.top : window).React = React; -React.renderComponent( - <Routes location="history"> - <Route name="app" path="/" handler={require('./layouts/Default.jsx')}> - <Route name="home" path="/" handler={require('./pages/Home.jsx')} /> - <Route name="privacy" handler={require('./pages/Privacy.jsx')} /> - </Route> - </Routes>, - document.body -); +function render(component) { + React.renderComponent(component(), document.body); +} + +var routes = { + '/': () => render(require('./pages/Home.jsx')), + '/privacy': () => render(require('./pages/Privacy.jsx')) +}; + +var router = new Router(routes).configure({html5history: true}).init(); + +AppDispatcher.register(function(payload) { + + var action = payload.action; + + switch (action.actionType) { + case ActionTypes.SET_CURRENT_ROUTE: + router.setRoute(action.route); + break; + } + + return true; // No errors. Needed by promise in Dispatcher. +}); diff --git a/src/AppDispatcher.js b/src/AppDispatcher.js @@ -0,0 +1,39 @@ +/* + * A singleton that operates as the central hub for application updates. + */ + +'use strict'; + +var {Dispatcher} = require('flux'); +var PayloadSources = require('./constants/PayloadSources'); +var copyProperties = require('react/lib/copyProperties'); + +var AppDispatcher = copyProperties(new Dispatcher(), { + + /** + * @param {object} action The details of the action, including the action's + * type and additional data coming from the server. + */ + handleServerAction: function(action) { + var payload = { + source: PayloadSources.SERVER_ACTION, + action: action + }; + this.dispatch(payload); + }, + + /** + * @param {object} action The details of the action, including the action's + * type and additional data coming from the view. + */ + handleViewAction: function(action) { + var payload = { + source: PayloadSources.VIEW_ACTION, + action: action + }; + this.dispatch(payload); + } + +}); + +module.exports = AppDispatcher; diff --git a/src/actions/RouteActions.js b/src/actions/RouteActions.js @@ -0,0 +1,21 @@ +'use strict'; + +var AppDispatcher = require('../AppDispatcher'); +var ActionTypes = require('../constants/ActionTypes'); + +var AppActions = { + + /** + * Set the current route. + * @param {string} route Supply a route value, such as `todos/completed`. + */ + setRoute: function(route) { + AppDispatcher.handleViewAction({ + actionType: ActionTypes.SET_CURRENT_ROUTE, + route: route + }); + } + +}; + +module.exports = AppActions; diff --git a/src/components/Link.jsx b/src/components/Link.jsx @@ -0,0 +1,30 @@ +/** + * @jsx React.DOM + */ + +'use strict'; + +var React = require('react'); +var RouteActions = require('../actions/RouteActions'); + +var Link = React.createClass({ + propTypes: { + to: React.PropTypes.string.isRequired, + children: React.PropTypes.component.isRequired + }, + render() { + this.props.href = + this.props.to && this.props.to.lastIndexOf('/', 0) === 0 ? + this.props.to : '/' + this.props.to; + + return this.transferPropsTo( + <a onClick={this.handleClick}>{this.props.children}</a> + ); + }, + handleClick(e) { + e.preventDefault(); + RouteActions.setRoute(this.props.to); + } +}); + +module.exports = Link; diff --git a/src/components/Navbar.jsx b/src/components/Navbar.jsx @@ -5,14 +5,14 @@ 'use strict'; var React = require('react'); -var {Link} = require('react-router'); +var Link = require('./Link.jsx'); var Navbar = React.createClass({ render() { return ( <div className="navbar-top"> <div className="container"> - <Link className="navbar-brand row" to="home"> + <Link className="navbar-brand row" to="/"> <img src="/images/logo-small.png" width="38" height="38" alt="React" /> {' Facebook React Starter Kit'} </Link> diff --git a/src/components/NavbarSpec.jsx b/src/components/NavbarSpec.jsx @@ -5,7 +5,7 @@ describe('Navbar', () => { beforeEach(() => { Navbar = require('./Navbar.jsx'); - component = new Navbar(); + component = Navbar(); }); it('should create a new instance of Navbar', () => { diff --git a/src/constants/ActionTypes.js b/src/constants/ActionTypes.js @@ -0,0 +1,6 @@ +module.exports = { + + // Route action types + SET_CURRENT_ROUTE: 'SET_CURRENT_ROUTE' + +}; diff --git a/src/constants/PayloadSources.js b/src/constants/PayloadSources.js @@ -0,0 +1,4 @@ +module.exports = { + VIEW_ACTION: 'VIEW_ACTION', + SERVER_ACTION: 'SERVER_ACTION' +}; diff --git a/src/layouts/Default.jsx b/src/layouts/Default.jsx @@ -5,7 +5,7 @@ 'use strict'; var React = require('react'); -var {Link} = require('react-router'); +var Link = require('../components/Link.jsx'); var Navbar = require('../components/Navbar.jsx'); var DefaultLayout = React.createClass({ @@ -19,13 +19,13 @@ var DefaultLayout = React.createClass({ <p>Complex web apps made easy</p> </div> </div> - <this.props.activeRouteHandler /> + {this.props.children} <div className="navbar-footer"> <div className="container"> <p className="text-muted"> {' © KriaSoft • '} - <Link to="home">Home</Link> {' • '} - <Link to="privacy">Privacy</Link> + <Link to="/">Home</Link>{' • '} + <Link to="/privacy">Privacy</Link> </p> </div> </div> diff --git a/src/pages/Home.jsx b/src/pages/Home.jsx @@ -5,40 +5,43 @@ 'use strict'; var React = require('react'); +var Layout = require('../layouts/Default.jsx'); var HomePage = React.createClass({ render() { return ( - <div className="container"> - <div className="row"> - <div className="col-sm-4"> - <h3>Runtime Components</h3> - <dl> - <dt><a href="https://facebook.github.io/react/">React</a></dt> - <dd>A JavaScript library for building user interfaces, developed by Facebook</dd> - <dt><a href="https://github.com/rackt/react-router">React-Router</a></dt> - <dd>A complete routing library for React</dd> - <dt><a href="http://getbootstrap.com/">Bootstrap</a></dt> - <dd>CSS framework for developing responsive, mobile first interfaces</dd> - </dl> - </div> - <div className="col-sm-4"> - <h3>Development Tools</h3> - <dl> - <dt><a href="http://gulpjs.com">Gulp</a></dt> - <dd>JavaScript streaming build system and task automation</dd> - <dt><a href="http://webpack.github.io/">Webpack</a></dt> - <dd>Compiles front-end source code into modules / bundles</dd> - <dt><a href="http://www.browsersync.io/">BrowserSync</a></dt> - <dd>A lightweight HTTP server for development</dd> - </dl> - </div> - <div className="col-sm-4"> - <h3>Fork me on GitHub</h3> - <p><a href="https://github.com/kriasoft/react-starter-kit">github.com/kriasoft/react-starter-kit</a></p> + <Layout> + <div className="container"> + <div className="row"> + <div className="col-sm-4"> + <h3>Runtime Components</h3> + <dl> + <dt><a href="https://facebook.github.io/react/">React</a></dt> + <dd>A JavaScript library for building user interfaces, developed by Facebook</dd> + <dt><a href="https://github.com/rackt/react-router">React-Router</a></dt> + <dd>A complete routing library for React</dd> + <dt><a href="http://getbootstrap.com/">Bootstrap</a></dt> + <dd>CSS framework for developing responsive, mobile first interfaces</dd> + </dl> + </div> + <div className="col-sm-4"> + <h3>Development Tools</h3> + <dl> + <dt><a href="http://gulpjs.com">Gulp</a></dt> + <dd>JavaScript streaming build system and task automation</dd> + <dt><a href="http://webpack.github.io/">Webpack</a></dt> + <dd>Compiles front-end source code into modules / bundles</dd> + <dt><a href="http://www.browsersync.io/">BrowserSync</a></dt> + <dd>A lightweight HTTP server for development</dd> + </dl> + </div> + <div className="col-sm-4"> + <h3>Fork me on GitHub</h3> + <p><a href="https://github.com/kriasoft/react-starter-kit">github.com/kriasoft/react-starter-kit</a></p> + </div> </div> </div> - </div> + </Layout> ); } }); diff --git a/src/pages/Privacy.jsx b/src/pages/Privacy.jsx @@ -5,53 +5,56 @@ 'use strict'; var React = require('react'); +var Layout = require('../layouts/Default.jsx'); var PrivacyPage = React.createClass({ render() { return ( - <div className="container"> - <h2>Privacy Policy</h2> - <p> - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean consequat tortor fermentum mi - fermentum dignissim. Nullam vel ipsum ut ligula elementum lobortis. Maecenas aliquam, massa laoreet - lacinia pretium, nisi urna venenatis tortor, nec imperdiet tellus libero efficitur metus. Fusce - semper posuere ligula, et facilisis metus bibendum interdum. Mauris at mauris sit amet sem pharetra - commodo a eu leo. Nam at est non risus cursus maximus. Nam feugiat augue libero, id consectetur - tortor bibendum non. Quisque nec fringilla lorem. Nullam efficitur vulputate mauris, nec maximus leo - dignissim id. - </p> - <p> - In hac habitasse platea dictumst. Duis sagittis dui ac ex suscipit maximus. Morbi pellentesque - venenatis felis sed convallis. Nulla varius, nibh vitae placerat tempus, mauris sem elementum ipsum, - eget sollicitudin nisl est vel purus. Fusce malesuada odio velit, non cursus leo fermentum id. Cras - pharetra sodales fringilla. Etiam quis est a dolor egestas pellentesque. Maecenas non scelerisque - purus, congue cursus arcu. Donec vel dapibus mi. Mauris maximus posuere placerat. Sed et libero eu - nibh tristique mollis a eget lectus. Donec interdum augue sollicitudin vehicula hendrerit. Vivamus - justo orci, molestie ac sollicitudin ac, lobortis at tellus. Etiam rhoncus ullamcorper risus eu - tempor. Sed porttitor, neque ac efficitur gravida, arcu lacus pharetra dui, in consequat elit tellus - auctor nulla. Donec placerat elementum diam, vitae imperdiet lectus luctus at. - </p> - <p> - Nullam eu feugiat mi. Quisque nec tristique nisl, dignissim dictum leo. Nam non quam nisi. Donec - rutrum turpis ac diam blandit, id pulvinar mauris suscipit. Pellentesque tincidunt libero ultricies - risus iaculis, sit amet consequat velit blandit. Fusce quis varius nulla. Nullam nisi nisi, suscipit - ut magna quis, feugiat porta nibh. Sed id enim lectus. Suspendisse elementum justo sapien, sit amet - consequat orci accumsan et. Aliquam ornare ullamcorper sem sed finibus. Nullam ac lacus pulvinar, - egestas felis ut, accumsan est. - </p> - <p> - Pellentesque sagittis vehicula sem quis luctus. Proin sodales magna in lorem hendrerit aliquam. - Integer eu varius orci. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere - cubilia Curae; Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia - Curae; Ut at mauris nibh. Suspendisse maximus ac eros at vestibulum. - </p> - <p> - Interdum et malesuada fames ac ante ipsum primis in faucibus. Quisque egestas tortor et dui - consequat faucibus. Nunc vitae odio ornare, venenatis ligula a, vulputate nisl. Aenean congue varius - ex, sit amet bibendum odio posuere at. Nulla facilisi. In finibus, nulla vitae tincidunt ornare, - sapien nulla fermentum mauris, sed consectetur tortor arcu eget arcu. Vestibulum vel quam enim. - </p> - </div> + <Layout> + <div className="container"> + <h2>Privacy Policy</h2> + <p> + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean consequat tortor fermentum mi + fermentum dignissim. Nullam vel ipsum ut ligula elementum lobortis. Maecenas aliquam, massa laoreet + lacinia pretium, nisi urna venenatis tortor, nec imperdiet tellus libero efficitur metus. Fusce + semper posuere ligula, et facilisis metus bibendum interdum. Mauris at mauris sit amet sem pharetra + commodo a eu leo. Nam at est non risus cursus maximus. Nam feugiat augue libero, id consectetur + tortor bibendum non. Quisque nec fringilla lorem. Nullam efficitur vulputate mauris, nec maximus leo + dignissim id. + </p> + <p> + In hac habitasse platea dictumst. Duis sagittis dui ac ex suscipit maximus. Morbi pellentesque + venenatis felis sed convallis. Nulla varius, nibh vitae placerat tempus, mauris sem elementum ipsum, + eget sollicitudin nisl est vel purus. Fusce malesuada odio velit, non cursus leo fermentum id. Cras + pharetra sodales fringilla. Etiam quis est a dolor egestas pellentesque. Maecenas non scelerisque + purus, congue cursus arcu. Donec vel dapibus mi. Mauris maximus posuere placerat. Sed et libero eu + nibh tristique mollis a eget lectus. Donec interdum augue sollicitudin vehicula hendrerit. Vivamus + justo orci, molestie ac sollicitudin ac, lobortis at tellus. Etiam rhoncus ullamcorper risus eu + tempor. Sed porttitor, neque ac efficitur gravida, arcu lacus pharetra dui, in consequat elit tellus + auctor nulla. Donec placerat elementum diam, vitae imperdiet lectus luctus at. + </p> + <p> + Nullam eu feugiat mi. Quisque nec tristique nisl, dignissim dictum leo. Nam non quam nisi. Donec + rutrum turpis ac diam blandit, id pulvinar mauris suscipit. Pellentesque tincidunt libero ultricies + risus iaculis, sit amet consequat velit blandit. Fusce quis varius nulla. Nullam nisi nisi, suscipit + ut magna quis, feugiat porta nibh. Sed id enim lectus. Suspendisse elementum justo sapien, sit amet + consequat orci accumsan et. Aliquam ornare ullamcorper sem sed finibus. Nullam ac lacus pulvinar, + egestas felis ut, accumsan est. + </p> + <p> + Pellentesque sagittis vehicula sem quis luctus. Proin sodales magna in lorem hendrerit aliquam. + Integer eu varius orci. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere + cubilia Curae; Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia + Curae; Ut at mauris nibh. Suspendisse maximus ac eros at vestibulum. + </p> + <p> + Interdum et malesuada fames ac ante ipsum primis in faucibus. Quisque egestas tortor et dui + consequat faucibus. Nunc vitae odio ornare, venenatis ligula a, vulputate nisl. Aenean congue varius + ex, sit amet bibendum odio posuere at. Nulla facilisi. In finibus, nulla vitae tincidunt ornare, + sapien nulla fermentum mauris, sed consectetur tortor arcu eget arcu. Vestibulum vel quam enim. + </p> + </div> + </Layout> ); } });