silverstripe-framework/admin/client/src/boot/BootRoutes.js
2016-07-29 15:58:23 +12:00

147 lines
4.4 KiB
JavaScript

import $ from 'jQuery';
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { Router as ReactRouter, useRouterHistory } from 'react-router';
import createHistory from 'history/lib/createBrowserHistory';
import Config from 'lib/Config';
import pageRouter from 'lib/Router';
import reactRouteRegister from 'lib/ReactRouteRegister';
import App from 'containers/App/App';
import { syncHistoryWithStore } from 'react-router-redux';
/**
* Bootstraps routes
*/
class BootRoutes {
/**
* @param {Object} store Redux store
*/
constructor(store) {
this.store = store;
// pageRouter must be initialised, regardless of whether we are
// using page.js routing for this request.
const base = Config.get('absoluteBaseUrl');
pageRouter.setAbsoluteBase(base);
}
/**
* Conditionally registers routes either as legacy (via page.js) or react-route powered routes
*
* @param {String} location Current location to check
*/
start(location) {
// Decide which router to use
if (this.matchesLegacyRoute(location)) {
this.initLegacyRouter();
} else {
this.initReactRouter();
}
}
/**
* Determine if the given location matches a legacy or a react route.
*
* @param {String} location URL
* @return {Boolean} True if this is a legacy non-react route
*/
matchesLegacyRoute(location) {
// Legacy routes will always cause a full page reload
const sections = Config.get('sections');
const currentPath = pageRouter.resolveURLToBase(location).replace(/\/$/, '');
// Check if the current url matches a legacy route
return !!Object.keys(sections).find((key) => {
const section = sections[key];
const route = pageRouter.resolveURLToBase(section.url).replace(/\/$/, '');
// Skip react routes
if (section.reactRouter) {
return false;
}
// Check if the beginning of the route is the same as the current location.
// Since we haven't decided on a router yet, we can't use it for route matching.
// TODO Limit full page load when transitioning from legacy to react route or vice versa
return currentPath.match(route);
});
}
/**
* Initialise routing to use react-route powered routing
*/
initReactRouter() {
reactRouteRegister.updateRootRoute({
component: App,
});
let history = syncHistoryWithStore(
useRouterHistory(createHistory)({
basename: Config.get('baseUrl'),
}),
this.store
);
ReactDOM.render(
<Provider store={this.store}>
<ReactRouter
history={history}
routes={reactRouteRegister.getRootRoute()}
/>
</Provider>,
document.getElementsByClassName('cms-content')[0]
);
}
/**
* Initialise routing to use page.js powered legacy routing for non-react sections
*/
initLegacyRouter() {
const sections = Config.get('sections');
const store = this.store;
pageRouter('*', (ctx, next) => {
// eslint-disable-next-line no-param-reassign
ctx.store = store;
next();
});
// Register all top level routes.
// This can be removed when top level sections are converted to React,
// have their own JavaScript controllers, and register their own routes.
let lastPath = null;
Object.keys(sections).forEach((key) => {
let route = pageRouter.resolveURLToBase(sections[key].url);
route = route.replace(/\/$/, ''); // Remove trailing slash
route = `${route}(/*?)?`; // add optional trailing slash
// page.js based routing, excludes any React-powered sections
pageRouter(route, (ctx, next) => {
if (document.readyState !== 'complete' || ctx.init) {
next();
return;
}
// Bootstrap on initial load
if (!lastPath) {
lastPath = window.location.pathname;
}
// Verify that this is a true state change. E.g. not a hash change.
// This emulates behaviour of old html history.js
const forceReload = ctx.data && ctx.data.__forceReload;
if (ctx.path !== lastPath || forceReload) {
// Load the panel and stop processing routes.
lastPath = ctx.path.replace(/#.*$/, '');
$('.cms-container')
.entwine('ss')
.handleStateChange(null, ctx.state);
}
});
});
pageRouter.start();
}
}
export default BootRoutes;