fix a ton of eslint errors

This commit is contained in:
catarak 2016-06-23 18:29:55 -04:00
parent 77cc7b2a8c
commit 3d42da18a8
51 changed files with 1026 additions and 937 deletions

View file

@ -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
}
}

37
client/components/Nav.js Normal file
View file

@ -0,0 +1,37 @@
import React from 'react';
import { Link } from 'react-router';
class Nav extends React.Component {
render() {
return (
<nav className="nav">
<ul className="nav__items-left">
<li className="nav__item">
<p
className="nav__new"
onClick={this.props.createProject}
>
New
</p>
</li>
<li className="nav__item">
<p
className="nav__save"
onClick={this.props.saveProject}
>
Save
</p>
</li>
</ul>
<ul className="nav__items-right">
<li className="nav__item">
{this.props.user.authenticated && <p>Hello, {this.props.user.username}!</p>}
{!this.props.user.authenticated && <p><Link to='/login'>Login</Link> or <Link to='/signup'>Sign Up</Link></p>}
</li>
</ul>
</nav>
);
}
}
export default Nav;

View file

@ -1,33 +0,0 @@
import React from 'react'
import { Link } from 'react-router'
class Nav extends React.Component {
render() {
return (
<nav className="nav">
<ul className="nav__items-left">
<li className="nav__item">
<p className="nav__new"
onClick={this.props.createProject}>
New
</p>
</li>
<li className="nav__item">
<p className="nav__save"
onClick={this.props.saveProject}>
Save
</p>
</li>
</ul>
<ul className="nav__items-right">
<li className="nav__item">
{this.props.user.authenticated && <p>Hello, {this.props.user.username}!</p>}
{!this.props.user.authenticated && <p><Link to={`/login`}>Login</Link> or <Link to={`/signup`}>Sign Up</Link></p>}
</li>
</ul>
</nav>
);
}
}
export default Nav;

View file

@ -13,7 +13,6 @@ class App extends React.Component {
}
render() {
debugger;
return (
<div className="app">
{this.state.isMounted && !window.devToolsExtension && process.env.NODE_ENV === 'development' && <DevTools />}

View file

@ -3,7 +3,7 @@ import * as ActionTypes from '../../../constants';
export function updateFile(name, content) {
return {
type: ActionTypes.CHANGE_SELECTED_FILE,
name: name,
content: content
}
name,
content
};
}

View file

@ -3,17 +3,17 @@ import * as ActionTypes from '../../../constants';
export function toggleSketch() {
return {
type: ActionTypes.TOGGLE_SKETCH
}
};
}
export function startSketch() {
return {
type: ActionTypes.START_SKETCH
}
};
}
export function stopSketch() {
return {
type: ActionTypes.STOP_SKETCH
}
};
}

View file

@ -3,23 +3,23 @@ import * as ActionTypes from '../../../constants';
export function openPreferences() {
return {
type: ActionTypes.OPEN_PREFERENCES
}
};
}
export function closePreferences() {
return {
type: ActionTypes.CLOSE_PREFERENCES
}
};
}
export function increaseFont() {
return {
type: ActionTypes.INCREASE_FONTSIZE
}
};
}
export function decreaseFont() {
return {
type: ActionTypes.DECREASE_FONTSIZE
}
};
}

View file

