350 open graph react #360
|
@ -17,7 +17,7 @@ spee.ch is a single-serving site that reads and publishes images and videos to a
|
||||||
* create your `speechConfig.js` file
|
* create your `speechConfig.js` file
|
||||||
* copy `speechConfig.js.example` and name it `speechConfig.js`
|
* copy `speechConfig.js.example` and name it `speechConfig.js`
|
||||||
* replace the `null` values in the config file with the appropriate values for your environement
|
* replace the `null` values in the config file with the appropriate values for your environement
|
||||||
* to start the server, from your command line run `node speech.js`
|
* to start the server, from your command line run `node serverindex.js`
|
||||||
* To run hot, use `nodemon` instead of `node`
|
* To run hot, use `nodemon` instead of `node`
|
||||||
* visit [localhost:3000](http://localhost:3000)
|
* visit [localhost:3000](http://localhost:3000)
|
||||||
|
|
||||||
|
|
32
helpers/handleRender.jsx
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
import { renderToString } from 'react-dom/server';
|
||||||
|
import { createStore } from 'redux';
|
||||||
|
import Reducer from '../react/reducers';
|
||||||
|
import renderFullPage from './renderFullPage.js';
|
||||||
|
|
||||||
|
import StaticRouter from 'react-router-dom/StaticRouter';
|
||||||
|
import GAListener from '../react/components/GAListener';
|
||||||
|
import App from '../react/app';
|
||||||
|
|
||||||
|
module.exports = (req, res) => {
|
||||||
|
let context = {};
|
||||||
|
|
||||||
|
// create a new Redux store instance
|
||||||
|
const store = createStore(Reducer);
|
||||||
|
|
||||||
|
// render component to a string
|
||||||
|
const html = renderToString(
|
||||||
|
<Provider store={store}>
|
||||||
|
<StaticRouter location={req.url} context={context}>
|
||||||
|
<GAListener>
|
||||||
|
<App />
|
||||||
|
</GAListener>
|
||||||
|
</StaticRouter>
|
||||||
|
</Provider>
|
||||||
|
);
|
||||||
|
|
||||||
|
// 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));
|
||||||
|
};
|
21
helpers/renderFullPage.js
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
module.exports = (html, preloadedState) => {
|
||||||
|
// take the html and preloadedState and return the full page
|
||||||
|
return `
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb#">
|
||||||
|
<head>
|
||||||
|
<!--google font-->
|
||||||
|
<link href="https://fonts.googleapis.com/css?family=Roboto:300" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
<body id="main-body">
|
||||||
|
<div class="row row--tall flex-container--column">
|
||||||
|
<div id="react-app" class="row row--tall flex-container--column">${html}</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
window.__PRELOADED_STATE__ = ${JSON.stringify(preloadedState).replace(/</g, '\\\u003c')}
|
||||||
|
</script>
|
||||||
|
<script src="/static/bundle.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`;
|
||||||
|
};
|
|
@ -7,7 +7,6 @@ const handlebarsHelpers = require('./helpers/handlebarsHelpers.js');
|
||||||
const { populateLocalsDotUser, serializeSpeechUser, deserializeSpeechUser } = require('./helpers/authHelpers.js');
|
const { populateLocalsDotUser, serializeSpeechUser, deserializeSpeechUser } = require('./helpers/authHelpers.js');
|
||||||
const config = require('./config/speechConfig.js');
|
const config = require('./config/speechConfig.js');
|
||||||
const logger = require('winston');
|
const logger = require('winston');
|
||||||
const { getDownloadDirectory } = require('./helpers/lbryApi');
|
|
||||||
const helmet = require('helmet');
|
const helmet = require('helmet');
|
||||||
const PORT = 3000; // set port
|
const PORT = 3000; // set port
|
||||||
const app = express(); // create an Express application
|
const app = express(); // create an Express application
|
||||||
|
@ -67,14 +66,7 @@ app.use(populateLocalsDotUser);
|
||||||
// start the server
|
// start the server
|
||||||
db.sequelize
|
db.sequelize
|
||||||
.sync() // sync sequelize
|
.sync() // sync sequelize
|
||||||
.then(() => { // get the download directory from the daemon
|
.then(() => { // require routes
|
||||||
logger.info('Retrieving daemon download directory...');
|
|
||||||
return getDownloadDirectory();
|
|
||||||
})
|
|
||||||
.then(hostedContentPath => {
|
|
||||||
// add the hosted content folder at a static path
|
|
||||||
app.use('/media', express.static(hostedContentPath));
|
|
||||||
// require routes
|
|
||||||
require('./routes/auth-routes.js')(app);
|
require('./routes/auth-routes.js')(app);
|
||||||
require('./routes/api-routes.js')(app);
|
require('./routes/api-routes.js')(app);
|
||||||
require('./routes/page-routes.js')(app);
|
require('./routes/page-routes.js')(app);
|
|
@ -6,7 +6,7 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "mocha --recursive",
|
"test": "mocha --recursive",
|
||||||
"test-all": "mocha --recursive",
|
"test-all": "mocha --recursive",
|
||||||
"start": "node speech.js",
|
"start": "node server.js",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"fix": "eslint . --fix",
|
"fix": "eslint . --fix",
|
||||||
"precommit": "eslint .",
|
"precommit": "eslint .",
|
||||||
|
@ -57,6 +57,7 @@
|
||||||
"sequelize-cli": "^3.0.0-3",
|
"sequelize-cli": "^3.0.0-3",
|
||||||
"sleep": "^5.1.1",
|
"sleep": "^5.1.1",
|
||||||
"universal-analytics": "^0.4.13",
|
"universal-analytics": "^0.4.13",
|
||||||
|
"webpack-node-externals": "^1.6.0",
|
||||||
"whatwg-fetch": "^2.0.3",
|
"whatwg-fetch": "^2.0.3",
|
||||||
"winston": "^2.3.1",
|
"winston": "^2.3.1",
|
||||||
"winston-slack-webhook": "billbitt/winston-slack-webhook"
|
"winston-slack-webhook": "billbitt/winston-slack-webhook"
|
||||||
|
@ -64,12 +65,15 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"babel-core": "^6.26.0",
|
"babel-core": "^6.26.0",
|
||||||
"babel-loader": "^7.1.2",
|
"babel-loader": "^7.1.2",
|
||||||
|
"babel-plugin-transform-object-rest-spread": "^6.26.0",
|
||||||
"babel-polyfill": "^6.26.0",
|
"babel-polyfill": "^6.26.0",
|
||||||
"babel-preset-es2015": "^6.24.1",
|
"babel-preset-es2015": "^6.24.1",
|
||||||
"babel-preset-react": "^6.24.1",
|
"babel-preset-react": "^6.24.1",
|
||||||
"babel-preset-stage-2": "^6.24.1",
|
"babel-preset-stage-2": "^6.24.1",
|
||||||
|
"babel-register": "^6.26.0",
|
||||||
"chai": "^4.1.2",
|
"chai": "^4.1.2",
|
||||||
"chai-http": "^3.0.0",
|
"chai-http": "^3.0.0",
|
||||||
|
"css-loader": "^0.28.9",
|
||||||
"eslint": "3.19.0",
|
"eslint": "3.19.0",
|
||||||
"eslint-config-standard": "10.2.1",
|
"eslint-config-standard": "10.2.1",
|
||||||
"eslint-plugin-import": "^2.2.0",
|
"eslint-plugin-import": "^2.2.0",
|
||||||
|
|
|
@ -10,18 +10,24 @@ import rootSaga from 'sagas';
|
||||||
Probably don't need these Probably don't need these `console.log`s here.
|
|||||||
import GAListener from 'components/GAListener';
|
import GAListener from 'components/GAListener';
|
||||||
import App from './app';
|
import App from './app';
|
||||||
|
|
||||||
|
// get the state from a global variable injected into the server-generated HTML
|
||||||
Probably don't need these Probably don't need these `console.log`s here.
|
|||||||
|
const preloadedState = window.__PRELOADED_STATE__ || null;
|
||||||
Probably don't need these Probably don't need these `console.log`s here.
|
|||||||
|
|
||||||
Probably don't need these Probably don't need these `console.log`s here.
|
|||||||
|
// Allow the passed state to be garbage-collected
|
||||||
Probably don't need these Probably don't need these `console.log`s here.
|
|||||||
|
delete window.__PRELOADED_STATE__
|
||||||
Probably don't need these Probably don't need these `console.log`s here.
|
|||||||
|
|
||||||
Probably don't need these Probably don't need these `console.log`s here.
|
|||||||
|
// create and apply middleware
|
||||||
Probably don't need these Probably don't need these `console.log`s here.
|
|||||||
const sagaMiddleware = createSagaMiddleware();
|
const sagaMiddleware = createSagaMiddleware();
|
||||||
const middleware = applyMiddleware(sagaMiddleware);
|
const middleware = applyMiddleware(sagaMiddleware);
|
||||||
|
const reduxMiddleware = window.__REDUX_DEVTOOLS_EXTENSION__ ? compose(middleware, window.__REDUX_DEVTOOLS_EXTENSION__()) : middleware;
|
||||||
Probably don't need these Probably don't need these `console.log`s here.
|
|||||||
|
|
||||||
const enhancer = window.__REDUX_DEVTOOLS_EXTENSION__ ? compose(middleware, window.__REDUX_DEVTOOLS_EXTENSION__()) : middleware;
|
// create teh store
|
||||||
Probably don't need these Probably don't need these `console.log`s here.
Probably don't need these Probably don't need these `console.log`s here.
|
|||||||
|
let store = createStore(Reducer, preloadedState, reduxMiddleware);
|
||||||
Probably don't need these Probably don't need these `console.log`s here.
Probably don't need these Probably don't need these `console.log`s here.
|
|||||||
let store = createStore(
|
|
||||||
Probably don't need these Probably don't need these `console.log`s here.
|
|||||||
Reducer,
|
|
||||||
Probably don't need these Probably don't need these `console.log`s here.
|
|||||||
enhancer,
|
|
||||||
Probably don't need these Probably don't need these `console.log`s here.
|
|||||||
);
|
|
||||||
Probably don't need these Probably don't need these `console.log`s here.
|
|||||||
|
|
||||||
|
// run the saga middlweare
|
||||||
Probably don't need these Probably don't need these `console.log`s here.
|
|||||||
sagaMiddleware.run(rootSaga);
|
sagaMiddleware.run(rootSaga);
|
||||||
|
|
||||||
|
// render the app
|
||||||
Probably don't need these Probably don't need these `console.log`s here.
|
|||||||
render(
|
render(
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
|
@ -1,4 +1,5 @@
|
||||||
const { site } = require('../config/speechConfig.js');
|
const { site } = require('../config/speechConfig.js');
|
||||||
|
// const handleRender = require('../helpers/handleRender.jsx');
|
||||||
|
|
||||||
module.exports = (app) => {
|
module.exports = (app) => {
|
||||||
// route for the home page
|
// route for the home page
|
||||||
|
|
|
@ -1,32 +1,67 @@
|
||||||
const Path = require('path');
|
const Path = require('path');
|
||||||
I would recommend creating separate webpack configs for dev/prod. Before we did our big restructure changes the apps webpack setup had three files: A base config: contains the generic stuff like babel, entry/output points, css loader, etc.
I would recommend creating separate webpack configs for dev/prod. Before we did our big restructure changes the apps webpack setup had three files:
A base config: contains the generic stuff like babel, entry/output points, css loader, etc.
A dev config which extends the base config: contains stuff like `watch: true` (not needed in prod) and source maps
A prod config which extends the base config: contains stuff like minification (that's the main thing)
`webpack-merge` is a great tool for this
https://github.com/survivejs/webpack-merge
|
|||||||
|
const nodeExternals = require('webpack-node-externals');
|
||||||
const REACT_ROOT = Path.resolve(__dirname, 'react/');
|
const REACT_ROOT = Path.resolve(__dirname, 'react/');
|
||||||
|
|
||||||
module.exports = {
|
console.log('REACT_ROOT:', REACT_ROOT);
|
||||||
entry : ['babel-polyfill', 'whatwg-fetch', './react/index.js'],
|
|
||||||
output: {
|
module.exports = [
|
||||||
path : Path.join(__dirname, '/public/bundle/'),
|
{
|
||||||
filename: 'bundle.js',
|
target: 'web',
|
||||||
},
|
entry : ['babel-polyfill', 'whatwg-fetch', './react/client.js'],
|
||||||
watch : true,
|
output: {
|
||||||
module: {
|
path : Path.join(__dirname, 'public/bundle/'),
|
||||||
loaders: [
|
publicPath: 'public/bundle/',
|
||||||
{
|
filename : 'bundle.js',
|
||||||
test : /.jsx?$/,
|
},
|
||||||
loader : 'babel-loader',
|
watch : true,
|
||||||
exclude: /node_modules/,
|
module: {
|
||||||
query : {
|
loaders: [
|
||||||
presets: ['es2015', 'react', 'stage-2'],
|
{
|
||||||
|
test : /.jsx?$/,
|
||||||
|
loader : 'babel-loader',
|
||||||
|
exclude: /node_modules/,
|
||||||
|
query : {
|
||||||
|
presets: ['es2015', 'react', 'stage-2'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
],
|
||||||
],
|
},
|
||||||
|
resolve: {
|
||||||
|
modules: [
|
||||||
|
REACT_ROOT,
|
||||||
|
'node_modules',
|
||||||
|
__dirname,
|
||||||
|
],
|
||||||
|
extensions: ['.js', '.jsx', '.scss'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
resolve: {
|
{
|
||||||
modules: [
|
target : 'node',
|
||||||
REACT_ROOT,
|
externals: nodeExternals(),
|
||||||
'node_modules',
|
entry : ['./index.js'],
|
||||||
__dirname,
|
output : {
|
||||||
],
|
path : __dirname,
|
||||||
extensions: ['.js', '.jsx', '.scss'],
|
publicPath: '',
|
||||||
|
filename : 'server.js',
|
||||||
|
},
|
||||||
|
watch : true,
|
||||||
|
module: {
|
||||||
|
loaders: [
|
||||||
|
{
|
||||||
|
loader : 'babel-loader',
|
||||||
|
exclude: /node_modules/,
|
||||||
|
query : {
|
||||||
|
presets: ['es2015'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
resolve: {
|
||||||
|
modules: [
|
||||||
|
'node_modules',
|
||||||
|
__dirname,
|
||||||
|
],
|
||||||
|
extensions: ['.js'],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
];
|
||||||
|
|
Probably don't need these
console.log
s here.