start to add selected file stuff

This commit is contained in:
catarak 2016-07-08 14:57:22 -04:00
parent e06c821923
commit 7a84137e9b
9 changed files with 70 additions and 19 deletions

View file

@ -23,5 +23,7 @@ export const NEW_PROJECT = 'NEW_PROJECT';
export const SET_PROJECT = 'SET_PROJECT'; export const SET_PROJECT = 'SET_PROJECT';
export const SET_PROJECTS = 'SET_PROJECTS'; export const SET_PROJECTS = 'SET_PROJECTS';
export const SET_SELECTED_FILE = 'SET_SELECTED_FILE';
// eventually, handle errors more specifically and better // eventually, handle errors more specifically and better
export const ERROR = 'ERROR'; export const ERROR = 'ERROR';

View file

@ -12,7 +12,8 @@ export function getProject(id) {
dispatch({ dispatch({
type: ActionTypes.SET_PROJECT, type: ActionTypes.SET_PROJECT,
project: response.data, project: response.data,
files: response.data.files files: response.data.files,
selectedFile: response.data.selectedFile
}); });
}) })
.catch(response => dispatch({ .catch(response => dispatch({
@ -34,7 +35,7 @@ export function saveProject() {
return (dispatch, getState) => { return (dispatch, getState) => {
const state = getState(); const state = getState();
const formParams = Object.assign({}, state.project); const formParams = Object.assign({}, state.project);
formParams.files = state.files; formParams.files = [...state.files];
if (state.project.id) { if (state.project.id) {
axios.put(`${ROOT_URL}/projects/${state.project.id}`, formParams, { withCredentials: true }) axios.put(`${ROOT_URL}/projects/${state.project.id}`, formParams, { withCredentials: true })
.then(() => { .then(() => {
@ -47,6 +48,12 @@ export function saveProject() {
error: response.data error: response.data
})); }));
} else { } else {
// this might be unnecessary, but to prevent collisions in mongodb
formParams.files.map(file => {
const newFile = Object.assign({}, file);
delete newFile.id;
return newFile;
});
axios.post(`${ROOT_URL}/projects`, formParams, { withCredentials: true }) axios.post(`${ROOT_URL}/projects`, formParams, { withCredentials: true })
.then(response => { .then(response => {
browserHistory.push(`/projects/${response.data.id}`); browserHistory.push(`/projects/${response.data.id}`);
@ -54,6 +61,7 @@ export function saveProject() {
type: ActionTypes.NEW_PROJECT, type: ActionTypes.NEW_PROJECT,
name: response.data.name, name: response.data.name,
id: response.data.id, id: response.data.id,
selectedFile: response.data.selectedFile,
files: response.data.files files: response.data.files
}); });
}) })
@ -75,6 +83,7 @@ export function createProject() {
type: ActionTypes.NEW_PROJECT, type: ActionTypes.NEW_PROJECT,
name: response.data.name, name: response.data.name,
id: response.data.id, id: response.data.id,
selectedFile: response.data.selectedFile,
files: response.data.files files: response.data.files
}); });
}) })

View file

@ -1,22 +1,32 @@
import React, { PropTypes } from 'react'; import React, { PropTypes } from 'react';
import classNames from 'classnames';
function Sidebar(props) { function Sidebar(props) {
return ( return (
<section className="sidebar"> <section className="sidebar">
<ul className="sidebar__file-list"> <ul className="sidebar__file-list">
{props.files.map(file => {props.files.map(file => {
<li let itemClass = classNames({
className="sidebar__file-item" 'sidebar__file-item': true,
key={file.id} 'sidebar__file-item--selected': file.id === props.selectedFile.id
>{file.name}</li> });
)} return (
<li
className={itemClass}
key={file.id}
>{file.name}</li>
);
})}
</ul> </ul>
</section> </section>
); );
} }
Sidebar.propTypes = { Sidebar.propTypes = {
files: PropTypes.array.isRequired files: PropTypes.array.isRequired,
selectedFile: PropTypes.shape({
id: PropTypes.string.isRequired
})
}; };
export default Sidebar; export default Sidebar;

View file