@ -1,54 +1,54 @@
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) {
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
type: ActionTypes.ERROR,
error: response.data
}));
}
};
}
export function setProjectName(event) {
var name = event.target.textContent;
const name = event.target.textContent;
return {
type: ActionTypes.SET_PROJECT_NAME,
name: name
}
name
};
}
export function saveProject() {
return function(dispatch, getState) {
var state = getState();
var formParams = Object.assign({}, state.project);
return (dispatch, getState) => {
const state = getState();
const 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 => {
.then(() => {
dispatch({
type: ActionTypes.PROJECT_SAVE_SUCCESS
})
.catch(response => dispatch({
type: ActionTypes.PROJECT_SAVE_FAIL
.catch((response) => dispatch({
type: ActionTypes.PROJECT_SAVE_FAIL,
error: response.data
}));
})
}
else {
});
} else {
axios.post(`${ROOT_URL}/projects`, formParams, { withCredentials: true })
.then(response => {
browserHistory.push('/projects/' + response.data.id);
browserHistory.push(`/projects/${response.data.id}`);
dispatch({
type: ActionTypes.NEW_PROJECT,
name: response.data.name,
@ -60,18 +60,19 @@ export function saveProject() {
});
})
.catch(response => dispatch({
type: ActionTypes.PROJECT_SAVE_FAIL
type: ActionTypes.PROJECT_SAVE_FAIL,
error: response.data
}));
}
}
};
}
export function createProject() {
return function(dispatch) {
return (dispatch) => {
axios.post(`${ROOT_URL}/projects`, {}, { withCredentials: true })
.then(response => {
browserHistory.push('/projects/' + response.data.id);
browserHistory.push(`/projects/${response.data.id}`);
dispatch({
type: ActionTypes.NEW_PROJECT,
name: response.data.name,
@ -83,7 +84,8 @@ export function createProject() {
});
})
.catch(response => dispatch({
type: ActionTypes.PROJECT_SAVE_FAIL
type: ActionTypes.PROJECT_SAVE_FAIL,
error: response.data
}));
}
};
}

View file

@ -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 <div ref="container" className="editor-holder"></div>;
}
}
export default Editor;

View file

@ -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 <div ref="container" className="editor-holder"></div>;
}
}
export default Editor;

View file

@ -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 (
<div className={preferencesContainerClass} tabIndex="0">
<div className="preferences__heading">
<h2 className="preferences__title">Preferences</h2>
<button className="preferences__exit-button" onClick={this.props.closePreferences}>
<Isvg src={exitUrl} alt="Exit Preferences" />
</button>
</div>
<div className="preference">
<h3 className="preference__title">Text Size</h3>
<button className="preference__plus-button" onClick={this.props.decreaseFont}>
<Isvg src={minusUrl} alt="Decrease Font Size" />
</button>
<p className="preference__value">{this.props.fontSize}</p>
<button className="preference__minus-button" onClick={this.props.increaseFont}>
<Isvg src={plusUrl} alt="Increase Font Size" />
</button>
</div>
</div>
);
}
}
export default Preferences;

View file

@ -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 (
<div className={preferencesContainerClass} tabindex="0">
<div className="preferences__heading">
<h2 className="preferences__title">Preferences</h2>
<button className="preferences__exit-button" onClick={this.props.closePreferences}>
<Isvg src={exitUrl} alt="Exit Preferences" />
</button>
</div>
<div className="preference">
<h3 className="preference__title">Text Size</h3>
<button className="preference__plus-button" onClick={this.props.decreaseFont}>
<Isvg src={minusUrl} alt="Decrease Font Size" />
</button>
<p className="preference__value">{this.props.fontSize}</p>
<button className="preference__minus-button" onClick={this.props.increaseFont}>
<Isvg src={plusUrl} alt="Increase Font Size" />
</button>
</div>
</div>
);
}
}
export default Preferences;

View file

@ -0,0 +1,20 @@
import React from 'react';
class Preview extends React.Component {
componentDidMount() {
}
render() {
return (
<div>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.0/p5.min.js"></script>
<script type="text/javascript">
{this.props.content}
</script>
</div>
);
}
}
export default Preview;

View file

@ -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 (
<div>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.5.0/p5.min.js"></script>
<script type="text/javascript">
{this.props.content}
</script>
</div>
);
}
}
export default Preview;

View file

@ -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 <iframe
className="preview-frame"
frameBorder="0"
sandbox="allow-scripts allow-pointer-lock allow-same-origin allow-popups allow-modals allow-forms"
title="sketch output"></iframe>;
}
}
export default PreviewFrame;

View file

@ -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 <iframe className="preview-frame" frameBorder="0" sandbox="allow-scripts allow-pointer-lock allow-same-origin allow-popups allow-modals allow-forms" title="sketch output"></iframe>;
}
}
export default PreviewFrame;

