ff-stream-web

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

commit 7e06940b0616b8f3d9e84faeb3b533f4854d6a72
parent ee0d700833c17c3c6b9659ca555c96a3d8ab6c3f
Author: Konstantin Tarkus <hello@tarkus.me>
Date:   Sun, 15 Mar 2015 20:35:53 +0300

Update React from 0.12.2 to 0.13.0; update React components to ES classes

Diffstat:
Mpackage.json | 34+++++++++++++++++-----------------
Msrc/actions/AppActions.js | 21+++++++++++++++------
Msrc/app.js | 19++++++++++---------
Msrc/components/App/App.js | 91+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
Dsrc/components/App/NavigationMixin.js | 94-------------------------------------------------------------------------------
Msrc/components/ContentPage/ContentPage.js | 18++++++++++--------
Msrc/components/HomePage/HomePage.js | 18++++++++++--------
Msrc/components/Navbar/Navbar.js | 4++--
Msrc/components/NotFoundPage/NotFoundPage.js | 4++--
Msrc/components/TextBox/TextBox.js | 22++++++++++------------
Msrc/constants/ActionTypes.js | 4+---
Msrc/constants/PayloadSources.js | 4+---
Dsrc/constants/Settings.js | 23-----------------------
Msrc/core/Dispatcher.js | 4++--
Msrc/stores/AppStore.js | 2+-
15 files changed, 161 insertions(+), 201 deletions(-)

