2018-02-22 19:48:46 +01:00
|
|
|
import React from 'react';
|
|
|
|
import { renderToString } from 'react-dom/server';
|
|
|
|
import { createStore, applyMiddleware } from 'redux';
|
|
|
|
import { Provider } from 'react-redux';
|
2018-02-23 00:43:26 +01:00
|
|
|
import { StaticRouter } from 'react-router-dom';
|
2018-12-14 04:55:59 +01:00
|
|
|
import renderFullPage from './renderFullPage';
|
2018-02-22 19:48:46 +01:00
|
|
|
import createSagaMiddleware from 'redux-saga';
|
2018-02-23 00:43:26 +01:00
|
|
|
import { call } from 'redux-saga/effects';
|
2018-06-07 00:12:07 +02:00
|
|
|
import Helmet from 'react-helmet';
|
2018-09-28 05:56:10 +02:00
|
|
|
import * as httpContext from 'express-http-context';
|
2018-06-07 00:12:07 +02:00
|
|
|
|
2018-05-23 20:16:47 +02:00
|
|
|
import Reducers from '@reducers';
|
|
|
|
import GAListener from '@components/GAListener';
|
|
|
|
import App from '@app';
|
|
|
|
import Sagas from '@sagas';
|
|
|
|
import Actions from '@actions';
|
2018-02-23 03:05:00 +01:00
|
|
|
|
2018-12-14 04:55:59 +01:00
|
|
|
const createCanonicalLink = require('../../utils/createCanonicalLink');
|
2018-11-01 15:58:12 +01:00
|
|
|
|
|
|
|
const getCanonicalUrlFromShow = show => {
|
|
|
|
const requestId = show.requestList[show.request.id];
|
|
|
|
const requestType = show.request.type;
|
2018-12-16 08:39:34 +01:00
|
|
|
|
2018-11-12 16:20:41 +01:00
|
|
|
if (!requestId || !requestType) {
|
|
|
|
return null;
|
|
|
|
}
|
2018-12-16 08:39:34 +01:00
|
|
|
|
2018-11-01 15:58:12 +01:00
|
|
|
switch (requestType) {
|
|
|
|
case 'ASSET_DETAILS':
|
2018-11-02 18:27:18 +01:00
|
|
|
const asset = show.assetList[requestId.key];
|
|
|
|
return createCanonicalLink({ asset: { ...asset.claimData, shortId: asset.shortId }});
|
2018-11-01 15:58:12 +01:00
|
|
|
case 'CHANNEL':
|
|
|
|
return createCanonicalLink({ channel: show.channelList[requestId.key] });
|
|
|
|
default:
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2018-02-23 00:43:26 +01:00
|
|
|
const returnSagaWithParams = (saga, params) => {
|
|
|
|
return function * () {
|
|
|
|
yield call(saga, params);
|
|
|
|
};
|
|
|
|
};
|
2018-11-12 17:19:31 +01:00
|
|
|
|
2018-12-14 04:55:59 +01:00
|
|
|
export default (req, res) => {
|
2018-02-22 19:48:46 +01:00
|
|
|
let context = {};
|
|
|
|
|
2018-09-28 05:56:10 +02:00
|
|
|
const {
|
|
|
|
action = false,
|
|
|
|
saga = false,
|
|
|
|
} = httpContext.get('routeData');
|
2018-12-18 02:52:51 +01:00
|
|
|
|
2018-12-18 01:16:19 +01:00
|
|
|
if (action === 'fallback') {
|
|
|
|
res.status(404);
|
|
|
|
}
|
2018-02-22 19:48:46 +01:00
|
|
|
|
2018-09-28 05:56:10 +02:00
|
|
|
const runSaga = (action !== false && saga !== false);
|
|
|
|
const renderPage = (store) => {
|
2018-10-09 20:10:06 +02:00
|
|
|
|
|
|
|
// Workaround, remove when a solution for async httpContext exists
|
|
|
|
const showState = store.getState().show;
|
|
|
|
const assetKeys = Object.keys(showState.assetList);
|
2018-12-16 08:39:34 +01:00
|
|
|
|
2018-12-14 23:10:12 +01:00
|
|
|
if(assetKeys.length !== 0) {
|
2018-10-09 20:10:06 +02:00
|
|
|
res.claimId = showState.assetList[assetKeys[0]].claimId;
|
|
|
|
} else {
|
2018-12-14 23:10:12 +01:00
|
|
|
const channelKeys = Object.keys(showState.channelList);
|
|
|
|
|
2018-12-18 01:16:19 +01:00
|
|
|
if (channelKeys.length !== 0) {
|
2018-12-14 23:10:12 +01:00
|
|
|
res.claimId = showState.channelList[channelKeys[0]].longId;
|
|
|
|
res.isChannel = true;
|
2018-12-18 01:16:19 +01:00
|
|
|
}
|
2018-10-09 20:10:06 +02:00
|
|
|
}
|
|
|
|
|
2018-09-28 05:56:10 +02:00
|
|
|
// render component to a string
|
|
|
|
const html = renderToString(
|
|
|
|
<Provider store={store}>
|
|
|
|
<StaticRouter location={req.url} context={context}>
|
|
|
|
<GAListener>
|
|
|
|
<App />
|
|
|
|
</GAListener>
|
|
|
|
</StaticRouter>
|
|
|
|
</Provider>
|
|
|
|
);
|
2018-02-22 19:48:46 +01:00
|
|
|
|
2018-09-28 05:56:10 +02:00
|
|
|
// get head tags from helmet
|
|
|
|
const helmet = Helmet.renderStatic();
|
2018-02-22 19:48:46 +01:00
|
|
|
|
2018-09-28 05:56:10 +02:00
|
|
|
// check for a redirect
|
|
|
|
if (context.url) {
|
|
|
|
return res.redirect(301, context.url);
|
|
|
|
}
|
2018-02-23 03:05:00 +01:00
|
|
|
|
2018-09-28 05:56:10 +02:00
|
|
|
// get the initial state from our Redux store
|
|
|
|
const preloadedState = store.getState();
|
2018-02-22 19:48:46 +01:00
|
|
|
|
2018-09-28 05:56:10 +02:00
|
|
|
// send the rendered page back to the client
|
|
|
|
res.send(renderFullPage(helmet, html, preloadedState));
|
|
|
|
};
|
|
|
|
|
|
|
|
if (runSaga) {
|
|
|
|
// create and apply middleware
|
|
|
|
const sagaMiddleware = createSagaMiddleware();
|
|
|
|
const middleware = applyMiddleware(sagaMiddleware);
|
|
|
|
|
|
|
|
// create a new Redux store instance
|
|
|
|
const store = createStore(Reducers, middleware);
|
|
|
|
|
|
|
|
// create an action to handle the given url,
|
|
|
|
// and create a the saga needed to handle that action
|
|
|
|
const boundAction = action(req.params, req.url);
|
|
|
|
const boundSaga = returnSagaWithParams(saga, boundAction);
|
2018-02-22 19:48:46 +01:00
|
|
|
|
2018-09-28 05:56:10 +02:00
|
|
|
// run the saga middleware with the saga call
|
|
|
|
sagaMiddleware
|
|
|
|
.run(boundSaga)
|
|
|
|
.done
|
2018-11-01 15:58:12 +01:00
|
|
|
.then(() => {
|
|
|
|
// redirect if request does not use canonical url
|
|
|
|
const canonicalUrl = getCanonicalUrlFromShow(store.getState().show);
|
2018-12-18 02:52:51 +01:00
|
|
|
|
2018-12-18 01:16:19 +01:00
|
|
|
if (!canonicalUrl) {
|
|
|
|
res.status(404);
|
|
|
|
}
|
2018-12-18 02:52:51 +01:00
|
|
|
|
2018-11-01 15:58:12 +01:00
|
|
|
if (canonicalUrl && canonicalUrl !== req.originalUrl) {
|
|
|
|
console.log(`redirecting ${req.originalUrl} to ${canonicalUrl}`);
|
|
|
|
res.redirect(canonicalUrl);
|
|
|
|
}
|
2018-12-18 02:52:51 +01:00
|
|
|
|
2018-11-01 15:58:12 +01:00
|
|
|
return renderPage(store)
|
|
|
|
});
|
2018-09-28 05:56:10 +02:00
|
|
|
} else {
|
|
|
|
const store = createStore(Reducers);
|
|
|
|
renderPage(store);
|
|
|
|
}
|
2018-02-22 19:48:46 +01:00
|
|
|
};
|