* fix(files): Decouple files from challenges * feat(server/react): Remove action logger use redux remote devtools instead! * feat(Challenges): Disable js on edit, enable on execute * feat(Challenge/Preview): Show message when js is disabled * refactor(frameEpic): Reduce code by using lodash * feat(frameEpic): Disable js in preview by state * feat(frameEpic): Colocate epic in Challenges/redux * refactor(ExecuteChallengeEpic): CoLocated with Challenges * refactor(executeChallengesEpic): Separate tests from main logic * feat(Challenge/Preview): Update main on edit * feat(frameEpuc): Replace frame on edit/execute This allows for sandbox to work properly * fix(Challenges/Utils): Require utisl * revert(frameEpic): Hoist function to mount code in frame * fix(frameEpic): Ensure new frame is given classname * feat(executeChallenge): Update main on code unlocked * fix(frameEpic): Filter out empty test message * fix(Challenge/Preview): Remove unnessary quote in classname * feat(codeStorageEpic): Separate localstorage from solutions loading * fix(fetchUser): Merge user actions into one prefer many effects from one action over one action to one effect * fix(themes): Centralize theme utils and defs * fix(entities.user): Fix user reducer namespacing * feat(frame): Refactor frameEpic to util * feat(Challenges.redux): Should not attempt to update main from storage * fix(loadPreviousChallengeEpic): Refactor for RFR * fix(Challenges.Modern): Show preview plane
104 lines
2.8 KiB
JavaScript
104 lines
2.8 KiB
JavaScript
import debug from 'debug';
|
|
import { renderToString } from 'react-dom/server';
|
|
import createMemoryHistory from 'history/createMemoryHistory';
|
|
import { NOT_FOUND } from 'redux-first-router';
|
|
import devtoolsEnhancer from 'remote-redux-devtools';
|
|
|
|
import {
|
|
errorThrowerMiddleware
|
|
} from '../utils/react.js';
|
|
import { createApp, provideStore, App } from '../../common/app';
|
|
import waitForEpics from '../../common/utils/wait-for-epics.js';
|
|
import { titleSelector } from '../../common/app/redux';
|
|
|
|
const log = debug('fcc:react-server');
|
|
const isDev = process.env.NODE_ENV !== 'production';
|
|
|
|
// add routes here as they slowly get reactified
|
|
// remove their individual controllers
|
|
const routes = [
|
|
'/challenges',
|
|
'/challenges/*',
|
|
'/map',
|
|
'/settings',
|
|
'/settings/*'
|
|
];
|
|
|
|
const devRoutes = [];
|
|
|
|
const middlewares = isDev ? [errorThrowerMiddleware] : [];
|
|
export default function reactSubRouter(app) {
|
|
var router = app.loopback.Router();
|
|
|
|
router.get('/videos', (req, res) => res.redirect('/map'));
|
|
router.get(
|
|
'/videos/:dashedName',
|
|
(req, res) => res.redirect(`/challenges/${req.params.dashedName}`)
|
|
);
|
|
|
|
// These routes are in production
|
|
routes.forEach((route) => {
|
|
router.get(route, serveReactApp);
|
|
});
|
|
|
|
if (process.env.NODE_ENV === 'development') {
|
|
devRoutes.forEach(function(route) {
|
|
router.get(route, serveReactApp);
|
|
});
|
|
}
|
|
|
|
app.use('/:lang', router);
|
|
|
|
function serveReactApp(req, res, next) {
|
|
const { lang } = req;
|
|
const serviceOptions = { req };
|
|
createApp({
|
|
serviceOptions,
|
|
middlewares,
|
|
enhancers: [
|
|
devtoolsEnhancer({ name: 'server' })
|
|
],
|
|
history: createMemoryHistory({ initialEntries: [ req.originalUrl ] }),
|
|
defaultStaet: { app: { lang } }
|
|
})
|
|
.filter(({
|
|
location: {
|
|
type,
|
|
kind,
|
|
pathname
|
|
} = {}
|
|
}) => {
|
|
if (kind === 'redirect') {
|
|
log('react found a redirect');
|
|
res.redirect(pathname);
|
|
return false;
|
|
}
|
|
|
|
if (type === NOT_FOUND) {
|
|
log(`react tried to find ${req.path} but got 404`);
|
|
next();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
})
|
|
.flatMap(({ store, epic }) => {
|
|
return waitForEpics(epic)
|
|
.map(() => renderToString(
|
|
provideStore(App, store)
|
|
))
|
|
.map((markup) => ({ markup, store, epic }));
|
|
})
|
|
.do(({ markup, store, epic }) => {
|
|
log('react markup rendered, data fetched');
|
|
const state = store.getState();
|
|
const title = titleSelector(state);
|
|
epic.dispose();
|
|
res.expose(state, 'data', { isJSON: true });
|
|
res.expose(req.flash(), 'flash', { isJSON: true });
|
|
res.render('layout-react', { markup, title });
|
|
})
|
|
.subscribe(() => log('html rendered and sent'), next);
|
|
}
|
|
}
|