View file

@ -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 (
<div className="toolbar">
<img className="toolbar__logo" src={logoUrl} alt="p5js Logo" />
<button className={playButtonClass} onClick={this.props.startSketch}>
<Isvg src={playUrl} alt="Play Sketch" />
</button>
<button className={stopButtonClass} onClick={this.props.stopSketch}>
<Isvg src={stopUrl} alt="Stop Sketch" />
</button>
<div className="toolbar__project-name-container">
<span
className="toolbar__project-name"
onBlur={this.props.setProjectName.bind(this)}
contentEditable
suppressContentEditableWarning
>
{this.props.projectName}
</span>
</div>
<button className={preferencesButtonClass} onClick={this.props.openPreferences}>
<Isvg src={preferencesUrl} alt="Show Preferences" />
</button>
</div>
);
}
}
export default Toolbar;

View file

@ -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 (
<div className="toolbar">
<img className="toolbar__logo" src={logoUrl} alt="p5js Logo"/>
<button className={playButtonClass} onClick={this.props.startSketch}>
<Isvg src={playUrl} alt="Play Sketch" />
</button>
<button className={stopButtonClass} onClick={this.props.stopSketch}>
<Isvg src={stopUrl} alt="Stop Sketch" />
</button>
<div className="toolbar__project-name-container">
<span className="toolbar__project-name"
onBlur={this.props.setProjectName.bind(this)}
contentEditable={true}
suppressContentEditableWarning={true}>
{this.props.projectName}
</span>
</div>
<button className={preferencesButtonClass} onClick={this.props.openPreferences}>
<Isvg src={preferencesUrl} alt="Show Preferences" />
</button>
</div>
);
}
}
export default Toolbar;

View file

@ -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 (
<div className="ide">
<Nav
user={this.props.user}
createProject={this.props.createProject}
saveProject={this.props.saveProject}
/>
<Toolbar
className="Toolbar"
isPlaying={this.props.ide.isPlaying}
startSketch={this.props.startSketch}
stopSketch={this.props.stopSketch}
projectName={this.props.project.name}
setProjectName={this.props.setProjectName}
openPreferences={this.props.openPreferences}
isPreferencesVisible={this.props.preferences.isVisible}
/>
<Preferences
isVisible={this.props.preferences.isVisible}
closePreferences={this.props.closePreferences}
increaseFont={this.props.increaseFont}
decreaseFont={this.props.decreaseFont}
fontSize={this.props.preferences.fontSize}
/>
<Editor
content={this.props.file.content}
updateFile={this.props.updateFile}
fontSize={this.props.preferences.fontSize}
/>
<PreviewFrame
content={this.props.file.content}
head={
<link type="text/css" rel="stylesheet" href="/preview-styles.css" />
}
isPlaying={this.props.ide.isPlaying}
/>
</div>
);
}
}
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);

View file

@ -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 (
<div className="ide">
<Nav user={this.props.user}
createProject={this.props.createProject}
saveProject={this.props.saveProject}/>
<Toolbar
className="Toolbar"
isPlaying={this.props.ide.isPlaying}
startSketch={this.props.startSketch}
stopSketch={this.props.stopSketch}
projectName={this.props.project.name}
setProjectName={this.props.setProjectName}
openPreferences={this.props.openPreferences}
isPreferencesVisible={this.props.preferences.isVisible}/>
<Preferences
isVisible={this.props.preferences.isVisible}
closePreferences={this.props.closePreferences}
increaseFont={this.props.increaseFont}
decreaseFont={this.props.decreaseFont}
fontSize={this.props.preferences.fontSize}/>
<Editor
content={this.props.file.content}
updateFile={this.props.updateFile}
fontSize={this.props.preferences.fontSize} />
<PreviewFrame
content={this.props.file.content}
head={
<link type='text/css' rel='stylesheet' href='/preview-styles.css' />
}
isPlaying={this.props.ide.isPlaying}/>
</div>
)
}
}
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);

View file