diff --git a/package.json b/package.json @@ -10,41 +10,41 @@ "npm": ">= 2.1" }, "dependencies": { - "babel": "4.7.1", + "babel": "4.7.12", "bootstrap": "3.3.2", "eventemitter3": "0.1.6", "express": "4.12.2", "flux": "2.0.1", "front-matter": "1.0.0", "jade": "1.9.2", - "lodash": "3.4.0", - "react": "0.12.2", - "superagent": "0.21.0" + "lodash": "3.5.0", + "react": "0.13.0", + "superagent": "1.1.0" }, "devDependencies": { "autoprefixer-loader": "^1.2.0", - "babel-core": "^4.7.1", - "babel-eslint": "^1.0.13", + "babel-core": "^4.7.12", + "babel-eslint": "^2.0.0", "babel-loader": "^4.1.0", - "browser-sync": "^2.2.2", + "browser-sync": "^2.2.4", "css-loader": "^0.9.1", "del": "^1.1.1", - "eslint": "^0.15.1", - "eslint-loader": "^0.4.0", + "eslint": "^0.16.0", + "eslint-loader": "^0.6.0", "gulp": "^3.8.11", "gulp-autoprefixer": "^2.1.0", "gulp-cache": "^0.2.8", - "gulp-changed": "^1.1.1", + "gulp-changed": "^1.2.1", "gulp-csscomb": "^3.0.3", - "gulp-gh-pages": "^0.4.0", + "gulp-gh-pages": "^0.5.0", "gulp-htmlmin": "^1.1.1", "gulp-if": "^1.2.5", "gulp-imagemin": "^2.2.1", - "gulp-jsbeautifier": "^0.0.5", + "gulp-jsbeautifier": "^0.0.6", "gulp-less": "^3.0.1", "gulp-load-plugins": "^0.8.1", - "gulp-minify-css": "^0.5.1", - "gulp-plumber": "^0.6.6", + "gulp-minify-css": "^1.0.0", + "gulp-plumber": "^1.0.0", "gulp-rename": "^1.2.0", "gulp-render": "^0.2.2", "gulp-replace": "^0.5.3", @@ -54,14 +54,14 @@ "jest-cli": "^0.4.0", "less": "^2.4.0", "less-loader": "^2.1.0", - "minimist": "^1.1.0", + "minimist": "^1.1.1", "protractor": "^1.8.0", "psi": "^1.0.6", - "react-tools": "^0.12.2", + "react-tools": "^0.13.0", "run-sequence": "^1.0.2", "style-loader": "^0.8.3", "url-loader": "^0.5.5", - "webpack": "^1.7.2", + "webpack": "^1.7.3", "webpack-dev-server": "^1.7.0" }, "jest": { diff --git a/src/actions/AppActions.js b/src/actions/AppActions.js @@ -13,28 +13,37 @@ import ActionTypes from '../constants/ActionTypes'; import ExecutionEnvironment from 'react/lib/ExecutionEnvironment'; import http from 'superagent'; -module.exports = { +export default { - navigateTo(path) { + navigateTo(path, options) { if (ExecutionEnvironment.canUseDOM) { - window.history.pushState({}, document.title, path); + if (options && options.replace) { + window.history.replaceState({}, document.title, path); + } else { + window.history.pushState({}, document.title, path); + } } Dispatcher.handleViewAction({ - actionType: ActionTypes.CHANGE_LOCATION, path: path + actionType: ActionTypes.CHANGE_LOCATION, + path }); }, loadPage(path, cb) { Dispatcher.handleViewAction({ - actionType: ActionTypes.LOAD_PAGE, path: path + actionType: ActionTypes.LOAD_PAGE, + path }); http.get('/api/page' + path) .accept('application/json') .end((err, res) => { Dispatcher.handleServerAction({ - actionType: ActionTypes.LOAD_PAGE, path: path, err: err, page: res.body + actionType: ActionTypes.LOAD_PAGE, + path, + err, + page: res.body }); if (cb) { cb(); diff --git a/src/app.js b/src/app.js @@ -10,24 +10,24 @@ import 'babel/polyfill'; -import React from 'react'; +import React from 'react/addons'; import emptyFunction from 'react/lib/emptyFunction'; import App from './components/App'; import Dispatcher from './core/Dispatcher'; import AppActions from './actions/AppActions'; import ActionTypes from './constants/ActionTypes'; -var path = decodeURI(window.location.pathname); -var setMetaTag = (name, content) => { +let path = decodeURI(window.location.pathname); +let setMetaTag = (name, content) => { // Remove and create a new <meta /> tag in order to make it work // with bookmarks in Safari - var elements = document.getElementsByTagName('meta'); + let elements = document.getElementsByTagName('meta'); [].slice.call(elements).forEach((element) => { if (element.getAttribute('name') === name) { element.parentNode.removeChild(element); } }); - var meta = document.createElement('meta'); + let meta = document.createElement('meta'); meta.setAttribute('name', name); meta.setAttribute('content', content); document.getElementsByTagName('head')[0].appendChild(meta); @@ -35,19 +35,20 @@ var setMetaTag = (name, content) => { function run() { // Render the top-level React component - var props = { + let props = { path: path, onSetTitle: (title) => document.title = title, onSetMeta: setMetaTag, onPageNotFound: emptyFunction }; - var component = React.createElement(App, props); - var app = React.render(component, document.body); + let element = React.createElement(App, props); + React.render(element, document.body); // Update `Application.path` prop when `window.location` is changed Dispatcher.register((payload) => { if (payload.action.actionType === ActionTypes.CHANGE_LOCATION) { - app.setProps({path: decodeURI(payload.action.path)}); + element = React.cloneElement(element, {path: payload.action.path}); + React.render(element, document.body); } }); } diff --git a/src/components/App/App.js b/src/components/App/App.js @@ -12,22 +12,27 @@ import './App.less'; import React from 'react'; import invariant from 'react/lib/invariant'; -import NavigationMixin from './NavigationMixin'; +import AppActions from '../../actions/AppActions'; import AppStore from '../../stores/AppStore'; import Navbar from '../Navbar'; import ContentPage from '../ContentPage'; import NotFoundPage from '../NotFoundPage'; -export default React.createClass({ +export default class App extends React.Component { - mixins: [NavigationMixin], + componentDidMount() { + window.addEventListener('popstate', this.handlePopState); + window.addEventListener('click', this.handleClick); + } + + componentWillUnmount() { + window.removeEventListener('popstate', this.handlePopState); + window.removeEventListener('click', this.handleClick); + } - propTypes: { - path: React.PropTypes.string.isRequired, - onSetTitle: React.PropTypes.func.isRequired, - onSetMeta: React.PropTypes.func.isRequired, - onPageNotFound: React.PropTypes.func.isRequired - }, + shouldComponentUpdate(nextProps) { + return this.props.path !== nextProps.path; + } render() { var page = AppStore.getPage(this.props.path); @@ -58,7 +63,7 @@ export default React.createClass({ <div className="navbar-footer"> <div className="container"> <p className="text-muted"> - <span>© KriaSoft</span> + <span>© Your Company</span> <span><a href="/">Home</a></span> <span><a href="/privacy">Privacy</a></span> </p> @@ -68,4 +73,68 @@ export default React.createClass({ ); } -}); + handlePopState(event) { + AppActions.navigateTo(window.location.pathname, {replace: !!event.state}); + } + + handleClick(event) { + if (event.button === 1 || event.metaKey || event.ctrlKey || event.shiftKey || event.defaultPrevented) { + return; + } + + // Ensure link + var el = event.target; + while (el && el.nodeName !== 'A') { + el = el.parentNode; + } + if (!el || el.nodeName !== 'A') { + return; + } + + // Ignore if tag has + // 1. "download" attribute + // 2. rel="external" attribute + if (el.getAttribute('download') || el.getAttribute('rel') === 'external') { + return; + } + + // Ensure non-hash for the same path + var link = el.getAttribute('href'); + if (el.pathname === location.pathname && (el.hash || link === '#')) { + return; + } + + // Check for mailto: in the href + if (link && link.indexOf('mailto:') > -1) { + return; + } + + // Check target + if (el.target) { + return; + } + + // X-origin + var origin = window.location.protocol + '//' + window.location.hostname + + (window.location.port ? ':' + window.location.port : ''); + if (!(el.href && el.href.indexOf(origin) === 0)) { + return; + } + + // Rebuild path + var path = el.pathname + el.search + (el.hash || ''); + + event.preventDefault(); + AppActions.loadPage(path, () => { + AppActions.navigateTo(path); + }); + } + +} + +App.propTypes = { + path: React.PropTypes.string.isRequired, + onSetTitle: React.PropTypes.func.isRequired, + onSetMeta: React.PropTypes.func.isRequired, + onPageNotFound: React.PropTypes.func.isRequired +}; diff --git a/src/components/App/NavigationMixin.js b/src/components/App/NavigationMixin.js @@ -1,94 +0,0 @@ -/* - * React.js Starter Kit - * Copyright (c) 2014 Konstantin Tarkus (@koistya), KriaSoft LLC. - * - * This source code is licensed under the MIT license found in the - * LICENSE.txt file in the root directory of this source tree. - */ - -'use strict'; - -import ExecutionEnvironment from 'react/lib/ExecutionEnvironment'; -import AppActions from '../../actions/AppActions'; - -var NavigationMixin = { - - componentDidMount() { - if (ExecutionEnvironment.canUseDOM) { - window.addEventListener('popstate', this.handlePopState); - window.addEventListener('click', this.handleClick); - } - }, - - componentWillUnmount() { - window.removeEventListener('popstate', this.handlePopState); - window.removeEventListener('click', this.handleClick); - }, - - handlePopState(event) { - //if (event.state) { - // TODO: Replace current location - // var path = event.state.path; - // replace(path, event.state); - //} - if (!event.state) { - AppActions.navigateTo(window.location.pathname); - } - }, - - handleClick(event) { - if (event.button === 1 || event.metaKey || event.ctrlKey || event.shiftKey || event.defaultPrevented) { - return; - } - - // Ensure link - var el = event.target; - while (el && el.nodeName !== 'A') { - el = el.parentNode; - } - if (!el || el.nodeName !== 'A') { - return; - } - - // Ignore if tag has - // 1. "download" attribute - // 2. rel="external" attribute - if (el.getAttribute('download') || el.getAttribute('rel') === 'external') { - return; - } - - // Ensure non-hash for the same path - var link = el.getAttribute('href'); - if (el.pathname === location.pathname && (el.hash || link === '#')) { - return; - } - - // Check for mailto: in the href - if (link && link.indexOf('mailto:') > -1) { - return; - } - - // Check target - if (el.target) { - return; - } - - // X-origin - var origin = window.location.protocol + '//' + window.location.hostname + - (window.location.port ? ':' + window.location.port : ''); - if (!(el.href && el.href.indexOf(origin) === 0)) { - return; - } - - // Rebuild path - var path = el.pathname + el.search + (el.hash || ''); - - event.preventDefault(); - AppActions.loadPage(path, () => { - AppActions.navigateTo(path); - }); - } - -}; - -module.exports = NavigationMixin; diff --git a/src/components/ContentPage/ContentPage.js b/src/components/ContentPage/ContentPage.js @@ -10,17 +10,19 @@ import React from 'react'; -export default React.createClass({ - - propTypes: { - body: React.PropTypes.string.isRequired - }, +export default class ContentPage extends React.Component { render() { var { className, body, other } = this.props; - return <div className={'ContentPage ' + className} - dangerouslySetInnerHTML={{__html: body}} {...other} />; + return ( + <div className={'ContentPage ' + className} + dangerouslySetInnerHTML={{__html: body}} {...other} /> + ); } -}); +} + +ContentPage.propTypes = { + body: React.PropTypes.string.isRequired +}; diff --git a/src/components/HomePage/HomePage.js b/src/components/HomePage/HomePage.js @@ -10,15 +10,17 @@ import React from 'react'; -export default React.createClass({ - - propTypes: { - body: React.PropTypes.string.isRequired - }, +export default class HomePage extends React.Component { render() { - return <div className="ContentPage" - dangerouslySetInnerHTML={{__html: this.props.body}} />; + return ( + <div className="ContentPage" + dangerouslySetInnerHTML={{__html: this.props.body}} /> + ); } -}); +} + +HomePage.propTypes = { + body: React.PropTypes.string.isRequired +}; diff --git a/src/components/Navbar/Navbar.js b/src/components/Navbar/Navbar.js @@ -10,7 +10,7 @@ import React from 'react'; -export default React.createClass({ +export default class Navbar extends React.Component { render() { return ( @@ -25,4 +25,4 @@ export default React.createClass({ ); } -}); +} diff --git a/src/components/NotFoundPage/NotFoundPage.js b/src/components/NotFoundPage/NotFoundPage.js @@ -12,7 +12,7 @@ import React from 'react'; -export default React.createClass({ +export default class NotFoundPage extends React.Component { render() { return ( @@ -23,4 +23,4 @@ export default React.createClass({ ); } -}); +} diff --git a/src/components/TextBox/TextBox.js b/src/components/TextBox/TextBox.js @@ -12,17 +12,7 @@ import './TextBox.less'; import React from 'react'; -export default React.createClass({ - - propTypes: { - maxLines: React.PropTypes.number - }, - - getDefaultProps() { - return { - maxLines: 1 - }; - }, +export default class TextBox extends React.Component { render() { return ( @@ -34,4 +24,12 @@ export default React.createClass({ ); } -}); +} + +TextBox.propTypes = { + maxLines: React.PropTypes.number +}; + +TextBox.defaultProps = { + maxLines: 1 +}; diff --git a/src/constants/ActionTypes.js b/src/constants/ActionTypes.js @@ -10,7 +10,7 @@ import keyMirror from 'react/lib/keyMirror'; -var ActionTypes = keyMirror({ +export default keyMirror({ LOAD_PAGE: null, LOAD_PAGE_SUCCESS: null, @@ -18,5 +18,3 @@ var ActionTypes = keyMirror({ CHANGE_LOCATION: null }); - -module.exports = ActionTypes; diff --git a/src/constants/PayloadSources.js b/src/constants/PayloadSources.js @@ -10,11 +10,9 @@ import keyMirror from 'react/lib/keyMirror'; -var PayloadSources = keyMirror({ +export default keyMirror({ VIEW_ACTION: null, SERVER_ACTION: null }); - -module.exports = PayloadSources; diff --git a/src/constants/Settings.js b/src/constants/Settings.js @@ -1,23 +0,0 @@ -/* - * React.js Starter Kit - * Copyright (c) 2014 Konstantin Tarkus (@koistya), KriaSoft LLC. - * - * This source code is licensed under the MIT license found in the - * LICENSE.txt file in the root directory of this source tree. - */ - -'use strict'; - -module.exports = { - - defaults: { - - page: { - title: 'React.js Starter Kit', - description: 'A skeleton for an isomorphic web application (SPA) built with React.js and Flux', - keywords: null - - } - } - -}; diff --git a/src/core/Dispatcher.js b/src/core/Dispatcher.js @@ -16,7 +16,7 @@ import assign from 'react/lib/Object.assign'; * A singleton that operates as the central hub for application updates. * For more information visit https://facebook.github.io/flux/ */ -var Dispatcher = assign(new Flux.Dispatcher(), { +let Dispatcher = assign(new Flux.Dispatcher(), { /** * @param {object} action The details of the action, including the action's @@ -44,4 +44,4 @@ var Dispatcher = assign(new Flux.Dispatcher(), { }); -module.exports = Dispatcher; +export default Dispatcher; diff --git a/src/stores/AppStore.js b/src/stores/AppStore.js @@ -96,4 +96,4 @@ AppStore.dispatcherToken = Dispatcher.register((payload) => { }); -module.exports = AppStore; +export default AppStore;