fix a ton of eslint errors
This commit is contained in:
parent
77cc7b2a8c
commit
3d42da18a8
51 changed files with 1026 additions and 937 deletions
30
.eslintrc
30
.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
|
||||
}
|
||||
}
|
37
client/components/Nav.js
Normal file
37
client/components/Nav.js
Normal 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;
|
|
@ -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;
|
|
@ -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 />}
|
|
@ -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
|
||||
};
|
||||
}
|
|
@ -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
|
||||
}
|
||||
};
|
||||
}
|
|
@ -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
|
||||
}
|
||||
};
|
||||
}
|
|
@ -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
|
||||
}));
|
||||
}
|
||||
};
|
||||
}
|
43
client/modules/IDE/components/Editor.js
Normal file
43
client/modules/IDE/components/Editor.js
Normal 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;
|
|
@ -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;
|
38
client/modules/IDE/components/Preferences.js
Normal file
38
client/modules/IDE/components/Preferences.js
Normal 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;
|
|
@ -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;
|
20
client/modules/IDE/components/Preview.js
Normal file
20
client/modules/IDE/components/Preview.js
Normal 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;
|
|
@ -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;
|
67
client/modules/IDE/components/PreviewFrame.js
Normal file
67
client/modules/IDE/components/PreviewFrame.js
Normal 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;
|
|
@ -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;
|
52
client/modules/IDE/components/Toolbar.js
Normal file
52
client/modules/IDE/components/Toolbar.js
Normal 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;
|
|
@ -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;
|
83
client/modules/IDE/pages/IDEView.js
Normal file
83
client/modules/IDE/pages/IDEView.js
Normal 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);
|
|
@ -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);
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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
|
||||
};
|
||||
}
|
34
client/modules/User/components/LoginForm.js
Normal file
34
client/modules/User/components/LoginForm.js
Normal 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;
|
|
@ -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;
|
58
client/modules/User/components/SignupForm.js
Normal file
58
client/modules/User/components/SignupForm.js
Normal 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;
|
|
@ -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;
|
37
client/modules/User/pages/LoginView.js
Normal file
37
client/modules/User/pages/LoginView.js
Normal 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);
|
|
@ -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);
|
38
client/modules/User/pages/SignupView.js
Normal file
38
client/modules/User/pages/SignupView.js
Normal 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);
|
|
@ -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);
|
|
@ -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;
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
}
|
||||
});
|
||||
})
|
||||
});
|
||||
}
|
|
@ -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' });
|
||||
}
|
|
@ -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) {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
}
|
|
@ -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);
|
||||
});
|
||||
|
|
|
@ -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;
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in a new issue