@ -1,7 +1,7 @@
import * as ActionTypes from '../../../constants';
const initialState = {
name: "sketch.js",
name: 'sketch.js',
content: `function setup() {
createCanvas(400, 400);
}
@ -9,7 +9,7 @@ const initialState = {
function draw() {
background(220);
}`
}
};
const file = (state = initialState, action) => {
switch (action.type) {
@ -17,16 +17,16 @@ const file = (state = initialState, action) => {
return {
name: action.name,
content: action.content
}
};
case ActionTypes.NEW_PROJECT:
return {
name: action.file.name,
content: action.file.conent
}
};
default:
return state
}
return state;
}
};
export default file;

View file

@ -2,25 +2,25 @@ import * as ActionTypes from '../../../constants';
const initialState = {
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
}
return state;
}
};
export default ide;

View file

@ -3,7 +3,7 @@ import * as ActionTypes from '../../../constants';
const initialState = {
isVisible: false,
fontSize: 18
}
};
const preferences = (state = initialState, action) => {
switch (action.type) {
@ -11,25 +11,25 @@ const preferences = (state = initialState, action) => {
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
}
return state;
}
};
export default preferences;

View file

@ -1,20 +1,20 @@
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,
@ -23,10 +23,10 @@ const project = (state = initialState, action) => {
name: action.project.file.name,
content: action.project.file.content
}
}
};
default:
return state;
}
}
};
export default project;

View file

