diff --git a/helpers/handleRender.jsx b/helpers/handlePageRender.jsx similarity index 91% rename from helpers/handleRender.jsx rename to helpers/handlePageRender.jsx index 58b80206..5ec46c3b 100644 --- a/helpers/handleRender.jsx +++ b/helpers/handlePageRender.jsx @@ -3,7 +3,7 @@ import { renderToString } from 'react-dom/server'; import { createStore } from 'redux'; import Reducer from '../react/reducers'; import { Provider } from 'react-redux'; -import StaticRouter from 'react-router-dom/StaticRouter'; +import { StaticRouter } from 'react-router-dom'; import GAListener from '../react/components/GAListener'; import App from '../react/app'; import renderFullPage from './renderFullPage.js'; @@ -28,7 +28,7 @@ module.exports = (req, res) => { // check for a redirect if (context.url) { // Somewhere a `` was rendered - res.redirect(301, context.url); + return res.redirect(301, context.url); } else { // we're good, send the response } diff --git a/helpers/handleShowRender.jsx b/helpers/handleShowRender.jsx new file mode 100644 index 00000000..96787bb1 --- /dev/null +++ b/helpers/handleShowRender.jsx @@ -0,0 +1,68 @@ +import React from 'react'; +import { renderToString } from 'react-dom/server'; +import { createStore, applyMiddleware } from 'redux'; +import Reducer from '../react/reducers'; +import { Provider } from 'react-redux'; +import { StaticRouter, matchPath } from 'react-router-dom'; +import GAListener from '../react/components/GAListener'; +import App from '../react/app'; +import renderFullPage from './renderFullPage'; +import routes from '../react/routes'; +import createSagaMiddleware from 'redux-saga'; +import rootSaga from '../react/sagas'; + +module.exports = (req, res) => { + let context = {}; + + // create and apply middleware + const sagaMiddleware = createSagaMiddleware(); + const middleware = applyMiddleware(sagaMiddleware); + + // create a new Redux store instance + const store = createStore(Reducer, middleware); + + // run the saga middlweare + sagaMiddleware.run(rootSaga); + + // get data as promises + const promises = []; + routes.some(route => { + const match = matchPath(req.path, route); + if (match) { + let fetchData = route.component.fetchData; + if (fetchData instanceof Function) { + promises.push(fetchData(store, match)); + }; + }; + }); + console.log('promises', promises); + + // after promises have resolved, render the component + Promise.all(promises).then(data => { + console.log('data:', data); + // render component to a string + const html = renderToString( + + + + + + + + ); + + // check for a redirect + if (context.url) { + console.log('REDIRECTING:', context.url); + return res.redirect(301, context.url); + } else { + console.log(`we're good, send the response`, html); + } + + // get the initial state from our Redux store + const preloadedState = store.getState(); + + // send the rendered page back to the client + res.send(renderFullPage(html, preloadedState)); + }); +}; diff --git a/package.json b/package.json index 8a02ac24..8b7e5403 100644 --- a/package.json +++ b/package.json @@ -35,11 +35,13 @@ "config": "^1.26.1", "connect-multiparty": "^2.0.0", "cookie-session": "^2.0.0-beta.3", + "cross-fetch": "^1.1.1", "express": "^4.15.2", "express-handlebars": "^3.0.0", "form-data": "^2.3.1", "helmet": "^3.8.1", "mysql2": "^1.3.5", + "node-fetch": "^2.0.0", "passport": "^0.4.0", "passport-local": "^1.0.0", "prop-types": "^15.6.0", diff --git a/react/actions/show.js b/react/actions/show.js index 3a23b66d..5c31f58b 100644 --- a/react/actions/show.js +++ b/react/actions/show.js @@ -4,6 +4,7 @@ import { CHANNEL, ASSET_LITE, ASSET_DETAILS } from 'constants/show_request_types // basic request parsing export function handleShowPageUri (params) { + console.log('dispatching handleShowpageUri'); return { type: actions.HANDLE_SHOW_URI, data: params, diff --git a/react/api/assetApi.js b/react/api/assetApi.js index 10e21ac4..12f5fa6c 100644 --- a/react/api/assetApi.js +++ b/react/api/assetApi.js @@ -1,4 +1,5 @@ import Request from 'utils/request'; +const { site: { host } } = require('../../config/speechConfig.js'); export function getLongClaimId (name, modifier) { // console.log('getting long claim id for asset:', name, modifier); @@ -15,25 +16,23 @@ export function getLongClaimId (name, modifier) { body['claimName'] = name; const params = { method : 'POST', - headers: new Headers({ - 'Content-Type': 'application/json', - }), - body: JSON.stringify(body), + headers: { 'Content-Type': 'application/json' }, + body : JSON.stringify(body), }; // create url - const url = `/api/claim/long-id`; + const url = `${host}/api/claim/long-id`; // return the request promise return Request(url, params); }; export function getShortId (name, claimId) { // console.log('getting short id for asset:', name, claimId); - const url = `/api/claim/short-id/${claimId}/${name}`; + const url = `${host}/api/claim/short-id/${claimId}/${name}`; return Request(url); }; export function getClaimData (name, claimId) { // console.log('getting claim data for asset:', name, claimId); - const url = `/api/claim/data/${name}/${claimId}`; + const url = `${host}/api/claim/data/${name}/${claimId}`; return Request(url); }; diff --git a/react/api/channelApi.js b/react/api/channelApi.js index d060fe2c..ddc25c05 100644 --- a/react/api/channelApi.js +++ b/react/api/channelApi.js @@ -1,16 +1,17 @@ import Request from 'utils/request'; import request from '../utils/request'; +const { site: { host } } = require('../../config/speechConfig.js'); export function getChannelData (name, id) { console.log('getting channel data for channel:', name, id); if (!id) id = 'none'; - const url = `/api/channel/data/${name}/${id}`; + const url = `${host}/api/channel/data/${name}/${id}`; return request(url); }; export function getChannelClaims (name, longId, page) { console.log('getting channel claims for channel:', name, longId); if (!page) page = 1; - const url = `/api/channel/claims/${name}/${longId}/${page}`; + const url = `${host}/api/channel/claims/${name}/${longId}/${page}`; return Request(url); }; diff --git a/react/api/fileApi.js b/react/api/fileApi.js index ecc4a652..64ff1ad9 100644 --- a/react/api/fileApi.js +++ b/react/api/fileApi.js @@ -1,11 +1,12 @@ import Request from 'utils/request'; +const { site: { host } } = require('../../config/speechConfig.js'); export function checkFileAvailability (name, claimId) { - const url = `/api/file/availability/${name}/${claimId}`; + const url = `${host}/api/file/availability/${name}/${claimId}`; return Request(url); } export function triggerClaimGet (name, claimId) { - const url = `/api/claim/get/${name}/${claimId}`; + const url = `${host}/api/claim/get/${name}/${claimId}`; return Request(url); } diff --git a/react/app.js b/react/app.js index 212713c0..e8aa7ea5 100644 --- a/react/app.js +++ b/react/app.js @@ -1,20 +1,13 @@ import React from 'react'; import { Route, Switch } from 'react-router-dom'; -import PublishPage from 'components/PublishPage'; -import AboutPage from 'components/AboutPage'; -import LoginPage from 'containers/LoginPage'; -import ShowPage from 'containers/ShowPage'; -import FourOhFourPage from 'components/FourOhFourPage'; +import routes from './routes'; const App = () => { return ( - - - - - - + {routes.map((route, index) => ( + + ))} ); }; diff --git a/react/containers/ShowPage/view.jsx b/react/containers/ShowPage/view.jsx index cf565f02..fdf8ec87 100644 --- a/react/containers/ShowPage/view.jsx +++ b/react/containers/ShowPage/view.jsx @@ -3,10 +3,15 @@ import ErrorPage from 'components/ErrorPage'; import ShowAssetLite from 'components/ShowAssetLite'; import ShowAssetDetails from 'components/ShowAssetDetails'; import ShowChannel from 'components/ShowChannel'; +import { handleShowPageUri } from 'actions/show'; import { CHANNEL, ASSET_LITE, ASSET_DETAILS } from 'constants/show_request_types'; class ShowPage extends React.Component { + static fetchData (store, match) { + console.log('the store:', store); + return store.dispatch(handleShowPageUri(match.params)); + } componentDidMount () { this.props.handleShowPageUri(this.props.match.params); } diff --git a/react/routes.js b/react/routes.js new file mode 100644 index 00000000..9bc9c0ae --- /dev/null +++ b/react/routes.js @@ -0,0 +1,33 @@ +import PublishPage from 'components/PublishPage'; +import AboutPage from 'components/AboutPage'; +import LoginPage from 'containers/LoginPage'; +import ShowPage from 'containers/ShowPage'; +import FourOhFourPage from 'components/FourOhFourPage'; + +const routes = [ + { path : '/', + exact : true, + component: PublishPage, + }, + { path : '/about', + exact : true, + component: AboutPage, + }, + { path : '/login', + exact : true, + component: LoginPage, + }, + { path : '/:identifier/:claim', + exact : true, + component: ShowPage, + }, + { path : '/:claim', + exact : true, + component: ShowPage, + }, + { + component: FourOhFourPage, + }, +]; + +export default routes; diff --git a/react/utils/request.js b/react/utils/request.js index 81674721..7be069d9 100644 --- a/react/utils/request.js +++ b/react/utils/request.js @@ -1,3 +1,5 @@ +import 'cross-fetch/polyfill'; + /** * Parses the JSON returned by a network request * diff --git a/routes/page-routes.js b/routes/page-routes.js index 4e013199..2203710c 100644 --- a/routes/page-routes.js +++ b/routes/page-routes.js @@ -1,29 +1,29 @@ const { site } = require('../config/speechConfig.js'); -const handleRender = require('../helpers/handleRender.jsx'); +const handlePageRender = require('../helpers/handlePageRender.jsx'); module.exports = (app) => { // route for the home page app.get('/', (req, res) => { - handleRender(req, res); + handlePageRender(req, res); }); // route to display login page app.get('/login', (req, res) => { - handleRender(req, res); + handlePageRender(req, res); }); // route to show 'about' page app.get('/about', (req, res) => { - handleRender(req, res); + handlePageRender(req, res); }); // route to display a list of the trending images app.get('/trending', (req, res) => { res.status(301).redirect('/popular'); }); app.get('/popular', (req, res) => { - handleRender(req, res); + handlePageRender(req, res); }); // route to display a list of the trending images app.get('/new', (req, res) => { - handleRender(req, res); + handlePageRender(req, res); }); // route to send embedable video player (for twitter) app.get('/embed/:claimId/:name', ({ params }, res) => { diff --git a/routes/serve-routes.js b/routes/serve-routes.js index 9a1c303e..040bfede 100644 --- a/routes/serve-routes.js +++ b/routes/serve-routes.js @@ -1,13 +1,13 @@ const { sendGAServeEvent } = require('../helpers/googleAnalytics'); - const { determineResponseType, flipClaimNameAndIdForBackwardsCompatibility, logRequestData, getClaimIdAndServeAsset } = require('../helpers/serveHelpers.js'); const lbryUri = require('../helpers/lbryUri.js'); - +const handleShowRender = require('../helpers/handleShowRender.jsx'); const SERVE = 'SERVE'; module.exports = (app) => { // route to serve a specific asset using the channel or claim id - app.get('/:identifier/:claim', ({ headers, ip, originalUrl, params }, res) => { + app.get('/:identifier/:claim', (req, res) => { + const { headers, ip, originalUrl, params } = req; // decide if this is a show request let hasFileExtension; try { @@ -17,7 +17,8 @@ module.exports = (app) => { } let responseType = determineResponseType(hasFileExtension, headers); if (responseType !== SERVE) { - return res.status(200).render('index'); + // return res.status(200).render('index'); + return handleShowRender(req, res); } // handle serve request // send google analytics diff --git a/webpack.config.js b/webpack.config.js index 5e8d10f4..ef184b3b 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -41,7 +41,7 @@ module.exports = [ __dirname: false, }, externals: [nodeExternals()], - entry : ['./server.js'], + entry : ['babel-polyfill', 'whatwg-fetch', './server.js'], output : { path : Path.resolve(__dirname), publicPath: '/',