@ -11,6 +11,7 @@ import * as FileActions from '../actions/files';
import * as IDEActions from '../actions/ide'; import * as IDEActions from '../actions/ide';
import * as PreferencesActions from '../actions/preferences'; import * as PreferencesActions from '../actions/preferences';
import * as ProjectActions from '../actions/project'; import * as ProjectActions from '../actions/project';
import { getFile } from '../reducers/files';
class IDEView extends React.Component { class IDEView extends React.Component {
componentDidMount() { componentDidMount() {
@ -45,14 +46,18 @@ class IDEView extends React.Component {
decreaseFont={this.props.decreaseFont} decreaseFont={this.props.decreaseFont}
fontSize={this.props.preferences.fontSize} fontSize={this.props.preferences.fontSize}
/> />
<Sidebar files={this.props.files} /> <Sidebar
files={this.props.files}
selectedFile={this.props.selectedFile}
/>
<Editor <Editor
content={this.props.files[0].content} content={this.props.selectedFile.content}
updateFileContent={this.props.updateFileContent} updateFileContent={this.props.updateFileContent}
fontSize={this.props.preferences.fontSize} fontSize={this.props.preferences.fontSize}
files={this.props.files}
/> />
<PreviewFrame <PreviewFrame
content={this.props.files[0].content} content={this.props.selectedFile.content}
head={ head={
<link type="text/css" rel="stylesheet" href="/preview-styles.css" /> <link type="text/css" rel="stylesheet" href="/preview-styles.css" />
} }
@ -89,12 +94,17 @@ IDEView.propTypes = {
increaseFont: PropTypes.func.isRequired, increaseFont: PropTypes.func.isRequired,
decreaseFont: PropTypes.func.isRequired, decreaseFont: PropTypes.func.isRequired,
files: PropTypes.array.isRequired, files: PropTypes.array.isRequired,
updateFileContent: PropTypes.func.isRequired updateFileContent: PropTypes.func.isRequired,
selectedFile: PropTypes.shape({
id: PropTypes.string,
content: PropTypes.string.isRequired
})
}; };
function mapStateToProps(state) { function mapStateToProps(state) {
return { return {
files: state.files, files: state.files,
selectedFile: getFile(state.files, state.ide.selectedFile),
ide: state.ide, ide: state.ide,
preferences: state.preferences, preferences: state.preferences,
user: state.user, user: state.user,

View file

@ -20,14 +20,18 @@ const defaultHTML =
</html> </html>
`; `;
// if the project has never been saved,
const initialState = [ const initialState = [
{ {
name: 'sketch.js', name: 'sketch.js',
content: defaultSketch content: defaultSketch,
id: '1'
}, },
{ {
name: 'index.html', name: 'index.html',
content: defaultHTML content: defaultHTML,
id: '2'
}]; }];
@ -50,4 +54,6 @@ const files = (state = initialState, action) => {
} }
}; };
export const getFile = (state, id) => state.filter(file => file.id === id)[0];
export default files; export default files;

View file

@ -1,7 +1,8 @@
import * as ActionTypes from '../../../constants'; import * as ActionTypes from '../../../constants';
const initialState = { const initialState = {
isPlaying: false isPlaying: false,
selectedFile: '1'
}; };
const ide = (state = initialState, action) => { const ide = (state = initialState, action) => {
@ -18,6 +19,10 @@ const ide = (state = initialState, action) => {
return { return {
isPlaying: false isPlaying: false
}; };
case ActionTypes.SET_SELECTED_FILE:
case ActionTypes.SET_PROJECT:
case ActionTypes.NEW_PROJECT:
return Object.assign({}, state, { selectedFile: action.selectedFile });
default: default:
return state; return state;
} }

View file

@ -60,6 +60,7 @@
"babel-core": "^6.8.0", "babel-core": "^6.8.0",
"bcrypt-nodejs": "0.0.3", "bcrypt-nodejs": "0.0.3",
"body-parser": "^1.15.1", "body-parser": "^1.15.1",
"bson-objectid": "^1.1.4",
"classnames": "^2.2.5", "classnames": "^2.2.5",
"codemirror": "^5.14.2", "codemirror": "^5.14.2",
"connect-mongo": "^1.2.0", "connect-mongo": "^1.2.0",

View file

@ -2,8 +2,7 @@ import Project from '../models/project';
export function createProject(req, res) { export function createProject(req, res) {
const projectValues = { const projectValues = {
user: req.user ? req.user._id : undefined, // eslint-disable-line no-underscore-dangle user: req.user ? req.user._id : undefined // eslint-disable-line no-underscore-dangle
file: {}
}; };
Object.assign(projectValues, req.body); Object.assign(projectValues, req.body);

View file

@ -40,7 +40,8 @@ const projectSchema = new Schema({
name: { type: String, default: "Hello p5.js, it's the server" }, name: { type: String, default: "Hello p5.js, it's the server" },
user: { type: Schema.Types.ObjectId, ref: 'User' }, user: { type: Schema.Types.ObjectId, ref: 'User' },
files: {type: [ fileSchema ], default: [{ name: 'sketch.js', content: defaultSketch, _id: new ObjectId() }, { name: 'index.html', content: defaultHTML, _id: new ObjectId() }]}, files: {type: [ fileSchema ], default: [{ name: 'sketch.js', content: defaultSketch, _id: new ObjectId() }, { name: 'index.html', content: defaultHTML, _id: new ObjectId() }]},
_id: { type: String, default: shortid.generate } _id: { type: String, default: shortid.generate },
selectedFile: Schema.Types.ObjectId
}, { timestamps: true }); }, { timestamps: true });
projectSchema.virtual('id').get(function(){ projectSchema.virtual('id').get(function(){
@ -51,4 +52,12 @@ projectSchema.set('toJSON', {
virtuals: true virtuals: true
}); });
projectSchema.pre('save', function createSelectedFile(next) {
const project = this;
if (!project.selectedFile) {
project.selectedFile = project.files[0]._id; // eslint-disable-line no-underscore-dangle
return next();
}
});
export default mongoose.model('Project', projectSchema); export default mongoose.model('Project', projectSchema);