@ -1,12 +1,19 @@
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 authError(error) {
return {
type: ActionTypes.AUTH_ERROR,
payload: error
};
}
export function signUpUser(formValues) {
return function(dispatch) {
return (dispatch) => {
axios.post(`${ROOT_URL}/signup`, formValues, { withCredentials: true })
.then(response => {
dispatch({ type: ActionTypes.AUTH_USER,
@ -15,11 +22,11 @@ export function signUpUser(formValues) {
browserHistory.push('/');
})
.catch(response => dispatch(authError(response.data.error)));
}
};
}
export function loginUser(formValues) {
return function(dispatch) {
return (dispatch) => {
axios.post(`${ROOT_URL}/login`, formValues, { withCredentials: true })
.then(response => {
dispatch({ type: ActionTypes.AUTH_USER,
@ -28,24 +35,18 @@ export function loginUser(formValues) {
browserHistory.push('/');
})
.catch(response => dispatch(authError(response.data.error)));
}
};
}
export function getUser() {
return function(dispatch) {
return (dispatch) => {
axios.get(`${ROOT_URL}/session`, { withCredentials: true })
.then(response => {
dispatch({type: ActionTypes.AUTH_USER,
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
};
}

View file

@ -0,0 +1,34 @@
import React from 'react';
class LoginForm extends React.Component {
render() {
const { fields: { email, password }, handleSubmit } = this.props;
return (
<form className="login-form" onSubmit={handleSubmit(this.props.loginUser.bind(this))}>
<p className="login-form__field">
<label className="login-form__email-label" htmlFor="email">Email:</label>
<input
className="login-form__email-input"
id="email"
type="text"
placeholder="Email"
{...email}
/>
</p>
<p className="login-form__field">
<label className="signup-form__password-label" htmlFor="password">Password:</label>
<input
className="signup-form__password-input"
id="password"
type="password"
placeholder="Password"
{...password}
/>
</p>
<input type="submit" value="Login" />
</form>
);
}
}
export default LoginForm;

View file

@ -1,22 +0,0 @@
import React from 'react'
class LoginForm extends React.Component {
render() {
const {fields: {email, password}, handleSubmit } = this.props;
return (
<form className="login-form" onSubmit={handleSubmit(this.props.loginUser.bind(this))}>
<p className="login-form__field">
<label className="login-form__email-label" for="email">Email:</label>
<input className="login-form__email-input" id="email" type="text" placeholder="Email" {...email}/>
</p>
<p className="login-form__field">
<label className="signup-form__password-label" for="password">Password:</label>
<input className="signup-form__password-input" id="password" type="password" placeholder="Password" {...password}/>
</p>
<input type="submit" value="Login" />
</form>
)
}
}
export default LoginForm;

View file

@ -0,0 +1,58 @@
import React from 'react';
class SignupForm extends React.Component {
render() {
const { fields: { username, email, password, confirmPassword }, handleSubmit } = this.props;
return (
<form className="signup-form" onSubmit={handleSubmit(this.props.signUpUser.bind(this))}>
<p className="signup-form__field">
<label className="signup-form__username-label" htmlFor="username">Username:</label>
<input
className="signup-form__username-input"
id="username"
type="text"
placeholder="Username"
{...username}
/>
</p>
<p className="signup-form__field">
<label className="signup-form__email-label" htmlFor="email">Email:</label>
<input
className="signup-form__email-input"
id="email"
type="text"
placeholder="Email"
{...email}
/>
</p>
<p className="signup-form__field">
<label className="signup-form__password-label" htmlFor="password">Password:</label>
<input
className="signup-form__password-input"
id="password"
type="password"
placeholder="Password"
{...password}
/>
</p>
<p className="signup-form__field">
<label
className="signup-form__confirm-password-label"
htmlFor="confirm-password">
Confirm Password:
</label>
<input
className="signup-form__confirm-password-input"
id="confirm-password"
type="password"
placeholder="Confirm Password"
{...confirmPassword}
/>
</p>
<input type="submit" value="Sign Up" />
</form>
);
}
}
export default SignupForm;

View file

@ -1,30 +0,0 @@
import React from 'react'
class SignupForm extends React.Component {
render() {
const {fields: { username, email, password, confirmPassword }, handleSubmit} = this.props;
return (
<form className="signup-form" onSubmit={handleSubmit(this.props.signUpUser.bind(this))}>
<p className="signup-form__field">
<label className="signup-form__username-label" for="username">Username:</label>
<input className="signup-form__username-input" id="username" type="text" placeholder="Username" {...username}/>
</p>
<p className="signup-form__field">
<label className="signup-form__email-label" for="email">Email:</label>
<input className="signup-form__email-input" id="email" type="text" placeholder="Email" {...email}/>
</p>
<p className="signup-form__field">
<label className="signup-form__password-label" for="password">Password:</label>
<input className="signup-form__password-input" id="password" type="password" placeholder="Password" {...password}/>
</p>
<p className="signup-form__field">
<label className="signup-form__confirm-password-label" for="confirm-password">Confirm Password:</label>
<input className="signup-form__confirm-password-input" id="confirm-password" type="password" placeholder="Confirm Password" {...confirmPassword}/>
</p>
<input type="submit" value="Sign Up" />
</form>
)
}
}
export default SignupForm;

View file

@ -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 (
<div className="login">
<h1>Login</h1>
<LoginForm {...this.props} />
</div>
);
}
}
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);

View file

@ -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 (
<div className="login">
<h1>Login</h1>
<LoginForm {...this.props} />
</div>
)
}
}
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);

View file

@ -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 (
<div className="signup">
<h1>Sign Up</h1>
<SignupForm {...this.props} />
</div>
);
}
}
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);

View file

@ -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 (
<div className="signup">
<h1>Sign Up</h1>
<SignupForm {...this.props} />
</div>
)
}
}
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);

View file

@ -1,4 +1,4 @@
import * as ActionTypes from '../../constants'
import * as ActionTypes from '../../constants';
const user = (state = { authenticated: false }, action) => {
switch (action.type) {
@ -8,10 +8,10 @@ const user = (state = {authenticated: false}, action) => {
case ActionTypes.AUTH_ERROR:
return {
authenticated: false
}
};
default:
return state;
}
}
};
export default user;

View file

@ -1,10 +1,10 @@
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,
@ -13,6 +13,6 @@ const rootReducer = combineReducers({
preferences,
user,
project
})
});
export default rootReducer
export default rootReducer;

View file

