Skip to content

Commit

Permalink
Merge pull request #314 from ParabolInc/look-away
Browse files Browse the repository at this point in the history
Switch from react-look to aphrodite.

fix #313
fix #307 
fix #305 
fix #302 
fix #295 
fix #290
fix #282
fix #276
fix #252
fix #227
fix #221
fix #190
fix #124
  • Loading branch information
jordanh committed Oct 4, 2016
2 parents eda3e19 + 05a1622 commit 681a5dd
Show file tree
Hide file tree
Showing 293 changed files with 6,484 additions and 4,743 deletions.
Binary file removed .mod_bin/react-look-1.0.1-parabol-patch2.tgz
Binary file not shown.
Binary file removed .mod_bin/react-look-core-1.0.1-parabol-patch2.tgz
Binary file not shown.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ Action is a Node.js application based upon the
| Client State | [Redux](http://redux.js.org/) |
| Client Data Cache | [Cashay](https://github.com/mattkrick/cashay) |
| Front-end Views | [React](https://facebook.github.io/react/) |
| Styling | [react-look](https://github.com/rofrischmann/react-look/) |
| Styling | [aphrodite](https://github.com/khan/aphrodite) |

Action is programmed in ECMAscript ES6/7 (including async/await).
Transpilation is provided by [babel](https://github.com/babel/babel).
Expand Down
3 changes: 3 additions & 0 deletions docs/epics/0001_Action_Epic_MVTP.md
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,9 @@ be able to accept it, so that they can join their teammates.
want to see a list of their projects and actions, so that they can see what
they are responsible for.

- [ ] When a TM creates a new project or action, they will want to be able
to assign it to a specific team, so that they can organize their work.

- [X] When a TM wants to update an item on their dashboard, they will want
controls that feel similar to the controls on the team dashboard, so that
they now how to keep an accurate account of work they are responsible for.
9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
"scripts": {
"start": "NODE_ENV=production node ./src/server/server.babel.js",
"dev": "NODE_ENV=development node ./src/server/server.babel.js",
"build": "rimraf build && concurrently \"npm run build:client\" \"npm run build:server\"",
"build:themes": "babel-node ./webpack/utils/buildThemeJSON.js",
"build": "rimraf build && npm run build:themes && concurrently \"npm run build:client\" \"npm run build:server\"",
"bs": "rimraf build && npm run build && npm start",
"quickstart": "npm run db:migrate && npm run build && npm run start",
"build:client": "NODE_ENV=production webpack --config ./webpack/prod.babel.js",
Expand Down Expand Up @@ -67,14 +68,15 @@
"stats-webpack-plugin": "0.4.2",
"transform-loader": "^0.2.3",
"url-loader": "0.5.7",
"webpack": "^2.1.0-beta.18",
"webpack": "2.1.0-beta.22",
"webpack-dev-middleware": "1.7.0",
"webpack-hot-middleware": "^2.12.2",
"webpack-shell-plugin": "^0.4.3",
"why-did-you-update": "0.0.8"
},
"dependencies": {
"@segment/snippet": "^4.0.0",
"aphrodite": "^0.5.0",
"auth0": "^2.3.1",
"auth0-lock": "^9.2.2",
"babel-polyfill": "6.13.0",
Expand All @@ -94,6 +96,7 @@
"graphiql": "0.7.8",
"graphql": "0.7.0",
"graphql-custom-datetype": "^0.3.0",
"hepha": "0.0.1",
"isomorphic-fetch": "2.2.1",
"jsonwebtoken": "^7.1.9",
"jwt-decode": "^2.1.0",
Expand All @@ -109,8 +112,6 @@
"react-fontawesome": "^1.1.0",
"react-helmet": "^3.1.0",
"react-hotkey-hoc": "^0.2.1",
"react-look": "file:./.mod_bin/react-look-1.0.1-parabol-patch2.tgz",
"react-look-core": "file:./.mod_bin/react-look-core-1.0.1-parabol-patch2.tgz",
"react-notification-system": "^0.2.10",
"react-redux": "^4.4.5",
"react-router": "2.8.0",
Expand Down
19 changes: 5 additions & 14 deletions src/client/Root.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,17 @@
import React from 'react';
import {Presets, Plugins, LookRoot} from 'react-look';
import {Router, browserHistory} from 'react-router';
import {Provider} from 'react-redux';
import routesFactory from 'universal/routes/index';

const lookConfig = Presets['react-dom'];
lookConfig.styleElementId = '_look';

if (!__PRODUCTION__) {
lookConfig.plugins.push(Plugins.friendlyClassName);
}

export default function Root({store}) {
const routes = routesFactory(store);
return (
<LookRoot config={lookConfig}>
<Provider store={store}>
<div>
<Router history={browserHistory} routes={routes} />
</div>
</Provider>
</LookRoot>
<Provider store={store}>
<div>
<Router history={browserHistory} routes={routes}/>
</div>
</Provider>
);
}

Expand Down
5 changes: 4 additions & 1 deletion src/client/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import {cashay} from 'cashay';
import ActionHTTPTransport from 'universal/utils/ActionHTTPTransport';
import makeStore from './makeStore';
import Root from './Root';
import {StyleSheet} from 'aphrodite';

// const {routing} = window.__INITIAL_STATE__; // eslint-disable-line no-underscore-dangle

const initialState = {};


const createCashay = (store, cashaySchema) => {
const persistedToken = store.getState().auth.token;
cashay.create({
Expand All @@ -27,6 +29,8 @@ const createCashay = (store, cashaySchema) => {
* During the production client bundle build, the server will need to be
* stopped.
*/
// eslint-disable-next-line no-underscore-dangle
StyleSheet.rehydrate(window.__APHRODITE__);
// eslint-disable-next-line global-require
cashaySchema = require('cashay!../server/utils/getCashaySchema.js?stopRethink');
createCashay(store, cashaySchema);
Expand All @@ -42,7 +46,6 @@ const createCashay = (store, cashaySchema) => {
// eslint-disable-next-line global-require
cashaySchema = require('cashay!../server/utils/getCashaySchema.js');

// Hot Module Replacement API
// eslint-disable-next-line global-require
const {AppContainer} = require('react-hot-loader');
createCashay(store, cashaySchema);
Expand Down
68 changes: 22 additions & 46 deletions src/server/Html.js
Original file line number Diff line number Diff line change
@@ -1,78 +1,54 @@
/* eslint react/no-danger:0 */
import React, {PropTypes} from 'react';
import {LookRoot} from 'react-look';
import {Provider} from 'react-redux';
import {RouterContext} from 'react-router';
import {renderToString} from 'react-dom/server';
import makeSegmentSnippet from '@segment/snippet';

const segmentSnippet = makeSegmentSnippet.min({
const segKey = process.env.SEGMENT_WRITE_KEY;
const segmentSnippet = segKey && makeSegmentSnippet.min({
host: 'cdn.segment.com',
apiKey: process.env.SEGMENT_WRITE_KEY
apiKey: segKey
});

// Injects the server rendered state and app into a basic html template
export default function Html({
store,
title,
lookConfig,
lookCSSToken,
entries,
renderProps
}) {
const PROD = process.env.NODE_ENV === 'production';
export default function Html({store, entries, StyleSheetServer, renderProps}) {
const {manifest, app, vendor} = entries;
const initialState = `window.__INITIAL_STATE__ = ${JSON.stringify(store.getState())}`;
// TURN ON WHEN WE SEND STATE TO CLIENT
// const initialState = `window.__INITIAL_STATE__ = ${JSON.stringify(store.getState())}`;
// <script dangerouslySetInnerHTML={{__html: initialState}}/>

const root = PROD && renderToString(
<LookRoot config={lookConfig}>
const {html, css} = StyleSheetServer.renderStatic(() => {
return renderToString(
<Provider store={store}>
<RouterContext {...renderProps} />
</Provider>
</LookRoot>
);
const fontAwesomeUrl = PROD ?
'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.6.3/css/font-awesome.min.css' :
'/static/css/font-awesome.css';
);
});
const dehydratedStyles = `window.__APHRODITE__ = ${JSON.stringify(css.renderedClassNames)}`;
const fontAwesomeUrl = 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.6.3/css/font-awesome.min.css';
return (
<html>
<head>
<meta charSet="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<meta property="description" content="Team transparency, made easy."/>
<style dangerouslySetInnerHTML={{__html: lookCSSToken}} id={lookConfig.styleElementId}/>
<link
rel="stylesheet"
type="text/css"
href={fontAwesomeUrl}
/>
<title>{title}</title>
<style data-aphrodite dangerouslySetInnerHTML={{__html: css.content}}/>
<link rel="stylesheet" type="text/css" href={fontAwesomeUrl}/>
{/* segment.io analytics */}
{process.env.SEGMENT_WRITE_KEY &&
<script
type="text/javascript"
dangerouslySetInnerHTML={{__html: segmentSnippet}}
/>
}
<script type="text/javascript" dangerouslySetInnerHTML={{__html: segmentSnippet}}/>
</head>
<body>
<script dangerouslySetInnerHTML={{__html: initialState}}/>
{PROD ?
<div id="root" dangerouslySetInnerHTML={{__html: root}}></div> :
<div id="root"></div>}
{PROD && <script dangerouslySetInnerHTML={{__html: manifest.text}}/>}
{PROD && <script src={vendor.js}/>}
<script src={PROD ? app.js : '/static/app.js'}/>
<script dangerouslySetInnerHTML={{__html: dehydratedStyles}}/>
<div id="root" dangerouslySetInnerHTML={{__html: html}}></div>
<script dangerouslySetInnerHTML={{__html: manifest.text}}/>
<script src={vendor.js}/>
<script src={app.js}/>
</body>
</html>
);
}

Html.propTypes = {
StyleSheetServer: PropTypes.object,
store: PropTypes.object.isRequired,
title: PropTypes.string.isRequired,
lookConfig: PropTypes.object.isRequired,
lookCSSToken: PropTypes.string.isRequired,
entries: PropTypes.object,
renderProps: PropTypes.object
};
47 changes: 37 additions & 10 deletions src/server/createSSR.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,60 @@ import {createStore, applyMiddleware} from 'redux';
import makeReducer from 'universal/redux/makeReducer';
import {match} from 'react-router';
import thunkMiddleware from 'redux-thunk';
import React from 'react';
import {renderToStaticMarkup} from 'react-dom/server';
import Html from './Html';
const metaAndTitle = `
<meta charSet="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<meta property="description" content="Team transparency, made easy."/>
<title>Action | Parabol Inc</title>
`;

export default function createSSR(req, res) {
const finalCreateStore = applyMiddleware(thunkMiddleware)(createStore);
const store = finalCreateStore(makeReducer(), {});
if (process.env.NODE_ENV === 'production') {
// eslint-disable-next-line global-require
const routesOrPrerender = require('../../build/prerender');
const makeRoutes = routesOrPrerender('routes');
// eslint-disable-next-line global-require
/* eslint-disable global-require */
const makeRoutes = require('../../build/prerender').default;
// get the same StyleSheetServer that the universal uses
const {cashay, cashaySchema, StyleSheetServer} = require('../../build/prerender');
const assets = require('../../build/assets.json');
/* eslint-enable */
cashay.create({
store,
schema: cashaySchema,
httpTransport: {}
});
const routes = makeRoutes(store);
match({routes, location: req.url}, (error, redirectLocation, renderProps) => {
if (error) {
res.status(500).send(error.message);
} else if (redirectLocation) {
res.redirect(redirectLocation.pathname + redirectLocation.search);
} else if (renderProps) {
// So hacky i'm almost proud of it
routesOrPrerender()(req, res, store, assets.entries, renderProps);
const {entries} = assets;
const htmlString = renderToStaticMarkup(
<Html store={store} entries={entries} StyleSheetServer={StyleSheetServer} renderProps={renderProps}/>
);
res.send(`<!DOCTYPE html>${htmlString}`.replace('<head>', `<head>${metaAndTitle}`));
} else {
res.status(404).send('Not found');
}
});
} else {
// just send a cheap html doc + stringified store
// eslint-disable-next-line global-require
const routesOrPrerender = require('./routesOrPrerender');
routesOrPrerender()(req, res, store);
const devHtml = `
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="/static/css/font-awesome.css"/>
</head>
<body>
<div id="root"></div>
<script src="/static/app.js"></script>
</body>
</html>
`;
res.send(devHtml.replace('<head>', `<head>${metaAndTitle}`));
}
}
29 changes: 15 additions & 14 deletions src/server/emailSSR.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
import React from 'react';
import Oy from 'oy-vey';
import TeamInvite from 'universal/modules/email/containers/TeamInvite/TeamInvite';
import SummaryEmail from 'universal/modules/email/containers/SummaryEmail/SummaryEmail';
// import TeamInvite from 'universal/modules/email/containers/TeamInvite/TeamInvite';
// import WelcomeEmail from 'universal/modules/email/containers/WelcomeEmail/WelcomeEmail';

const demoInviteProps = {
inviterAvatar: '/static/images/avatars/jh-linkedin-avatar.jpg',
inviterName: 'Jordan Husney',
inviterFirstName: 'Jordan',
inviterEmail: 'jordan@parabol.co',
inviteeEmail: 'terry@parabol.co',
firstProject: 'Onboarding flow designed',
teamName: 'Engineering',
teamLink: 'https://prbl.io/a/b7s8x9'
};
// const demoInviteProps = {
// inviterAvatar: '/static/images/avatars/jh-linkedin-avatar.jpg',
// inviterName: 'Jordan Husney',
// inviterFirstName: 'Jordan',
// inviterEmail: 'jordan@parabol.co',
// inviteeEmail: 'terry@parabol.co',
// firstProject: 'Meeting summary email designed',
// teamName: 'Parabol',
// teamLink: 'https://prbl.io/a/b7s8x9'
// };

export default function emailSSR(req, res) {
const html = Oy.renderTemplate(<TeamInvite {...demoInviteProps} />, {
title: 'Welcome to Action by Parabol',
previewText: 'Welcome to Action by Parabol'
const html = Oy.renderTemplate(<SummaryEmail />, {
title: 'Action Meeting Summary from Parabol',
previewText: 'Action Meeting Summary from Parabol'
});

res.set('Cache-Control', 'no-cache, no-store, must-revalidate');
Expand Down
5 changes: 3 additions & 2 deletions src/server/graphql/models/Action/actionMutation.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export default {
args: {
newAction: {
type: new GraphQLNonNull(CreateActionInput),
description: 'The new action including an id, status, and type, and teamMemberId'
description: 'The new action including an id, teamMemberId, [agendaId], and sortOrder '
}
},
async resolve(source, {newAction}, {authToken}) {
Expand All @@ -65,7 +65,8 @@ export default {
...newAction,
userId,
createdAt: now,
updatedAt: now
updatedAt: now,
isComplete: false
};
await r.table('Action').insert(action);
}
Expand Down
10 changes: 5 additions & 5 deletions src/server/graphql/models/Action/actionSchema.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,6 @@ export const Action = new GraphQLObjectType({
type: GraphQLBoolean,
description: 'Marks the item as checked off'
},
isActive: {
type: GraphQLBoolean,
description: 'Shows the item in subscriptions'
},
createdAt: {
type: GraphQLISO8601Type,
description: 'The timestamp the action was created'
Expand Down Expand Up @@ -58,8 +54,12 @@ const actionInputThunk = () => ({
agendaId: {
type: GraphQLID,
description: 'the agenda item that created this project, if any'
},
isComplete: {
type: GraphQLBoolean,
description: 'Marks the item as checked off'
}
});

export const CreateActionInput = nonnullifyInputThunk('CreateActionInput', actionInputThunk, ['id']);
export const CreateActionInput = nonnullifyInputThunk('CreateActionInput', actionInputThunk, ['id', 'teamMemberId']);
export const UpdateActionInput = nonnullifyInputThunk('UpdateActionInput', actionInputThunk, ['id']);
1 change: 1 addition & 0 deletions src/server/graphql/models/Action/actionSubscription.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export default {
const changefeedHandler = makeChangefeedHandler(socket, subbedChannelName);
r.table('Action')
.getAll(userId, {index: 'userId'})
.filter({isComplete: false})
.pluck(requestedFields)
.changes({includeInitial: true})
.run({cursor: true}, changefeedHandler);
Expand Down
Loading

0 comments on commit 681a5dd

Please sign in to comment.