ff-stream-web

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

commit 89ceb6ab614ec4258f47b97e3d412350baab1815
parent 8b655d36070ef6a9ba1c69327204ca78b584312b
Author: Vladimir Kutepov <frenzzy.man@gmail.com>
Date:   Tue,  4 Oct 2016 23:25:49 +0400

Add the Admin page with redirect example (#879)

- Add support of redirects at the routing level, e.g. { path: '/admin', action() { return { redirect: '/login' } } }
- Remove context.history as it's no longer used on the server in favor of core/history.js singleton
- Create /admin page example with a redirect to /login page
Diffstat:
Msrc/client.js | 33++++++++++++++++++---------------
Msrc/components/App.js | 3---
Msrc/components/Footer/Footer.js | 2++
Msrc/components/Link/Link.js | 7++-----
Asrc/core/history.js | 5+++++
Asrc/routes/admin/Admin.css | 21+++++++++++++++++++++
Asrc/routes/admin/Admin.js | 32++++++++++++++++++++++++++++++++
Asrc/routes/admin/index.js | 31+++++++++++++++++++++++++++++++
Msrc/routes/index.js | 1+
Msrc/server.js | 11+++++------
10 files changed, 117 insertions(+), 29 deletions(-)

diff --git a/src/client.js b/src/client.js @@ -13,16 +13,13 @@ import ReactDOM from 'react-dom'; import FastClick from 'fastclick'; import UniversalRouter from 'universal-router'; import queryString from 'query-string'; -import createBrowserHistory from 'history/createBrowserHistory'; import { createPath } from 'history/PathUtils'; +import history from './core/history'; import App from './components/App'; // Global (context) variables that can be easily accessed from any React component // https://facebook.github.io/react/docs/context.html const context = { - // Navigation manager, e.g. history.push('/home') - // https://github.com/mjackson/history - history: createBrowserHistory(), // Enables critical path CSS rendering // https://github.com/kriasoft/isomorphic-style-loader insertCss: (...styles) => { @@ -111,7 +108,7 @@ let onRenderComplete = function initialRenderComplete() { FastClick.attach(document.body); const container = document.getElementById('app'); -let currentLocation = context.history.location; +let currentLocation = history.location; let routes = require('./routes').default; // Re-render the app when window.location changes @@ -122,7 +119,7 @@ async function onLocationChange(location) { scrollY: window.pageYOffset, }; // Delete stored scroll position for next page if any - if (context.history.action === 'PUSH') { + if (history.action === 'PUSH') { delete scrollPositionsHistory[location.key]; } currentLocation = location; @@ -136,15 +133,21 @@ async function onLocationChange(location) { query: queryString.parse(location.search), }); - // Render the result of the resolved route into the DOM - // if the location was not changed during the routing process - if (currentLocation.key === location.key) { - ReactDOM.render( - <App context={context}>{route.component}</App>, - container, - () => onRenderComplete(route, location) - ); + // Prevent multiple page renders during the routing process + if (currentLocation.key !== location.key) { + return; } + + if (route.redirect) { + history.replace(route.redirect); + return; + } + + ReactDOM.render( + <App context={context}>{route.component}</App>, + container, + () => onRenderComplete(route, location) + ); } catch (err) { if (process.env.NODE_ENV !== 'production') { throw err; @@ -158,7 +161,7 @@ async function onLocationChange(location) { // Handle client-side navigation by using HTML5 History API // For more information visit https://github.com/mjackson/history#readme -context.history.listen(onLocationChange); +history.listen(onLocationChange); onLocationChange(currentLocation); // Enable Hot Module Replacement (HMR) diff --git a/src/components/App.js b/src/components/App.js @@ -10,9 +10,6 @@ import React, { PropTypes } from 'react'; const ContextType = { - // Navigation manager, e.g. history.push('/home') - // https://github.com/mjackson/history - history: PropTypes.object.isRequired, // Enables critical path CSS rendering // https://github.com/kriasoft/isomorphic-style-loader insertCss: PropTypes.func.isRequired, diff --git a/src/components/Footer/Footer.js b/src/components/Footer/Footer.js @@ -20,6 +20,8 @@ function Footer() { <span className={s.spacer}>·</span> <Link className={s.link} to="/">Home</Link> <span className={s.spacer}>·</span> + <Link className={s.link} to="/admin">Admin</Link> + <span className={s.spacer}>·</span> <Link className={s.link} to="/privacy">Privacy</Link> <span className={s.spacer}>·</span> <Link className={s.link} to="/not-found">Not Found</Link> diff --git a/src/components/Link/Link.js b/src/components/Link/Link.js @@ -8,6 +8,7 @@ */ import React, { Component, PropTypes } from 'react'; +import history from '../../core/history'; function isLeftClickEvent(event) { return event.button === 0; @@ -25,10 +26,6 @@ class Link extends Component { onClick: PropTypes.func, }; - static contextTypes = { - history: PropTypes.object.isRequired, - }; - handleClick = (event) => { if (this.props.onClick) { this.props.onClick(event); @@ -43,7 +40,7 @@ class Link extends Component { } event.preventDefault(); - this.context.history.push(this.props.to); + history.push(this.props.to); }; render() { diff --git a/src/core/history.js b/src/core/history.js @@ -0,0 +1,5 @@ +import createBrowserHistory from 'history/createBrowserHistory'; + +// Navigation manager, e.g. history.push('/home') +// https://github.com/mjackson/history +export default process.env.BROWSER && createBrowserHistory(); diff --git a/src/routes/admin/Admin.css b/src/routes/admin/Admin.css @@ -0,0 +1,21 @@ +/** + * React Starter Kit (https://www.reactstarterkit.com/) + * + * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE.txt file in the root directory of this source tree. + */ + +@import '../../components/variables.css'; + +.root { + padding-left: 20px; + padding-right: 20px; +} + +.container { + margin: 0 auto; + padding: 0 0 40px; + max-width: var(--max-content-width); +} diff --git a/src/routes/admin/Admin.js b/src/routes/admin/Admin.js @@ -0,0 +1,32 @@ +/** + * React Starter Kit (https://www.reactstarterkit.com/) + * + * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE.txt file in the root directory of this source tree. + */ + +import React, { PropTypes } from 'react'; +import withStyles from 'isomorphic-style-loader/lib/withStyles'; +import Layout from '../../components/Layout'; +import s from './Admin.css'; + +function Admin({ title }) { + return ( + <Layout> + <div className={s.root}> + <div className={s.container}> + <h1>{title}</h1> + <p>...</p> + </div> + </div> + </Layout> + ); +} + +Admin.propTypes = { + title: PropTypes.string.isRequired, +}; + +export default withStyles(s)(Admin); diff --git a/src/routes/admin/index.js b/src/routes/admin/index.js @@ -0,0 +1,31 @@ +/** + * React Starter Kit (https://www.reactstarterkit.com/) + * + * Copyright © 2014-2016 Kriasoft, LLC. All rights reserved. + * + * This source code is licensed under the MIT license found in the + * LICENSE.txt file in the root directory of this source tree. + */ + +import React from 'react'; +import Admin from './Admin'; + +const title = 'Admin Page'; +const isAdmin = false; + +export default { + + path: '/admin', + + action() { + if (!isAdmin) { + return { redirect: '/login' }; + } + + return { + title, + component: <Admin title={title} />, + }; + }, + +}; diff --git a/src/routes/index.js b/src/routes/index.js @@ -20,6 +20,7 @@ export default { require('./contact').default, require('./login').default, require('./register').default, + require('./admin').default, // place new routes before... require('./content').default, diff --git a/src/server.js b/src/server.js @@ -18,7 +18,6 @@ import jwt from 'jsonwebtoken'; import React from 'react'; import ReactDOM from 'react-dom/server'; import UniversalRouter from 'universal-router'; -import createMemoryHistory from 'history/createMemoryHistory'; import PrettyError from 'pretty-error'; import App from './components/App'; import Html from './components/Html'; @@ -91,11 +90,6 @@ app.get('*', async (req, res, next) => { // Global (context) variables that can be easily accessed from any React component // https://facebook.github.io/react/docs/context.html const context = { - // Navigation manager, e.g. history.push('/home') - // https://github.com/mjackson/history - history: createMemoryHistory({ - initialEntries: [req.url], - }), // Enables critical path CSS rendering // https://github.com/kriasoft/isomorphic-style-loader insertCss: (...styles) => { @@ -109,6 +103,11 @@ app.get('*', async (req, res, next) => { query: req.query, }); + if (route.redirect) { + res.redirect(route.status || 302, route.redirect); + return; + } + const data = { ...route }; data.children = ReactDOM.renderToString(<App context={context}>{route.component}</App>); data.style = [...css].join('');