@ -1,10 +1,14 @@
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'
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());
};
const routes = (store) => {
return (
@ -15,10 +19,6 @@ const routes = (store) => {
<Route path="/projects/:project_id" component={IDEView} />
</Route>
);
}
const checkAuth = (store) => {
store.dispatch(getUser());
}
};
export default routes;

View file

@ -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
return store;
}

View file

@ -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",

View file

@ -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,11 +18,15 @@ 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) => {
User.findOne({ email: email.toLowerCase() },
(err, user) => { // eslint-disable-line consistent-return
if (!user) {
return done(null, false, { msg: `Email ${email} not found.` });
}
user.comparePassword(password, (err, isMatch) => {
user.comparePassword(password, (innerErr, isMatch) => {
if (innerErr) {
return done(innerErr);
}
if (isMatch) {
return done(null, user);
}
@ -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();

View file

@ -1,17 +1,17 @@
import Project from '../models/project'
import Project from '../models/project';
export function createProject(req, res) {
let projectValues = {
user: req.user ? req.user._id : undefined,
const projectValues = {
user: req.user ? req.user._id : undefined, // eslint-disable-line no-underscore-dangle
file: {}
}
};
Object.assign(projectValues, req.body);
Project.create(projectValues, function(err, newProject) {
Project.create(projectValues, (err, newProject) => {
if (err) { return res.json({ success: false }); }
return res.json({
id: newProject._id,
id: newProject._id, // eslint-disable-line no-underscore-dangle
name: newProject.name,
file: {
name: newProject.file.name,
@ -25,10 +25,10 @@ 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}) }
}, (err, updatedProject) => {
if (err) { return res.json({ success: false }); }
return res.json({
id: updatedProject._id,
id: updatedProject._id, // eslint-disable-line no-underscore-dangle
name: updatedProject.name,
file: {
name: updatedProject.file.name,
@ -39,18 +39,18 @@ export function updateProject(req, res) {
}
export function getProject(req, res) {
Project.findById(req.params.project_id, function(err, project) {
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,
id: project._id, // eslint-disable-line no-underscore-dangle
name: project.name,
file: {
name: project.file.name,
content: project.file.conent
}
});
})
});
}

View file

@ -1,22 +1,15 @@
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) => {
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({
req.logIn(user, (innerErr) => {
if (innerErr) { return next(innerErr); }
return res.json({
email: req.user.email,
username: req.user.username
});
@ -24,12 +17,12 @@ export function createSession(req, res, next) {
})(req, res, next);
}
export function getSession(req, res, next) {
export function getSession(req, res) {
if (req.user) {
return res.json({
email: req.user.email,
username: req.user.username
});
}
res.status(404).send({message: 'Session does not exist'});
return res.status(404).send({ message: 'Session does not exist' });
}

View file

@ -1,6 +1,4 @@
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({
@ -9,15 +7,18 @@ export function createUser(req, res, next) {
password: req.body.password
});
User.findOne({email: req.body.email}, (err, existingUser) => {
User.findOne({ email: req.body.email },
(err, existingUser) => { // eslint-disable-line consistent-return
if (err) { res.status(404).send({ error: err }); }
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);
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,
@ -26,5 +27,4 @@ export function createUser(req, res, next) {
});
});
});
}

View file

@ -15,15 +15,15 @@ const userSchema = new Schema({
/**
* 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) => {
if (err) { return next(err); }
bcrypt.hash(user.password, salt, null, (err, hash) => {
bcrypt.genSalt(10, (err, salt) => { // eslint-disable-line consistent-return
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,7 +31,7 @@ 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);
});

View file

@ -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
// 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;

View file

@ -1,13 +1,10 @@
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;

View file

@ -57,7 +57,9 @@ app.use('/api', projects);
// isomorphic rendering
app.use('/', serverRoutes);
const passportConfig = require('./config/passport');
// configure passport
// const passportConfig = require('./config/passport');
require('./config/passport');
// Connect to MongoDB
// mongoose.connect(process.env.MONGODB_URI || process.env.MONGOLAB_URI);
@ -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) => {
@ -79,3 +81,4 @@ app.listen(serverConfig.port, (error) => {
});
export default app;