diff --git a/.eslintrc b/.eslintrc index 48b0e6b8..4241933e 100644 --- a/.eslintrc +++ b/.eslintrc @@ -12,6 +12,34 @@ "classes": true }, "rules": { - - } + "react/no-multi-comp": 0, + "import/default": 0, + "import/no-duplicates": 0, + "import/named": 0, + "import/namespace": 0, + "import/no-unresolved": 0, + "import/no-named-as-default": 2, + "comma-dangle": 0, // not sure why airbnb turned this on. gross! + "indent": [2, 2, {"SwitchCase": 1}], + "no-console": 0, + "no-alert": 0, + "no-underscore-dangle": 0, + "max-len": [1, 120, 4], + }, + "plugins": [ + "react", "import" + ], + "settings": { + "import/parser": "babel-eslint", + "import/resolve": { + "moduleDirectory": ["node_modules"] + } + }, + "globals": { + "__DEVELOPMENT__": true, + "__CLIENT__": true, + "__SERVER__": true, + "__DISABLE_SSR__": true, + "__DEVTOOLS__": true + } } \ No newline at end of file diff --git a/client/components/Nav.js b/client/components/Nav.js new file mode 100644 index 00000000..b9ed95dc --- /dev/null +++ b/client/components/Nav.js @@ -0,0 +1,37 @@ +import React from 'react'; +import { Link } from 'react-router'; + +class Nav extends React.Component { + render() { + return ( + + ); + } +} + +export default Nav; diff --git a/client/components/Nav.jsx b/client/components/Nav.jsx deleted file mode 100644 index f529ea3b..00000000 --- a/client/components/Nav.jsx +++ /dev/null @@ -1,33 +0,0 @@ -import React from 'react' -import { Link } from 'react-router' - -class Nav extends React.Component { - render() { - return ( - - ); - } -} - -export default Nav; \ No newline at end of file diff --git a/client/constants.js b/client/constants.js index a1fe8529..2f5571d5 100644 --- a/client/constants.js +++ b/client/constants.js @@ -22,5 +22,5 @@ export const NEW_PROJECT = 'NEW_PROJECT'; export const SET_PROJECT = 'SET_PROJECT'; -//eventually, handle errors more specifically and better +// eventually, handle errors more specifically and better export const ERROR = 'ERROR'; diff --git a/client/modules/App/App.jsx b/client/modules/App/App.js similarity index 74% rename from client/modules/App/App.jsx rename to client/modules/App/App.js index 3228b629..937c7713 100644 --- a/client/modules/App/App.jsx +++ b/client/modules/App/App.js @@ -5,22 +5,21 @@ import DevTools from './components/DevTools'; class App extends React.Component { constructor(props, context) { super(props, context); - this.state = {isMounted: false}; + this.state = { isMounted: false }; } componentDidMount() { - this.setState({isMounted: true}); + this.setState({ isMounted: true }); } render() { - debugger; return (
{this.state.isMounted && !window.devToolsExtension && process.env.NODE_ENV === 'development' && } - { this.props.children } + {this.props.children}
); } } -export default connect()(App); \ No newline at end of file +export default connect()(App); diff --git a/client/modules/App/components/DevTools.jsx b/client/modules/App/components/DevTools.js similarity index 99% rename from client/modules/App/components/DevTools.jsx rename to client/modules/App/components/DevTools.js index 44cb9b1a..cda1802e 100644 --- a/client/modules/App/components/DevTools.jsx +++ b/client/modules/App/components/DevTools.js @@ -10,4 +10,4 @@ export default createDevTools( > -); \ No newline at end of file +); diff --git a/client/modules/IDE/actions/files.js b/client/modules/IDE/actions/files.js index 0b5684b5..40438b1e 100644 --- a/client/modules/IDE/actions/files.js +++ b/client/modules/IDE/actions/files.js @@ -1,9 +1,9 @@ import * as ActionTypes from '../../../constants'; export function updateFile(name, content) { - return { - type: ActionTypes.CHANGE_SELECTED_FILE, - name: name, - content: content - } -} \ No newline at end of file + return { + type: ActionTypes.CHANGE_SELECTED_FILE, + name, + content + }; +} diff --git a/client/modules/IDE/actions/ide.js b/client/modules/IDE/actions/ide.js index 63ed4a3c..1d40ed12 100644 --- a/client/modules/IDE/actions/ide.js +++ b/client/modules/IDE/actions/ide.js @@ -1,19 +1,19 @@ import * as ActionTypes from '../../../constants'; export function toggleSketch() { - return { - type: ActionTypes.TOGGLE_SKETCH - } + return { + type: ActionTypes.TOGGLE_SKETCH + }; } export function startSketch() { - return { - type: ActionTypes.START_SKETCH - } + return { + type: ActionTypes.START_SKETCH + }; } export function stopSketch() { - return { - type: ActionTypes.STOP_SKETCH - } -} \ No newline at end of file + return { + type: ActionTypes.STOP_SKETCH + }; +} diff --git a/client/modules/IDE/actions/preferences.js b/client/modules/IDE/actions/preferences.js index c825714f..2cfafcf4 100644 --- a/client/modules/IDE/actions/preferences.js +++ b/client/modules/IDE/actions/preferences.js @@ -1,25 +1,25 @@ import * as ActionTypes from '../../../constants'; export function openPreferences() { - return { - type: ActionTypes.OPEN_PREFERENCES - } + return { + type: ActionTypes.OPEN_PREFERENCES + }; } export function closePreferences() { - return { - type: ActionTypes.CLOSE_PREFERENCES - } + return { + type: ActionTypes.CLOSE_PREFERENCES + }; } export function increaseFont() { - return { - type: ActionTypes.INCREASE_FONTSIZE - } + return { + type: ActionTypes.INCREASE_FONTSIZE + }; } export function decreaseFont() { - return { - type: ActionTypes.DECREASE_FONTSIZE - } -} \ No newline at end of file + return { + type: ActionTypes.DECREASE_FONTSIZE + }; +} diff --git a/client/modules/IDE/actions/project.js b/client/modules/IDE/actions/project.js index 9c275e53..e656907d 100644 --- a/client/modules/IDE/actions/project.js +++ b/client/modules/IDE/actions/project.js @@ -1,89 +1,91 @@ import * as ActionTypes from '../../../constants'; -import { browserHistory } from 'react-router' -import axios from 'axios' +import { browserHistory } from 'react-router'; +import axios from 'axios'; const ROOT_URL = location.href.indexOf('localhost') > 0 ? 'http://localhost:8000/api' : '/api'; export function getProject(id) { - return function(dispatch) { - axios.get(`${ROOT_URL}/projects/${id}`, {withCredentials: true}) - .then(response => { - browserHistory.push(`/projects/${id}`); - dispatch({ - type: ActionTypes.SET_PROJECT_NAME, - project: response.data - }) - }) - .catch(response => dispatch({ - type: ActionTypes.ERROR - })); - } + return (dispatch) => { + axios.get(`${ROOT_URL}/projects/${id}`, { withCredentials: true }) + .then(response => { + browserHistory.push(`/projects/${id}`); + dispatch({ + type: ActionTypes.SET_PROJECT_NAME, + project: response.data + }); + }) + .catch(response => dispatch({ + type: ActionTypes.ERROR, + error: response.data + })); + }; } export function setProjectName(event) { - var name = event.target.textContent; - return { - type: ActionTypes.SET_PROJECT_NAME, - name: name - } + const name = event.target.textContent; + return { + type: ActionTypes.SET_PROJECT_NAME, + name + }; } export function saveProject() { - return function(dispatch, getState) { - var state = getState(); - var formParams = Object.assign({}, state.project); - formParams.file = state.file; - debugger; - if (state.id) { - axios.put(`${ROOT_URL}/projects/${state.id}`, formParams, {withCredentials: true}) - .then(response => { - dispatch({ - type: ActionTypes.PROJECT_SAVE_SUCCESS - }) - .catch(response => dispatch({ - type: ActionTypes.PROJECT_SAVE_FAIL - })); - }) - } - else { - axios.post(`${ROOT_URL}/projects`, formParams, {withCredentials: true}) - .then(response => { - browserHistory.push('/projects/' + response.data.id); - dispatch({ - type: ActionTypes.NEW_PROJECT, - name: response.data.name, - id: response.data.id, - file: { - name: response.data.file.name, - content: response.data.file.content - } - }); - }) - .catch(response => dispatch({ - type: ActionTypes.PROJECT_SAVE_FAIL - })); - } - } + return (dispatch, getState) => { + const state = getState(); + const formParams = Object.assign({}, state.project); + formParams.file = state.file; + if (state.id) { + axios.put(`${ROOT_URL}/projects/${state.id}`, formParams, { withCredentials: true }) + .then(() => { + dispatch({ + type: ActionTypes.PROJECT_SAVE_SUCCESS + }) + .catch((response) => dispatch({ + type: ActionTypes.PROJECT_SAVE_FAIL, + error: response.data + })); + }); + } else { + axios.post(`${ROOT_URL}/projects`, formParams, { withCredentials: true }) + .then(response => { + browserHistory.push(`/projects/${response.data.id}`); + dispatch({ + type: ActionTypes.NEW_PROJECT, + name: response.data.name, + id: response.data.id, + file: { + name: response.data.file.name, + content: response.data.file.content + } + }); + }) + .catch(response => dispatch({ + type: ActionTypes.PROJECT_SAVE_FAIL, + error: response.data + })); + } + }; } export function createProject() { - return function(dispatch) { - axios.post(`${ROOT_URL}/projects`, {}, {withCredentials: true}) - .then(response => { - browserHistory.push('/projects/' + response.data.id); - dispatch({ - type: ActionTypes.NEW_PROJECT, - name: response.data.name, - id: response.data.id, - file: { - name: response.data.file.name, - content: response.data.file.content - } - }); - }) - .catch(response => dispatch({ - type: ActionTypes.PROJECT_SAVE_FAIL - })); - } -} \ No newline at end of file + return (dispatch) => { + axios.post(`${ROOT_URL}/projects`, {}, { withCredentials: true }) + .then(response => { + browserHistory.push(`/projects/${response.data.id}`); + dispatch({ + type: ActionTypes.NEW_PROJECT, + name: response.data.name, + id: response.data.id, + file: { + name: response.data.file.name, + content: response.data.file.content + } + }); + }) + .catch(response => dispatch({ + type: ActionTypes.PROJECT_SAVE_FAIL, + error: response.data + })); + }; +} diff --git a/client/modules/IDE/components/Editor.js b/client/modules/IDE/components/Editor.js new file mode 100644 index 00000000..416d1c81 --- /dev/null +++ b/client/modules/IDE/components/Editor.js @@ -0,0 +1,43 @@ +import React from 'react'; +import CodeMirror from 'codemirror'; +import 'codemirror/mode/javascript/javascript'; +import 'codemirror/addon/selection/active-line'; + +class Editor extends React.Component { + + componentDidMount() { + this._cm = CodeMirror(this.refs.container, { // eslint-disable-line + theme: 'p5-widget', + value: this.props.content, + lineNumbers: true, + styleActiveLine: true, + mode: 'javascript' + }); + this._cm.on('change', () => { // eslint-disable-line + this.props.updateFile('sketch.js', this._cm.getValue()); + }); + this._cm.getWrapperElement().style['font-size'] = `${this.props.fontSize}px`; + } + + componentDidUpdate(prevProps) { + if (this.props.content !== prevProps.content && + this.props.content !== this._cm.getValue()) { + this._cm.setValue(this.props.content); // eslint-disable-line no-underscore-dangle + } + if (this.props.fontSize !== prevProps.fontSize) { + this._cm.getWrapperElement().style['font-size'] = `${this.props.fontSize}px`; + } + } + + componentWillUnmount() { + this._cm = null; + } + + _cm: CodeMirror.Editor + + render() { + return
; + } +} + +export default Editor; diff --git a/client/modules/IDE/components/Editor.jsx b/client/modules/IDE/components/Editor.jsx deleted file mode 100644 index 2d793b17..00000000 --- a/client/modules/IDE/components/Editor.jsx +++ /dev/null @@ -1,42 +0,0 @@ -import React from 'react'; -import CodeMirror from 'codemirror'; -import 'codemirror/mode/javascript/javascript'; -import 'codemirror/addon/selection/active-line' - -class Editor extends React.Component { - _cm: CodeMirror.Editor - - componentDidMount() { - this._cm = CodeMirror(this.refs.container, { - theme: 'p5-widget', - value: this.props.content, - lineNumbers: true, - styleActiveLine: true, - mode: 'javascript' - }); - this._cm.on('change', () => { - this.props.updateFile("sketch.js", this._cm.getValue()); - }); - this._cm.getWrapperElement().style['font-size'] = this.props.fontSize+'px'; - } - - componentDidUpdate(prevProps) { - if (this.props.content !== prevProps.content && - this.props.content !== this._cm.getValue()) { - this._cm.setValue(this.props.content); - } - if (this.props.fontSize !== prevProps.fontSize) { - this._cm.getWrapperElement().style['font-size'] = this.props.fontSize+'px'; - } - } - - componentWillUnmount() { - this._cm = null; - } - - render() { - return
; - } -} - -export default Editor; diff --git a/client/modules/IDE/components/Preferences.js b/client/modules/IDE/components/Preferences.js new file mode 100644 index 00000000..89e0bb89 --- /dev/null +++ b/client/modules/IDE/components/Preferences.js @@ -0,0 +1,38 @@ +import React from 'react'; + +const Isvg = require('react-inlinesvg'); +const exitUrl = require('../../../images/exit.svg'); +const plusUrl = require('../../../images/plus.svg'); +const minusUrl = require('../../../images/minus.svg'); +const classNames = require('classnames'); + +class Preferences extends React.Component { + render() { + const preferencesContainerClass = classNames({ + preferences: true, + 'preferences--selected': this.props.isVisible + }); + return ( +
+
+

Preferences

+ +
+
+

Text Size

+ +

{this.props.fontSize}

+ +
+
+ ); + } +} + +export default Preferences; diff --git a/client/modules/IDE/components/Preferences.jsx b/client/modules/IDE/components/Preferences.jsx deleted file mode 100644 index 9de15ebf..00000000 --- a/client/modules/IDE/components/Preferences.jsx +++ /dev/null @@ -1,38 +0,0 @@ -import React from 'react'; - -var Isvg = require('react-inlinesvg'); -var exitUrl = require('../../../images/exit.svg'); -var plusUrl = require('../../../images/plus.svg'); -var minusUrl = require('../../../images/minus.svg'); -var classNames = require('classnames'); - -class Preferences extends React.Component { - render() { - let preferencesContainerClass = classNames({ - "preferences": true, - "preferences--selected": this.props.isVisible - }); - return ( -
-
-

Preferences

- -
-
-

Text Size

- -

{this.props.fontSize}

- -
-
- ); - } -} - -export default Preferences; diff --git a/client/modules/IDE/components/Preview.js b/client/modules/IDE/components/Preview.js new file mode 100644 index 00000000..aea0132f --- /dev/null +++ b/client/modules/IDE/components/Preview.js @@ -0,0 +1,20 @@ +import React from 'react'; + +class Preview extends React.Component { + componentDidMount() { + + } + + render() { + return ( +
+ + +
+ ); + } +} + +export default Preview; diff --git a/client/modules/IDE/components/Preview.jsx b/client/modules/IDE/components/Preview.jsx deleted file mode 100644 index 12f98e0b..00000000 --- a/client/modules/IDE/components/Preview.jsx +++ /dev/null @@ -1,21 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; - -class Preview extends React.Component { - componentDidMount() { - console.log(ReactDOM.findDOMNode(this)); - } - - render() { - return ( -
- - -
- ); - } -} - -export default Preview; \ No newline at end of file diff --git a/client/modules/IDE/components/PreviewFrame.js b/client/modules/IDE/components/PreviewFrame.js new file mode 100644 index 00000000..3e0ddc0f --- /dev/null +++ b/client/modules/IDE/components/PreviewFrame.js @@ -0,0 +1,67 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; + +class PreviewFrame extends React.Component { + + componentDidMount() { + if (this.props.isPlaying) { + this.renderFrameContents(); + } + } + + clearPreview() { + const doc = ReactDOM.findDOMNode(this).contentDocument; + doc.write(''); + doc.close(); + } + + renderFrameContents() { + const doc = ReactDOM.findDOMNode(this).contentDocument; + if (doc.readyState === 'complete') { + renderSketch(); + } else { + setTimeout(this.renderFrameContents, 0); + } + } + + renderSketch() { + const doc = ReactDOM.findDOMNode(this).contentDocument; + this.clearPreview(); + ReactDOM.render(this.props.head, doc.head); + const p5Script = doc.createElement('script'); + p5Script.setAttribute('src', 'https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.0/p5.min.js'); + doc.body.appendChild(p5Script); + + const sketchScript = doc.createElement('script'); + sketchScript.textContent = this.props.content; + doc.body.appendChild(sketchScript); + } + + componentDidUpdate(prevProps, prevState) { + if (this.props.isPlaying !== prevProps.isPlaying) { + if (this.props.isPlaying) { + this.renderSketch(); + } else { + this.clearPreview(); + } + } + + if (this.props.isPlaying && this.props.content !== prevProps.content) { + this.renderSketch(); + } + } + + componentWillUnmount() { + ReactDOM.unmountComponentAtNode(ReactDOM.findDOMNode(this).contentDocument.body); + } + + render() { + return ; + } +} + +export default PreviewFrame; diff --git a/client/modules/IDE/components/PreviewFrame.jsx b/client/modules/IDE/components/PreviewFrame.jsx deleted file mode 100644 index 0137f400..00000000 --- a/client/modules/IDE/components/PreviewFrame.jsx +++ /dev/null @@ -1,64 +0,0 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; - -class PreviewFrame extends React.Component { - - componentDidMount() { - if (this.props.isPlaying) { - this.renderFrameContents(); - } - } - - renderFrameContents() { - let doc = ReactDOM.findDOMNode(this).contentDocument; - if(doc.readyState === 'complete') { - renderSketch(); - } else { - setTimeout(this.renderFrameContents, 0); - } - } - - renderSketch() { - let doc = ReactDOM.findDOMNode(this).contentDocument; - this.clearPreview(); - ReactDOM.render(this.props.head, doc.head); - let p5Script = doc.createElement('script'); - p5Script.setAttribute('src', 'https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.0/p5.min.js'); - doc.body.appendChild(p5Script); - - let sketchScript = doc.createElement('script'); - sketchScript.textContent = this.props.content; - doc.body.appendChild(sketchScript); - } - - clearPreview() { - let doc = ReactDOM.findDOMNode(this).contentDocument; - doc.write(''); - doc.close(); - } - - componentDidUpdate(prevProps, prevState) { - if (this.props.isPlaying != prevProps.isPlaying) { - if (this.props.isPlaying) { - this.renderSketch(); - } - else { - this.clearPreview(); - } - } - - if (this.props.isPlaying && this.props.content != prevProps.content) { - this.renderSketch(); - } - } - - componentWillUnmount() { - ReactDOM.unmountComponentAtNode(ReactDOM.findDOMNode(this).contentDocument.body); - } - - render() { - return ; - } -} - -export default PreviewFrame; diff --git a/client/modules/IDE/components/Toolbar.js b/client/modules/IDE/components/Toolbar.js new file mode 100644 index 00000000..dbc1ef93 --- /dev/null +++ b/client/modules/IDE/components/Toolbar.js @@ -0,0 +1,52 @@ +import React from 'react'; + +const Isvg = require('react-inlinesvg'); +const playUrl = require('../../../images/play.svg'); +const logoUrl = require('../../../images/p5js-logo.svg'); +const stopUrl = require('../../../images/stop.svg'); +const preferencesUrl = require('../../../images/preferences.svg'); +const classNames = require('classnames'); + +class Toolbar extends React.Component { + render() { + let playButtonClass = classNames({ + 'toolbar__play-button': true, + 'toolbar__play-button--selected': this.props.isPlaying + }); + let stopButtonClass = classNames({ + 'toolbar__stop-button': true, + 'toolbar__stop-button--selected': !this.props.isPlaying + }); + let preferencesButtonClass = classNames({ + 'toolbar__preferences-button': true, + 'toolbar__preferences-button--selected': this.props.isPreferencesVisible + }); + + return ( +
+ p5js Logo + + +
+ + {this.props.projectName} + +
+ +
+ ); + } +} + +export default Toolbar; diff --git a/client/modules/IDE/components/Toolbar.jsx b/client/modules/IDE/components/Toolbar.jsx deleted file mode 100644 index 7a4db35f..00000000 --- a/client/modules/IDE/components/Toolbar.jsx +++ /dev/null @@ -1,50 +0,0 @@ -import React from 'react'; - -var Isvg = require('react-inlinesvg'); -var playUrl = require('../../../images/play.svg'); -var logoUrl = require('../../../images/p5js-logo.svg'); -var stopUrl = require('../../../images/stop.svg'); -var preferencesUrl = require('../../../images/preferences.svg'); -var classNames = require('classnames'); - -class Toolbar extends React.Component { - render() { - let playButtonClass = classNames({ - "toolbar__play-button": true, - "toolbar__play-button--selected": this.props.isPlaying - }); - let stopButtonClass = classNames({ - "toolbar__stop-button": true, - "toolbar__stop-button--selected": !this.props.isPlaying - }); - let preferencesButtonClass = classNames({ - "toolbar__preferences-button": true, - "toolbar__preferences-button--selected": this.props.isPreferencesVisible - }); - - return ( -
- p5js Logo - - -
- - {this.props.projectName} - -
- -
- ); - } -} - -export default Toolbar; diff --git a/client/modules/IDE/pages/IDEView.js b/client/modules/IDE/pages/IDEView.js new file mode 100644 index 00000000..f97e287e --- /dev/null +++ b/client/modules/IDE/pages/IDEView.js @@ -0,0 +1,83 @@ +import React from 'react'; +import Editor from '../components/Editor'; +import PreviewFrame from '../components/PreviewFrame'; +import Toolbar from '../components/Toolbar'; +import Preferences from '../components/Preferences'; +import Nav from '../../../components/Nav'; +import { bindActionCreators } from 'redux'; +import { connect } from 'react-redux'; +import * as FileActions from '../actions/files'; +import * as IDEActions from '../actions/ide'; +import * as PreferencesActions from '../actions/preferences'; +import * as ProjectActions from '../actions/project'; + +class IDEView extends React.Component { + componentDidMount() { + if (this.props.params.project_id) { + const id = this.props.params.project_id; + this.props.getProject(id); + } + } + + render() { + return ( +
+
+ ); + } +} + +function mapStateToProps(state) { + return { + file: state.file, + ide: state.ide, + preferences: state.preferences, + user: state.user, + project: state.project + }; +} + +function mapDispatchToProps(dispatch) { + return bindActionCreators(Object.assign({}, + FileActions, + ProjectActions, + IDEActions, + PreferencesActions), + dispatch); +} + +export default connect(mapStateToProps, mapDispatchToProps)(IDEView); diff --git a/client/modules/IDE/pages/IDEView.jsx b/client/modules/IDE/pages/IDEView.jsx deleted file mode 100644 index b71d2c2d..00000000 --- a/client/modules/IDE/pages/IDEView.jsx +++ /dev/null @@ -1,72 +0,0 @@ -import React from 'react' -import Editor from '../components/Editor' -import PreviewFrame from '../components/PreviewFrame' -import Toolbar from '../components/Toolbar' -import Preferences from '../components/Preferences' -import Nav from '../../../components/Nav' -import { bindActionCreators } from 'redux' -import { connect } from 'react-redux' -import * as FileActions from '../actions/files' -import * as IDEActions from '../actions/ide' -import * as PreferencesActions from '../actions/preferences' -import * as ProjectActions from '../actions/project' - -class IDEView extends React.Component { - componentDidMount() { - if (this.props.params.project_id) { - const id = this.props.params.project_id - this.props.getProject(id); - } - } - - render() { - return ( -
-
- ) - } -} - -function mapStateToProps(state) { - return { - file: state.file, - ide: state.ide, - preferences: state.preferences, - user: state.user, - project: state.project - } -} - -function mapDispatchToProps(dispatch) { - return bindActionCreators(Object.assign({}, FileActions, ProjectActions, IDEActions, PreferencesActions), dispatch); -} - -export default connect(mapStateToProps, mapDispatchToProps)(IDEView); diff --git a/client/modules/IDE/reducers/files.js b/client/modules/IDE/reducers/files.js index d780961d..3c43f097 100644 --- a/client/modules/IDE/reducers/files.js +++ b/client/modules/IDE/reducers/files.js @@ -1,45 +1,45 @@ import * as ActionTypes from '../../../constants'; const initialState = { - name: "sketch.js", - content: `function setup() { - createCanvas(400, 400); + name: 'sketch.js', + content: `function setup() { + createCanvas(400, 400); } function draw() { - background(220); + background(220); }` -} +}; const file = (state = initialState, action) => { - switch (action.type) { - case ActionTypes.CHANGE_SELECTED_FILE: - return { - name: action.name, - content: action.content - } - case ActionTypes.NEW_PROJECT: - return { - name: action.file.name, - content: action.file.conent - } - default: - return state - } -} + switch (action.type) { + case ActionTypes.CHANGE_SELECTED_FILE: + return { + name: action.name, + content: action.content + }; + case ActionTypes.NEW_PROJECT: + return { + name: action.file.name, + content: action.file.conent + }; + default: + return state; + } +}; export default file; -//i'll add this in when there are multiple files +// i'll add this in when there are multiple files // const files = (state = [], action) => { -// switch (action.type) { -// case ActionTypes.CHANGE_SELECTED_FILE: -// //find the file with the name -// //update it -// //put in into the new array of files -// default: -// return state -// } +// switch (action.type) { +// case ActionTypes.CHANGE_SELECTED_FILE: +// //find the file with the name +// //update it +// //put in into the new array of files +// default: +// return state +// } // } -// export default files \ No newline at end of file +// export default files diff --git a/client/modules/IDE/reducers/ide.js b/client/modules/IDE/reducers/ide.js index e6df1422..e89ffdee 100644 --- a/client/modules/IDE/reducers/ide.js +++ b/client/modules/IDE/reducers/ide.js @@ -1,26 +1,26 @@ import * as ActionTypes from '../../../constants'; const initialState = { - isPlaying: false -} + isPlaying: false +}; const ide = (state = initialState, action) => { - switch (action.type) { - case ActionTypes.TOGGLE_SKETCH: - return { - isPlaying: !state.isPlaying - } - case ActionTypes.START_SKETCH: - return { - isPlaying: true - } - case ActionTypes.STOP_SKETCH: - return { - isPlaying: false - } - default: - return state - } -} + switch (action.type) { + case ActionTypes.TOGGLE_SKETCH: + return { + isPlaying: !state.isPlaying + }; + case ActionTypes.START_SKETCH: + return { + isPlaying: true + }; + case ActionTypes.STOP_SKETCH: + return { + isPlaying: false + }; + default: + return state; + } +}; -export default ide; \ No newline at end of file +export default ide; diff --git a/client/modules/IDE/reducers/preferences.js b/client/modules/IDE/reducers/preferences.js index 910accd8..9735251a 100644 --- a/client/modules/IDE/reducers/preferences.js +++ b/client/modules/IDE/reducers/preferences.js @@ -1,35 +1,35 @@ import * as ActionTypes from '../../../constants'; const initialState = { - isVisible: false, - fontSize: 18 -} + isVisible: false, + fontSize: 18 +}; const preferences = (state = initialState, action) => { - switch (action.type) { - case ActionTypes.OPEN_PREFERENCES: - return { - isVisible: true, - fontSize: state.fontSize - } - case ActionTypes.CLOSE_PREFERENCES: - return { - isVisible: false, - fontSize: state.fontSize - } - case ActionTypes.INCREASE_FONTSIZE: - return { - isVisible: state.isVisible, - fontSize: state.fontSize+2 - } - case ActionTypes.DECREASE_FONTSIZE: - return { - isVisible: state.isVisible, - fontSize: state.fontSize-2 - } - default: - return state - } -} + switch (action.type) { + case ActionTypes.OPEN_PREFERENCES: + return { + isVisible: true, + fontSize: state.fontSize + }; + case ActionTypes.CLOSE_PREFERENCES: + return { + isVisible: false, + fontSize: state.fontSize + }; + case ActionTypes.INCREASE_FONTSIZE: + return { + isVisible: state.isVisible, + fontSize: state.fontSize + 2 + }; + case ActionTypes.DECREASE_FONTSIZE: + return { + isVisible: state.isVisible, + fontSize: state.fontSize - 2 + }; + default: + return state; + } +}; -export default preferences; \ No newline at end of file +export default preferences; diff --git a/client/modules/IDE/reducers/project.js b/client/modules/IDE/reducers/project.js index d58f24ed..6d3a6b83 100644 --- a/client/modules/IDE/reducers/project.js +++ b/client/modules/IDE/reducers/project.js @@ -1,32 +1,32 @@ import * as ActionTypes from '../../../constants'; const initialState = { - name: "Hello p5.js" -} + name: 'Hello p5.js' +}; const project = (state = initialState, action) => { - switch (action.type) { - case ActionTypes.SET_PROJECT_NAME: - return { - name: action.name - } - case ActionTypes.NEW_PROJECT: - return { - id: action.id, - name: action.name - } - case ActionTypes.SET_PROJECT: - return { - id: action.project.id, - name: action.project.name, - file: { - name: action.project.file.name, - content: action.project.file.content - } - } - default: - return state; - } -} + switch (action.type) { + case ActionTypes.SET_PROJECT_NAME: + return { + name: action.name + }; + case ActionTypes.NEW_PROJECT: + return { + id: action.id, + name: action.name + }; + case ActionTypes.SET_PROJECT: + return { + id: action.project.id, + name: action.project.name, + file: { + name: action.project.file.name, + content: action.project.file.content + } + }; + default: + return state; + } +}; -export default project; \ No newline at end of file +export default project; diff --git a/client/modules/User/actions.js b/client/modules/User/actions.js index 747c087b..1b4ca040 100644 --- a/client/modules/User/actions.js +++ b/client/modules/User/actions.js @@ -1,51 +1,52 @@ -import * as ActionTypes from '../../constants' -import { browserHistory } from 'react-router' -import axios from 'axios' +import * as ActionTypes from '../../constants'; +import { browserHistory } from 'react-router'; +import axios from 'axios'; const ROOT_URL = location.href.indexOf('localhost') > 0 ? 'http://localhost:8000/api' : '/api'; -export function signUpUser(formValues) { - return function(dispatch) { - axios.post(`${ROOT_URL}/signup`, formValues, {withCredentials: true}) - .then(response => { - dispatch({ type: ActionTypes.AUTH_USER, - user: response.data - }); - browserHistory.push('/'); - }) - .catch(response => dispatch(authError(response.data.error))); - } -} - -export function loginUser(formValues) { - return function(dispatch) { - axios.post(`${ROOT_URL}/login`, formValues, {withCredentials: true}) - .then(response => { - dispatch({ type: ActionTypes.AUTH_USER, - user: response.data - }); - browserHistory.push('/'); - }) - .catch(response => dispatch(authError(response.data.error))); - } -} - -export function getUser() { - return function(dispatch) { - axios.get(`${ROOT_URL}/session`, {withCredentials: true}) - .then(response => { - dispatch({type: ActionTypes.AUTH_USER, - user: response.data - }); - }) - .catch(response => dispatch(authError(response.data.error))); - } -} - export function authError(error) { return { type: ActionTypes.AUTH_ERROR, payload: error }; -} \ No newline at end of file +} + +export function signUpUser(formValues) { + return (dispatch) => { + axios.post(`${ROOT_URL}/signup`, formValues, { withCredentials: true }) + .then(response => { + dispatch({ type: ActionTypes.AUTH_USER, + user: response.data + }); + browserHistory.push('/'); + }) + .catch(response => dispatch(authError(response.data.error))); + }; +} + +export function loginUser(formValues) { + return (dispatch) => { + axios.post(`${ROOT_URL}/login`, formValues, { withCredentials: true }) + .then(response => { + dispatch({ type: ActionTypes.AUTH_USER, + user: response.data + }); + browserHistory.push('/'); + }) + .catch(response => dispatch(authError(response.data.error))); + }; +} + +export function getUser() { + return (dispatch) => { + axios.get(`${ROOT_URL}/session`, { withCredentials: true }) + .then(response => { + dispatch({ + type: ActionTypes.AUTH_USER, + user: response.data + }); + }) + .catch(response => dispatch(authError(response.data.error))); + }; +} diff --git a/client/modules/User/components/LoginForm.js b/client/modules/User/components/LoginForm.js new file mode 100644 index 00000000..7e400d94 --- /dev/null +++ b/client/modules/User/components/LoginForm.js @@ -0,0 +1,34 @@ +import React from 'react'; + +class LoginForm extends React.Component { + render() { + const { fields: { email, password }, handleSubmit } = this.props; + return ( +
+

+ + +

+

+ + +

+ +
+ ); + } +} + +export default LoginForm; diff --git a/client/modules/User/components/LoginForm.jsx b/client/modules/User/components/LoginForm.jsx deleted file mode 100644 index f05a3ab9..00000000 --- a/client/modules/User/components/LoginForm.jsx +++ /dev/null @@ -1,22 +0,0 @@ -import React from 'react' - -class LoginForm extends React.Component { - render() { - const {fields: {email, password}, handleSubmit } = this.props; - return ( -
-

- - -

-

- - -

- -
- ) - } -} - -export default LoginForm; \ No newline at end of file diff --git a/client/modules/User/components/SignupForm.js b/client/modules/User/components/SignupForm.js new file mode 100644 index 00000000..53c4534a --- /dev/null +++ b/client/modules/User/components/SignupForm.js @@ -0,0 +1,58 @@ +import React from 'react'; + +class SignupForm extends React.Component { + render() { + const { fields: { username, email, password, confirmPassword }, handleSubmit } = this.props; + return ( +
+

+ + +

+

+ + +

+

+ + +

+

+ + +

+ +
+ ); + } +} + +export default SignupForm; diff --git a/client/modules/User/components/SignupForm.jsx b/client/modules/User/components/SignupForm.jsx deleted file mode 100644 index 8868d58d..00000000 --- a/client/modules/User/components/SignupForm.jsx +++ /dev/null @@ -1,30 +0,0 @@ -import React from 'react' - -class SignupForm extends React.Component { - render() { - const {fields: { username, email, password, confirmPassword }, handleSubmit} = this.props; - return ( -
-

- - -

-

- - -

-

- - -

-

- - -

- -
- ) - } -} - -export default SignupForm; \ No newline at end of file diff --git a/client/modules/User/pages/LoginView.js b/client/modules/User/pages/LoginView.js new file mode 100644 index 00000000..4900ed2a --- /dev/null +++ b/client/modules/User/pages/LoginView.js @@ -0,0 +1,37 @@ +import React from 'react'; +import { bindActionCreators } from 'redux'; +import { reduxForm } from 'redux-form'; +import * as UserActions from '../actions'; +import LoginForm from '../components/LoginForm'; + +class LoginView extends React.Component { + render() { + return ( +
+

Login

+ +
+ ); + } +} + +function mapStateToProps(state) { + return { + user: state.user + }; +} + +function mapDispatchToProps(dispatch) { + return bindActionCreators(UserActions, dispatch); +} + +function validate(formProps) { + const errors = {}; + return errors; +} + +export default reduxForm({ + form: 'login', + fields: ['email', 'password'], + validate +}, mapStateToProps, mapDispatchToProps)(LoginView); diff --git a/client/modules/User/pages/LoginView.jsx b/client/modules/User/pages/LoginView.jsx deleted file mode 100644 index fe645397..00000000 --- a/client/modules/User/pages/LoginView.jsx +++ /dev/null @@ -1,37 +0,0 @@ -import React from 'react' -import { bindActionCreators } from 'redux' -import {reduxForm} from 'redux-form' -import * as UserActions from '../actions' -import LoginForm from '../components/LoginForm' - -class LoginView extends React.Component { - render() { - return ( -
-

Login

- -
- ) - } -} - -function mapStateToProps(state) { - return { - user: state.user - } -} - -function mapDispatchToProps(dispatch) { - return bindActionCreators(UserActions, dispatch); -} - -function validate(formProps) { - const errors = {}; - return errors; -} - -export default reduxForm({ - form: 'login', - fields: ['email', 'password'], - validate -}, mapStateToProps, mapDispatchToProps)(LoginView); \ No newline at end of file diff --git a/client/modules/User/pages/SignupView.js b/client/modules/User/pages/SignupView.js new file mode 100644 index 00000000..48280be7 --- /dev/null +++ b/client/modules/User/pages/SignupView.js @@ -0,0 +1,38 @@ +import React from 'react'; +import { bindActionCreators } from 'redux'; +import * as UserActions from '../actions'; +import { reduxForm } from 'redux-form'; +import SignupForm from '../components/SignupForm'; + +class SignupView extends React.Component { + render() { + return ( +
+

Sign Up

+ +
+ ); + } +} + +function mapStateToProps(state) { + return { + user: state.user + }; +} + +function mapDispatchToProps(dispatch) { + return bindActionCreators(UserActions, dispatch); +} + +function validate(formProps) { + const errors = {}; + return errors; +} + +// export default connect(mapStateToProps, mapDispatchToProps)(SignupView); +export default reduxForm({ + form: 'signup', + fields: ['username', 'email', 'password', 'passwordConfirm'], + validate +}, mapStateToProps, mapDispatchToProps)(SignupView); diff --git a/client/modules/User/pages/SignupView.jsx b/client/modules/User/pages/SignupView.jsx deleted file mode 100644 index 7af19073..00000000 --- a/client/modules/User/pages/SignupView.jsx +++ /dev/null @@ -1,39 +0,0 @@ -import React from 'react' -import { bindActionCreators } from 'redux' -import { connect } from 'react-redux' -import * as UserActions from '../actions' -import { reduxForm } from 'redux-form' -import SignupForm from '../components/SignupForm' - -class SignupView extends React.Component { - render() { - return ( -
-

Sign Up

- -
- ) - } -} - -function mapStateToProps(state) { - return { - user: state.user - } -} - -function mapDispatchToProps(dispatch) { - return bindActionCreators(UserActions, dispatch); -} - -function validate(formProps) { - const errors = {}; - return errors; -} - -// export default connect(mapStateToProps, mapDispatchToProps)(SignupView); -export default reduxForm({ - form: 'signup', - fields: ['username', 'email', 'password', 'passwordConfirm'], - validate -}, mapStateToProps, mapDispatchToProps)(SignupView); diff --git a/client/modules/User/reducers.js b/client/modules/User/reducers.js index fa2ae8d2..db3dac3e 100644 --- a/client/modules/User/reducers.js +++ b/client/modules/User/reducers.js @@ -1,17 +1,17 @@ -import * as ActionTypes from '../../constants' +import * as ActionTypes from '../../constants'; -const user = (state = {authenticated: false}, action) => { - switch (action.type) { - case ActionTypes.AUTH_USER: - return { ...action.user, - authenticated: true }; - case ActionTypes.AUTH_ERROR: - return { - authenticated: false - } - default: - return state; - } -} +const user = (state = { authenticated: false }, action) => { + switch (action.type) { + case ActionTypes.AUTH_USER: + return { ...action.user, + authenticated: true }; + case ActionTypes.AUTH_ERROR: + return { + authenticated: false + }; + default: + return state; + } +}; -export default user; \ No newline at end of file +export default user; diff --git a/client/reducers.js b/client/reducers.js index 6e7e657f..839e4d75 100644 --- a/client/reducers.js +++ b/client/reducers.js @@ -1,18 +1,18 @@ -import { combineReducers } from 'redux' -import file from './modules/IDE/reducers/files' -import ide from './modules/IDE/reducers/ide' -import preferences from './modules/IDE/reducers/preferences' -import project from './modules/IDE/reducers/project' -import user from './modules/User/reducers' -import { reducer as form } from 'redux-form' +import { combineReducers } from 'redux'; +import file from './modules/IDE/reducers/files'; +import ide from './modules/IDE/reducers/ide'; +import preferences from './modules/IDE/reducers/preferences'; +import project from './modules/IDE/reducers/project'; +import user from './modules/User/reducers'; +import { reducer as form } from 'redux-form'; const rootReducer = combineReducers({ - form, - ide, - file, - preferences, - user, - project -}) + form, + ide, + file, + preferences, + user, + project +}); -export default rootReducer \ No newline at end of file +export default rootReducer; diff --git a/client/routes.js b/client/routes.js index a47e82e0..989e0ffc 100644 --- a/client/routes.js +++ b/client/routes.js @@ -1,24 +1,24 @@ -import { Route, IndexRoute } from 'react-router' -import React from 'react' -import App from './modules/App/App' -import IDEView from './modules/IDE/pages/IDEView' -import LoginView from './modules/User/pages/LoginView' -import SignupView from './modules/User/pages/SignupView' -import { getUser } from './modules/User/actions' - -const routes = (store) => { - return ( - - - - - - - ); -} +import { Route, IndexRoute } from 'react-router'; +import React from 'react'; +import App from './modules/App/App'; +import IDEView from './modules/IDE/pages/IDEView'; +import LoginView from './modules/User/pages/LoginView'; +import SignupView from './modules/User/pages/SignupView'; +import { getUser } from './modules/User/actions'; const checkAuth = (store) => { - store.dispatch(getUser()); -} + store.dispatch(getUser()); +}; -export default routes; \ No newline at end of file +const routes = (store) => { + return ( + + + + + + + ); +}; + +export default routes; diff --git a/client/store.js b/client/store.js index b08c16c0..023197c9 100644 --- a/client/store.js +++ b/client/store.js @@ -1,10 +1,9 @@ -import { createStore, applyMiddleware, compose } from 'redux' -import thunk from 'redux-thunk' -import DevTools from './modules/App/components/DevTools' -import rootReducer from './reducers' +import { createStore, applyMiddleware, compose } from 'redux'; +import thunk from 'redux-thunk'; +import DevTools from './modules/App/components/DevTools'; +import rootReducer from './reducers'; export default function configureStore(initialState) { - const enhancers = [ applyMiddleware(thunk), ]; @@ -18,15 +17,16 @@ export default function configureStore(initialState) { rootReducer, initialState, compose(...enhancers) - ) + ); if (module.hot) { // Enable Webpack hot module replacement for reducers module.hot.accept('./reducers', () => { - const nextRootReducer = require('./reducers').default - store.replaceReducer(nextRootReducer) - }) + const nextRootReducer = require('./reducers').default; // eslint-disable-line global-require + store.replaceReducer(nextRootReducer); + }); } - return store -} \ No newline at end of file + return store; +} + diff --git a/package.json b/package.json index a8e31273..8b4f8327 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "license": "MIT", "repository": { "type": "git", - "url": "https://github.com/catarak/p5.js-web-editor" + "url": "git+https://github.com/catarak/p5.js-web-editor.git" }, "devDependencies": { "babel-eslint": "^6.1.0", @@ -56,6 +56,7 @@ "connect-mongo": "^1.2.0", "cookie-parser": "^1.4.1", "dotenv": "^2.0.0", + "eslint-loader": "^1.3.0", "express": "^4.13.4", "express-session": "^1.13.0", "mongoose": "^4.4.16", diff --git a/server/config.js b/server/config.js index 7f40eb8e..cf7fb8e8 100644 --- a/server/config.js +++ b/server/config.js @@ -3,4 +3,4 @@ const config = { port: process.env.PORT || 8000, }; -export default config; \ No newline at end of file +export default config; diff --git a/server/config/passport.js b/server/config/passport.js index 0b78b96c..65cb2b94 100644 --- a/server/config/passport.js +++ b/server/config/passport.js @@ -1,8 +1,8 @@ const passport = require('passport'); -const GitHubStrategy = require('passport-github').Strategy; +// const GitHubStrategy = require('passport-github').Strategy; const LocalStrategy = require('passport-local').Strategy; -import User from '../models/user' +import User from '../models/user'; passport.serializeUser((user, done) => { done(null, user.id); @@ -18,23 +18,27 @@ passport.deserializeUser((id, done) => { * Sign in using Email and Password. */ passport.use(new LocalStrategy({ usernameField: 'email' }, (email, password, done) => { - User.findOne({ email: email.toLowerCase() }, (err, user) => { - if (!user) { - return done(null, false, { msg: `Email ${email} not found.` }); - } - user.comparePassword(password, (err, isMatch) => { - if (isMatch) { - return done(null, user); + User.findOne({ email: email.toLowerCase() }, + (err, user) => { // eslint-disable-line consistent-return + if (!user) { + return done(null, false, { msg: `Email ${email} not found.` }); } - return done(null, false, { msg: 'Invalid email or password.' }); + user.comparePassword(password, (innerErr, isMatch) => { + if (innerErr) { + return done(innerErr); + } + if (isMatch) { + return done(null, user); + } + return done(null, false, { msg: 'Invalid email or password.' }); + }); }); - }); })); /** * Sign in with GitHub. */ -//TODO add dotenv so I can add github login +// TODO add dotenv so I can add github login // passport.use(new GitHubStrategy({ // clientID: process.env.GITHUB_ID, // clientSecret: process.env.GITHUB_SECRET, @@ -44,7 +48,8 @@ passport.use(new LocalStrategy({ usernameField: 'email' }, (email, password, don // if (req.user) { // User.findOne({ github: profile.id }, (err, existingUser) => { // if (existingUser) { -// req.flash('errors', { msg: 'There is already a GitHub account that belongs to you. Sign in with that account or delete it, then link it with your current account.' }); +// req.flash('errors', { msg: 'There is already a GitHub account that belongs to you. ' +// + 'Sign in with that account or delete it, then link it with your current account.' }); // done(err); // } else { // User.findById(req.user.id, (err, user) => { @@ -68,7 +73,8 @@ passport.use(new LocalStrategy({ usernameField: 'email' }, (email, password, don // } // User.findOne({ email: profile._json.email }, (err, existingEmailUser) => { // if (existingEmailUser) { -// req.flash('errors', { msg: 'There is already an account using this email address. Sign in to that account and link it with GitHub manually from Account Settings.' }); +// req.flash('errors', { msg: 'There is already an account using this email address. Sign' +// + ' in to that account and link it with GitHub manually from Account Settings.' }); // done(err); // } else { // const user = new User(); diff --git a/server/controllers/project.controller.js b/server/controllers/project.controller.js index ddadac04..30c06d93 100644 --- a/server/controllers/project.controller.js +++ b/server/controllers/project.controller.js @@ -1,56 +1,56 @@ -import Project from '../models/project' +import Project from '../models/project'; export function createProject(req, res) { - let projectValues = { - user: req.user ? req.user._id : undefined, - file: {} - } - - Object.assign(projectValues, req.body); + const projectValues = { + user: req.user ? req.user._id : undefined, // eslint-disable-line no-underscore-dangle + file: {} + }; - Project.create(projectValues, function(err, newProject) { - if (err) { return res.json({success: false}); } - return res.json({ - id: newProject._id, - name: newProject.name, - file: { - name: newProject.file.name, - content: newProject.file.content - } - }); - }); + Object.assign(projectValues, req.body); + + Project.create(projectValues, (err, newProject) => { + if (err) { return res.json({ success: false }); } + return res.json({ + id: newProject._id, // eslint-disable-line no-underscore-dangle + name: newProject.name, + file: { + name: newProject.file.name, + content: newProject.file.content + } + }); + }); } export function updateProject(req, res) { - Project.update({_id: req.params.project_id}, - { - $set: req.body - }, function(err, updatedProject) { - if (err) { return res.json({success: false}) } - return res.json({ - id: updatedProject._id, - name: updatedProject.name, - file: { - name: updatedProject.file.name, - content: updatedProject.file.content - } - }); - }); + Project.update({ _id: req.params.project_id }, + { + $set: req.body + }, (err, updatedProject) => { + if (err) { return res.json({ success: false }); } + return res.json({ + id: updatedProject._id, // eslint-disable-line no-underscore-dangle + name: updatedProject.name, + file: { + name: updatedProject.file.name, + content: updatedProject.file.content + } + }); + }); } export function getProject(req, res) { - Project.findById(req.params.project_id, function(err, project) { - if (err) { - return res.status(404).send({message: 'Project with that id does not exist'}); - } + Project.findById(req.params.project_id, (err, project) => { + if (err) { + return res.status(404).send({ message: 'Project with that id does not exist' }); + } - return res.json({ - id: project._id, - name: project.name, - file: { - name: project.file.name, - content: project.file.conent - } - }); - }) -} \ No newline at end of file + return res.json({ + id: project._id, // eslint-disable-line no-underscore-dangle + name: project.name, + file: { + name: project.file.name, + content: project.file.conent + } + }); + }); +} diff --git a/server/controllers/session.controller.js b/server/controllers/session.controller.js index d9096434..f9585c0c 100644 --- a/server/controllers/session.controller.js +++ b/server/controllers/session.controller.js @@ -1,35 +1,28 @@ -import User from '../models/user' -import passport from 'passport' -import path from 'path' - - -export function destroySession(req, res) { - -} +import passport from 'passport'; export function createSession(req, res, next) { - passport.authenticate('local', (err, user, info) => { - if (err) { return next(err); } - if (!user) { - return res.status(401).send({ error: 'Invalid username or password' }); - } + passport.authenticate('local', (err, user) => { // eslint-disable-line consistent-return + if (err) { return next(err); } + if (!user) { + return res.status(401).send({ error: 'Invalid username or password' }); + } - req.logIn(user, (err) => { - if (err) { return next(err); } - res.json({ - email: req.user.email, - username: req.user.username - }); - }); - })(req, res, next); + req.logIn(user, (innerErr) => { + if (innerErr) { return next(innerErr); } + return res.json({ + email: req.user.email, + username: req.user.username + }); + }); + })(req, res, next); } -export function getSession(req, res, next) { - if (req.user) { - return res.json({ - email: req.user.email, - username: req.user.username - }); - } - res.status(404).send({message: 'Session does not exist'}); -} \ No newline at end of file +export function getSession(req, res) { + if (req.user) { + return res.json({ + email: req.user.email, + username: req.user.username + }); + } + return res.status(404).send({ message: 'Session does not exist' }); +} diff --git a/server/controllers/user.controller.js b/server/controllers/user.controller.js index 8fc8a4e6..209e254b 100644 --- a/server/controllers/user.controller.js +++ b/server/controllers/user.controller.js @@ -1,30 +1,30 @@ -import User from '../models/user' -import passport from 'passport' -import path from 'path' +import User from '../models/user'; export function createUser(req, res, next) { - const user = new User({ - username: req.body.username, + const user = new User({ + username: req.body.username, email: req.body.email, password: req.body.password }); - User.findOne({email: req.body.email}, (err, existingUser) => { - if (existingUser) { - return res.status(422).send({ error: 'Email is in use' }); - } - user.save((err) => { - if (err) { return next(err); } - req.logIn(user, (err) => { - if (err) { - return next(err); - } - res.json({ - email: req.user.email, - username: req.user.username - }); - }); - }); - }); + User.findOne({ email: req.body.email }, + (err, existingUser) => { // eslint-disable-line consistent-return + if (err) { res.status(404).send({ error: err }); } -} \ No newline at end of file + if (existingUser) { + return res.status(422).send({ error: 'Email is in use' }); + } + user.save((saveErr) => { // eslint-disable-line consistent-return + if (saveErr) { return next(saveErr); } + req.logIn(user, (loginErr) => { // eslint-disable-line consistent-return + if (loginErr) { + return next(loginErr); + } + res.json({ + email: req.user.email, + username: req.user.username + }); + }); + }); + }); +} diff --git a/server/models/project.js b/server/models/project.js index 44364251..0710e2ec 100644 --- a/server/models/project.js +++ b/server/models/project.js @@ -3,15 +3,15 @@ const Schema = mongoose.Schema; import shortid from 'shortid'; const fileSchema = new Schema({ - name: {type: String, default: 'sketch.js'}, - content: {type: String} -}, {timestamps: true}); + name: { type: String, default: 'sketch.js' }, + content: { type: String } +}, { timestamps: true }); const projectSchema = new Schema({ - name: {type: String, default: "Hello p5.js, it's the server"}, - user: {type: Schema.Types.ObjectId, ref: 'User'}, - file: {type: fileSchema}, - _id: {type: String, default: shortid.generate} -}, {timestamps: true}); + name: { type: String, default: "Hello p5.js, it's the server" }, + user: { type: Schema.Types.ObjectId, ref: 'User' }, + file: { type: fileSchema }, + _id: { type: String, default: shortid.generate } +}, { timestamps: true }); -export default mongoose.model('Project', projectSchema); \ No newline at end of file +export default mongoose.model('Project', projectSchema); diff --git a/server/models/user.js b/server/models/user.js index 45218218..30259775 100644 --- a/server/models/user.js +++ b/server/models/user.js @@ -3,27 +3,27 @@ const Schema = mongoose.Schema; const bcrypt = require('bcrypt-nodejs'); const userSchema = new Schema({ - name: { type: String, default: '' }, - username: { type: String, required: true, unique: true}, - password: { type: String }, - github: { type: String }, - email: { type: String, unique: true }, - tokens: Array, - admin: { type: Boolean, default: false } -}, {timestamps: true}); + name: { type: String, default: '' }, + username: { type: String, required: true, unique: true }, + password: { type: String }, + github: { type: String }, + email: { type: String, unique: true }, + tokens: Array, + admin: { type: Boolean, default: false } +}, { timestamps: true }); /** * Password hash middleware. */ -userSchema.pre('save', function (next) { +userSchema.pre('save', (next) => { // eslint-disable-line consistent-return const user = this; if (!user.isModified('password')) { return next(); } - bcrypt.genSalt(10, (err, salt) => { + bcrypt.genSalt(10, (err, salt) => { // eslint-disable-line consistent-return if (err) { return next(err); } - bcrypt.hash(user.password, salt, null, (err, hash) => { - if (err) { return next(err); } + bcrypt.hash(user.password, salt, null, (innerErr, hash) => { + if (innerErr) { return next(innerErr); } user.password = hash; - next(); + return next(); }); }); }); @@ -31,10 +31,10 @@ userSchema.pre('save', function (next) { /** * Helper method for validating user's password. */ -userSchema.methods.comparePassword = function (candidatePassword, cb) { +userSchema.methods.comparePassword = (candidatePassword, cb) => { bcrypt.compare(candidatePassword, this.password, (err, isMatch) => { cb(err, isMatch); }); }; -export default mongoose.model('User', userSchema); \ No newline at end of file +export default mongoose.model('User', userSchema); diff --git a/server/routes/project.routes.js b/server/routes/project.routes.js index 480030e7..2a31e632 100644 --- a/server/routes/project.routes.js +++ b/server/routes/project.routes.js @@ -9,4 +9,4 @@ router.route('/projects/:project_id').put(ProjectController.updateProject); router.route('/projects/:project_id').get(ProjectController.getProject); -export default router; \ No newline at end of file +export default router; diff --git a/server/routes/server.routes.js b/server/routes/server.routes.js index ad50100b..ccff0582 100644 --- a/server/routes/server.routes.js +++ b/server/routes/server.routes.js @@ -1,24 +1,24 @@ -import {Router} from 'express' +import { Router } from 'express'; const router = new Router(); -import path from 'path' +import path from 'path'; -// this is intended to be a temporary file +// this is intended to be a temporary file // until i figure out isomorphic rendering -router.route('/').get(function(req, res) { - res.sendFile(path.resolve(__dirname + '/../../index.html')); +router.route('/').get((req, res) => { + res.sendFile(path.resolve(`${__dirname}/../../index.html`)); }); -router.route('/signup').get(function(req, res) { - res.sendFile(path.resolve(__dirname + '/../../index.html')); +router.route('/signup').get((req, res) => { + res.sendFile(path.resolve(`${__dirname}/../../index.html`)); }); -router.route('/projects/:project_id').get(function(req, res) { - res.sendFile(path.resolve(__dirname + '/../../index.html')); +router.route('/projects/:project_id').get((req, res) => { + res.sendFile(path.resolve(`${__dirname}/../../index.html`)); }); -router.route('/login').get(function(req, res) { - res.sendFile(path.resolve(__dirname + '/../../index.html')); +router.route('/login').get((req, res) => { + res.sendFile(path.resolve(`${__dirname}/../../index.html`)); }); -export default router; \ No newline at end of file +export default router; diff --git a/server/routes/session.routes.js b/server/routes/session.routes.js index c94bee6b..b0305d70 100644 --- a/server/routes/session.routes.js +++ b/server/routes/session.routes.js @@ -1,15 +1,12 @@ import { Router } from 'express'; import * as SessionController from '../controllers/session.controller'; -import passport from 'passport'; const router = new Router(); router.route('/login').post(SessionController.createSession); -router.route('/logout').get(SessionController.destroySession); - router.route('/session').get(SessionController.getSession); export default router; -//TODO add github authentication stuff +// TODO add github authentication stuff diff --git a/server/server.js b/server/server.js index 13dbf6b6..e241e32b 100644 --- a/server/server.js +++ b/server/server.js @@ -7,7 +7,7 @@ const MongoStore = require('connect-mongo')(session); import passport from 'passport'; import path from 'path'; -//Webpack Requirements +// Webpack Requirements import webpack from 'webpack'; import config from '../webpack.config'; import webpackDevMiddleware from 'webpack-dev-middleware'; @@ -15,22 +15,22 @@ import webpackHotMiddleware from 'webpack-hot-middleware'; const app = new Express(); -//add check if production environment here +// add check if production environment here const compiler = webpack(config); app.use(webpackDevMiddleware(compiler, { noInfo: true, publicPath: config.output.publicPath })); app.use(webpackHotMiddleware(compiler)); -//Import all required modules +// Import all required modules import serverConfig from './config'; import users from './routes/user.routes'; import sessions from './routes/session.routes'; import projects from './routes/project.routes'; import serverRoutes from './routes/server.routes'; -//Body parser, cookie parser, sessions, serve public assets +// Body parser, cookie parser, sessions, serve public assets app.use(Express.static(path.resolve(__dirname, '../static'))); -app.use(bodyParser.urlencoded({extended: true})); +app.use(bodyParser.urlencoded({ extended: true })); app.use(bodyParser.json()); app.use(cookieParser()); app.use(session({ @@ -53,13 +53,15 @@ app.use(passport.session()); app.use('/api', users); app.use('/api', sessions); app.use('/api', projects); -//this is supposed to be TEMPORARY -- until i figure out +// this is supposed to be TEMPORARY -- until i figure out // isomorphic rendering app.use('/', serverRoutes); -const passportConfig = require('./config/passport'); +// configure passport +// const passportConfig = require('./config/passport'); +require('./config/passport'); -//Connect to MongoDB +// Connect to MongoDB // mongoose.connect(process.env.MONGODB_URI || process.env.MONGOLAB_URI); mongoose.connect(serverConfig.mongoURL); mongoose.connection.on('error', () => { @@ -67,9 +69,9 @@ mongoose.connection.on('error', () => { process.exit(1); }); -app.get("/", function(req, res) { - res.sendFile(path.resolve(__dirname + '/../index.html')); -}) +app.get('/', (req, res) => { + res.sendFile(path.resolve(`${__dirname}/../index.html`)); +}); // start app app.listen(serverConfig.port, (error) => { @@ -78,4 +80,5 @@ app.listen(serverConfig.port, (error) => { } }); -export default app; \ No newline at end of file +export default app; +