diff --git a/backend/server.js b/backend/server.js index 9d815dc731..af96b2c396 100644 --- a/backend/server.js +++ b/backend/server.js @@ -1,11 +1,223 @@ -const express = require('express') -const app = express() +const express = require('express'); +const app = express(); -// Example route -app.get('/', function (req, res) { - res.send('Hello World!') +const path = require('path'); +const bodyParser = require('body-parser') +const passport = require('passport'); +const LocalStrategy = require('passport-local'); +const User = require('../reactApp/models').User; +const Document = require('../reactApp/models').Document; + +var session = require('express-session'); +const MongoStore = require('connect-mongo')(session); + + +app.use(express.static(path.join(__dirname, 'build'))); + +app.use(bodyParser.json()); +app.use(bodyParser.urlencoded({ extended: false })); + +const mongoose = require('mongoose'); +mongoose.connect(process.env.MONGODB_URI) + +app.use(session({ + secret: 'secret', + store: new MongoStore({ + mongooseConnection: mongoose.connection + }) +})); + +passport.serializeUser(function(user, done) { + done(null, user._id); +}); + +passport.deserializeUser(function(id, done) { + User.findById(id, function(err, user) { + done(err, user); + }); +}); + +// passport strategy +passport.use(new LocalStrategy(function(username, password, done) { + // Find the user with the given username + // May need to adapt this to your own model! + User.findOne({ username: username }, function (err, user) { + // if there's an error, finish trying to authenticate (auth failed) + if (err) { return done(err); } + // if no user present, auth failed + if (!user) { + return done(null, false, { message: 'Incorrect username.' }); + } + // if passwords do not match, auth failed + if (user.password !== password) { + return done(null, false, { message: 'Incorrect password.' }); + } + // auth has has succeeded + return done(null, user); + }); +})); + +app.use(passport.initialize()); +app.use(passport.session()); + +app.post('/login', passport.authenticate('local'), (req, res) => { + //if the passport authenticate doesn't respond, then it goes to (req, res); can have multiple (req, res) until one responds + res.json({success: true, user: req.user}); + //passport.authenticate +}) + +app.post('/register', (req, res) => { + //make a new User and save it + const newUser = new User({ + username: req.body.username, + password: req.body.password, + firstName: req.body.firstName, + lastName: req.body.lastName, + }) + newUser.save((err, user) => { + if(err) { + res.json({success: false, error: err}) + } else { + res.json({success: true}) + } + }) +}) + +app.post('/docs', (req, res) => { + //make a new User and save it + const newDocument = new Document({ + title: req.body.title, + ownerid: req.user._id, + owner: req.user.username, + collaborators: req.body.collaborators, + password: req.body.password, + }) + newDocument.save((err, newDoc) => { + if(err) { + res.json({success: false, error: err}) + } else { + res.json({success: true, doc: newDoc}) + } + }) +}) + +app.use((req, res, next) => { + if (req.user) { + next(); + } else { + res.redirect('/login') + } +}); + +app.get('/getDocs', (req, res) => { + // Document.find({ownerid: req.user._id, collaborators: { $in: [req.user.username] }}, (err, docs) => { + Document.find({ $or: [{ownerid: req.user._id}, {collaborators: { $in: [req.user.username] }} ]}, (err, docs) => { + if (err) { + res.send(err) + } else { + res.json(docs) + } + }) +}) + +app.get('/getDoc/:docid', (req, res) => { + Document.findById(req.params.docid, (err, doc) => { + if (err) { + res.send(err) + } else { + res.json(doc) + } + }) +}) + +// app.get('/searchDoc/:docid', (req, res) => { +// Document.findById(req.params.docid, (err, doc) => { +// if (err) { +// res.send(err) +// } else { +// res.json(doc) +// } +// }) +// }) + +app.post('/saveDoc/:docid', (req, res) => { + Document.update({_id: req.params.docid}, { $set: {body: req.body.body, inlineStyles: req.body.inlineStyles} }, (err, result) => { + if (err) { + res.send({ success: false, error: err }) + } else { + res.json({success: true, result: result}) + } + } + ) +}) + +app.post('/addCollab/:docid', (req, res) => { + User.find({username: req.body.username}, (err, result) => { + if(err) { + res.json({ success: false, error: err }) + } else if (result.length === 0) { + res.json({ success: false, error: "No users found" }) + } else { + Document.update({_id: req.params.docid}, { $addToSet: {collaborators: req.body.username }}, (err, result) => { + if (err) { + res.send({ success: false, error: err }) + } else { + res.json({success: true, result: result}) + } + }) + } + }) }) +app.post('/searchDoc/:docid', (req, res) => { + Document.findById(req.params.docid, (err, doc) => { + if (err) { + res.send(err) + } else if (doc === null) { + res.json({success: false}) + } else if (req.body.password !== doc.password) { + res.json({success: false}) + } else { + Document.update({_id: req.params.docid}, { $push: {collaborators: req.user.username }}, (err, result) => { + if (err) { + res.send({ success: false, error: err }) + } else { + res.json({success: true, result: doc}) + } + }) + } + }) + }) + + // Document.update({_id: req.params.docid}, { $push: {collaborators: req.body.username }}, (err, result) => { + // if (err) { + // res.send({ success: false, error: err }) + // } else { + // res.json({success: true, result: result}) + // } + // }) + // } + // }) +// }) + +// app.post('/removeCollab/:docid', (req, res) => { +// User.find({username: req.body.username}, (err, result) => { +// if(err) { +// res.json({ success: false, error: err }) +// } else if (result.length === 0) { +// res.json({ success: false, error: "No users found" }) +// } else { +// Document.update({_id: req.params.docid}, { $pull: {collaborators: req.body.collaborator }}, (err, result) => { +// if (err) { +// res.send({ success: false, error: err }) +// } else { +// res.json({success: true, result: result}) +// } +// }) +// } +// }) +// }) + app.listen(3000, function () { console.log('Backend server for Electron App running on port 3000!') }) diff --git a/build/index.dev.html b/build/index.dev.html index 1a5ed9d40f..8f520159f1 100644 --- a/build/index.dev.html +++ b/build/index.dev.html @@ -3,6 +3,8 @@ Dev Electron App +
diff --git a/npm-debug.log b/npm-debug.log new file mode 100644 index 0000000000..2f80daa553 --- /dev/null +++ b/npm-debug.log @@ -0,0 +1,46 @@ +0 info it worked if it ends with ok +1 verbose cli [ '/usr/local/bin/node', '/usr/local/bin/npm', 'run', 'dev' ] +2 info using npm@3.10.10 +3 info using node@v6.11.3 +4 verbose run-script [ 'predev', 'dev', 'postdev' ] +5 info lifecycle electron-react-quick-start@1.0.0~predev: electron-react-quick-start@1.0.0 +6 silly lifecycle electron-react-quick-start@1.0.0~predev: no script for predev, continuing +7 info lifecycle electron-react-quick-start@1.0.0~dev: electron-react-quick-start@1.0.0 +8 verbose lifecycle electron-react-quick-start@1.0.0~dev: unsafe-perm in lifecycle true +9 verbose lifecycle electron-react-quick-start@1.0.0~dev: PATH: /usr/local/lib/node_modules/npm/bin/node-gyp-bin:/Users/staceykim/Desktop/horizons/GoogleDocs/ladydocs/node_modules/.bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin +10 verbose lifecycle electron-react-quick-start@1.0.0~dev: CWD: /Users/staceykim/Desktop/horizons/GoogleDocs/ladydocs +11 silly lifecycle electron-react-quick-start@1.0.0~dev: Args: [ '-c', +11 silly lifecycle 'concurrently -k "npm run devWebpack" "npm run devApp" "npm run devServer"' ] +12 silly lifecycle electron-react-quick-start@1.0.0~dev: Returned: code: 1 signal: null +13 info lifecycle electron-react-quick-start@1.0.0~dev: Failed to exec dev script +14 verbose stack Error: electron-react-quick-start@1.0.0 dev: `concurrently -k "npm run devWebpack" "npm run devApp" "npm run devServer"` +14 verbose stack Exit status 1 +14 verbose stack at EventEmitter. (/usr/local/lib/node_modules/npm/lib/utils/lifecycle.js:255:16) +14 verbose stack at emitTwo (events.js:106:13) +14 verbose stack at EventEmitter.emit (events.js:191:7) +14 verbose stack at ChildProcess. (/usr/local/lib/node_modules/npm/lib/utils/spawn.js:40:14) +14 verbose stack at emitTwo (events.js:106:13) +14 verbose stack at ChildProcess.emit (events.js:191:7) +14 verbose stack at maybeClose (internal/child_process.js:920:16) +14 verbose stack at Process.ChildProcess._handle.onexit (internal/child_process.js:230:5) +15 verbose pkgid electron-react-quick-start@1.0.0 +16 verbose cwd /Users/staceykim/Desktop/horizons/GoogleDocs/ladydocs +17 error Darwin 14.5.0 +18 error argv "/usr/local/bin/node" "/usr/local/bin/npm" "run" "dev" +19 error node v6.11.3 +20 error npm v3.10.10 +21 error code ELIFECYCLE +22 error electron-react-quick-start@1.0.0 dev: `concurrently -k "npm run devWebpack" "npm run devApp" "npm run devServer"` +22 error Exit status 1 +23 error Failed at the electron-react-quick-start@1.0.0 dev script 'concurrently -k "npm run devWebpack" "npm run devApp" "npm run devServer"'. +23 error Make sure you have the latest version of node.js and npm installed. +23 error If you do, this is most likely a problem with the electron-react-quick-start package, +23 error not with npm itself. +23 error Tell the author that this fails on your system: +23 error concurrently -k "npm run devWebpack" "npm run devApp" "npm run devServer" +23 error You can get information on how to open an issue for this project with: +23 error npm bugs electron-react-quick-start +23 error Or if that isn't available, you can get their info via: +23 error npm owner ls electron-react-quick-start +23 error There is likely additional logging output above. +24 verbose exit [ 1, true ] diff --git a/package.json b/package.json index 4a4ccca34d..dce184f693 100644 --- a/package.json +++ b/package.json @@ -19,22 +19,42 @@ "license": "", "devDependencies": { "babel-core": "^6.25.0", + "babel-eslint": "^6.1.0", "babel-loader": "^7.1.1", "babel-preset-es2015": "^6.24.1", "babel-preset-react": "^6.24.1", "concurrently": "^3.5.0", + "css-loader": "^0.28.7", "electron": "~1.6.2", + "eslint": "^3.0.1", + "eslint-plugin-react": "^5.2.2", "loglevel": "^1.4.1", "nodemon": "^1.11.0", + "style-loader": "^0.19.0", "webpack": "^2.7.0", - "webpack-dev-server": "^2.6.0", - "babel-eslint": "^6.1.0", - "eslint": "^3.0.1", - "eslint-plugin-react": "^5.2.2" + "webpack-dev-server": "^2.6.0" }, "dependencies": { - "express": "^4.15.3", + "body-parser": "^1.18.2", + "connect-mongo": "^2.0.0", + "draft-js": "^0.10.4", + "draft-js-alignment-plugin": "^2.0.0-beta1", + "draft-js-custom-styles": "^1.3.1", + "draft-js-export-html": "^1.2.0", + "draft-js-plugins-editor": "^2.0.0-beta1", + "draft-js-raw-content-state": "^1.0.7", + "express": "^4.16.2", + "express-session": "^1.15.6", + "immutable": "^3.8.2", + "material-ui": "^0.19.4", + "mongoose": "^4.12.5", + "passport": "^0.4.0", + "passport-local": "^1.0.0", + "path": "^0.12.7", "react": "^15.6.1", - "react-dom": "^15.6.1" + "react-color": "^2.13.8", + "react-dom": "^15.6.1", + "react-router": "^4.2.0", + "react-router-dom": "^4.2.2" } } diff --git a/reactApp/app.js b/reactApp/app.js deleted file mode 100644 index 33a4922e61..0000000000 --- a/reactApp/app.js +++ /dev/null @@ -1,11 +0,0 @@ -var React = require('react'); -var ReactDOM = require('react-dom'); - -/* This can check if your electron app can communicate with your backend */ -// fetch('http://localhost:3000') -// .then(resp => resp.text()) -// .then(text => console.log(text)) -// .catch(err => {throw err}) - -ReactDOM.render(

React lives!

, - document.getElementById('root')); diff --git a/reactApp/components/Docs.js b/reactApp/components/Docs.js new file mode 100644 index 0000000000..0f7b1c1f51 --- /dev/null +++ b/reactApp/components/Docs.js @@ -0,0 +1,189 @@ +import React from 'react'; +import { HashRouter, Link } from 'react-router-dom'; +import axios from 'axios'; +import Modal from 'react-modal'; + +const customStyles = { + content : { + top : '50%', + left : '50%', + right : 'auto', + bottom : 'auto', + marginRight : '-50%', + transform : 'translate(-50%, -50%)' + } +}; + +class Docs extends React.Component { + constructor(props) { + super(props); + + this.state = { + docs: [], + docid: '', + docidpass: '', + name: '', + password: '', + modalIsOpen: false + }; + + this.openModal = this.openModal.bind(this); + this.afterOpenModal = this.afterOpenModal.bind(this); + this.closeModal = this.closeModal.bind(this); + + } + + handleNameChange(event) { + this.setState({ + name: event.target.value, + }) + } + + handlePasswordChange(event) { + this.setState({ + password: event.target.value + }) + } + + handleSearchChange(event) { + this.setState({ + docid: event.target.value, + }) + } + + handleSearchPassChange(event) { + this.setState({ + docidpass: event.target.value, + }) + } + + addDocument() { + console.log('merp') + event.preventDefault(); + axios.post('http://localhost:3000/docs', + { title: this.state.name, + password: this.state.password }, + { withCredentials: true }) + // .then(response => { + // return response.json() + // }) + .then((respJson) => { + if(respJson.data.success === true) { + console.log('added document ') + // var newDoc = { + // name: this.state.name, + // password: this.state.password + // } + var docsCopy = this.state.docs.slice(); + docsCopy.push(respJson.data.doc) + this.setState({ + docs: docsCopy, + name: '', + password: '' + }) + this.closeModal(); + } else { + console.log('error', respJson.data.error) + this.setState({ + name: '', + password: '' + }) + } + }) + .catch((err) => console.log(err)) + } + + searchDoc() { + axios.post(`http://localhost:3000/searchDoc/${this.state.docid}`, { + password: this.state.docidpass + }) + .then((resp) => { + var docsCopy = this.state.docs.slice(); + console.log('result', resp.data.result) + docsCopy.push(resp.data.result) + if (resp.data.success === true) { + this.setState({ + docs: docsCopy, + docid: '', + docidpass: '' + }) + } else { + this.setState({ + docid: '', + docidpass: '' + }) + } + }) + } + + componentDidMount() { + axios.get('http://localhost:3000/getDocs') + // .then(resp => resp.json() + .then(docs => { + console.log(docs) + this.setState({ docs: docs.data }) + }) + .catch(err => console.log(err)); + } + + openModal() { + this.setState({modalIsOpen: true}); + } + + afterOpenModal() { + // references are now sync'd and can be accessed. + this.subtitle.style.color = '#f00'; + } + + closeModal() { + this.setState({modalIsOpen: false}); + } + + render() { + console.log(this.state.docs, 'docs'); + return ( + // +
+

Documents Portal

+
+ + + + +

this.subtitle = subtitle}>Create a New Document

+
+ Title: this.handleNameChange(event)} type="text" value={this.state.name} />
+ Password: this.handlePasswordChange(event)} type="password" value={this.state.password} />

+ +
+
+
+

+

My Documents

+
    + {this.state.docs.map(doc => ( +
    + {doc.title} +
    ))} +
+
+
+

Add a Document By Its ID

+ Document ID: this.handleSearchChange(event)} value={this.state.docid}/>
+ Document Password: this.handleSearchPassChange(event)} value={this.state.docidpass}/>
+

+
+ Logout +
+ //
+ ); + } +} + +export default Docs; diff --git a/reactApp/components/Login.js b/reactApp/components/Login.js new file mode 100644 index 0000000000..c731795206 --- /dev/null +++ b/reactApp/components/Login.js @@ -0,0 +1,71 @@ +import React from 'react'; +import { HashRouter, Link } from 'react-router-dom'; +import axios from 'axios'; +import Docs from './Docs' + +class Login extends React.Component { + constructor(props) { + super(props); + this.state = { + username: '', + password: '', + }; + + this.handleChange = this.handleChange.bind(this); + this.handleSubmit = this.handleSubmit.bind(this); + } + + handleChange(event) { + const target = event.target; + const value = target.value; + const name = target.name; + this.setState({ + [name]: value + }); + } + + handleSubmit(event) { + event.preventDefault(); + axios.post('http://localhost:3000/login', { + username: this.state.username, + password: this.state.password, + }, { + withCredentials: true + }) + // .then(response => { + // return response.json() + // }) + .then((resp) => { + console.log('handlesubmit resp', resp) + if(resp.data.success === true) { + this.props.history.push('/docs'); + + } else { + console.log('no user') + alert('Cannot find user') + } + }) + } + + render() { + return ( + // +
+

Login

+
+
+
+ Username:
+ Password:
+
+

+ Register +
+
+
+ //
s + ); + } +} + +export default Login; diff --git a/reactApp/components/MainComponent.js b/reactApp/components/MainComponent.js new file mode 100644 index 0000000000..35d3d32682 --- /dev/null +++ b/reactApp/components/MainComponent.js @@ -0,0 +1,333 @@ +import React from 'react'; +import { + Editor, + EditorState, + RichUtils, + DefaultDraftBlockRenderMap, + convertToRaw, + convertFromRaw +} from 'draft-js'; +import { Link } from 'react-router-dom'; +import * as colors from 'material-ui/styles/colors'; +import AppBar from 'material-ui/AppBar'; +import FontIcon from 'material-ui/FontIcon'; +import RaisedButton from 'material-ui/RaisedButton'; +import { CirclePicker } from 'react-color'; +import Popover from 'material-ui/Popover'; +import { Map } from 'immutable'; +import Register from './Register'; +import axios from 'axios'; +import Modal from 'react-modal'; +const myBlockTypes = DefaultDraftBlockRenderMap.merge(new Map({ + center: { + wrapper:
+ }, + right: { + wrapper:
+ } +})); + +const customStyles = { + content : { + top : '50%', + left : '50%', + right : 'auto', + bottom : 'auto', + marginRight : '-50%', + transform : 'translate(-50%, -50%)' + } +}; + +class Main extends React.Component { + constructor(props) { + super(props); + + this.state = { + editorState: EditorState.createEmpty(), + currentFontSize: 12, + title: '', + owner: '', + docid: '', + inlineStyles: {}, + addUser: '', + collaborators: [] + }; + + this.openModal = this.openModal.bind(this); + this.afterOpenModal = this.afterOpenModal.bind(this); + this.closeModal = this.closeModal.bind(this); + + } + + onChange(editorState) { + this.setState({ + editorState + }); + } + + toggleFormat(e, style, block) { + e.preventDefault(); + if (block) { + this.setState({ + editorState: RichUtils.toggleBlockType(this.state.editorState, style) + }); + } else { + this.setState({ + editorState: RichUtils.toggleInlineStyle(this.state.editorState, style) + }); + } + } + + formatButton({icon, style, block}) { + return ( + this.toggleFormat(e, style, block)} + icon={{icon}} + /> + ); + } + + formatColor(color) { + //create a style for the color + //use RichUtils to apply the new style + var newInlineStyles = Object.assign( + {}, + this.state.inlineStyles, + { + [color.hex]: { + color: color.hex + } + }); + + this.setState({ + inlineStyles: newInlineStyles, + editorState: RichUtils.toggleInlineStyle(this.state.editorState, color.hex) + }); + } + + openColorPicker(e) { + this.setState({ + colorPickerOpen: true, + colorPickerButton: e.target + }); + } + + closeColorPicker() { + this.setState({ + colorPickerOpen: false + }); + } + + colorPicker() { + return ( +
+ format_paint} + onClick={this.openColorPicker.bind(this)} + /> + + + +
+ ); + } + + applyIncreaseFontSize(e, shrink) { + e.preventDefault(); + var newFontSize = this.state.currentFontSize + (shrink ? -4 : 4); + var newInlineStyles = Object.assign( + {}, + this.state.inlineStyles, + { + [newFontSize]: { + fontSize: `${newFontSize}px` + } + } + ); + console.log(newInlineStyles, "styles"); + this.setState({ + inlineStyles: newInlineStyles, + editorState: RichUtils.toggleInlineStyle(this.state.editorState, String(newFontSize)), + currentFontSize: newFontSize + }); + } + + increaseFontSize(shrink) { + return ( + this.applyIncreaseFontSize(e, shrink)} + icon={{shrink ? 'remove_circle' : 'add_box'}} + /> + ); + } + + saveDoc() { + const contentState = this.state.editorState.getCurrentContent(); + const inlineState = this.state.inlineStyles + console.log('inlinestate', inlineState) + // console.log('contentState', contentState) + const rawContentState = convertToRaw(contentState); + // console.log('rawContentState', rawContentState) + const stringContent = JSON.stringify(rawContentState); + // console.log('stringContent', stringContent) + + axios.post(`http://localhost:3000/saveDoc/${this.props.match.params.docid}`, { + body: stringContent, + inlineStyles: inlineState + }) + .then((resp) => { + console.log(resp.data); + }) + } + + componentDidMount() { + axios.get(`http://localhost:3000/getDoc/${this.props.match.params.docid}`) + .then((resp) => { + const doc = resp.data; + if(doc.body) { + const rawContentState = JSON.parse(doc.body); + const contentState = convertFromRaw(rawContentState); + const newEditorState = EditorState.createWithContent(contentState); + + this.setState({ + title: doc.title, + owner: doc.owner, + docid: doc._id, + editorState: newEditorState, + inlineStyles: doc.inlineStyles, + collaborators: doc.collaborators + }) + } else { + this.setState({ + title: doc.title, + owner: doc.owner, + docid: doc._id, + collaborators: doc.collaborators + }) + } + }) + } + + openModal() { + this.setState({modalIsOpen: true}); + } + + afterOpenModal() { + // references are now sync'd and can be accessed. + this.subtitle.style.color = '#f00'; + } + + closeModal() { + this.setState({modalIsOpen: false}); + } + + handleNameChange(event) { + this.setState({ + addUser: event.target.value + }) + } + + addCollaborator() { + console.log(this.state.addUser, 'adduser'); + axios.post(`http://localhost:3000/addCollab/${this.state.docid}`, + { username: this.state.addUser }) + .then((resp) => { + console.log(resp); + if(resp.data.success === true && resp.data.result.nModified !== 0) { + this.setState({ + collaborators: [...this.state.collaborators, this.state.addUser], + addUser: '' + }) + } + }); + this.closeModal(); + } + + // removeCollaborator() { + // axios.post(`http://localhost:3000/removeCollab/${this.state.docid}`, + // { collaborator: this.state.addUser }) + // .then((resp) => { + // console.log(resp); + // console.log('first', this.state.collaborators) + // var index = this.state.collaborators.indexOf(this.state.addUser); + // console.log(index) + // var collabs = this.state.collaborators.slice().splice(index, 1) + // console.log(collabs) + // if(resp.data.success === true) { + // this.setState({ + // collaborators: collabs, + // addUser: '' + // }) + // } + // console.log('second', this.state.collaborators) + // }); + // this.closeModal(); + // } + + render() { + return ( +
+ +

Shareable ID: {this.props.match.params.docid}

+

Owner: {this.state.owner}

+

Collaborators: {this.state.collaborators.join(', ')} +

+
+ Return to Doc Portal

+ + + +

this.subtitle = subtitle}>Edit Collaborators

+
+ Collaborators: this.handleNameChange(event)} type="text" value={this.state.addUser} />
+ + {/* */} +
+
+
+ +
+ {this.formatButton({icon: 'format_bold', style: 'BOLD' })} + {this.formatButton({icon: 'format_italic', style: 'ITALIC' })} + {this.formatButton({icon: 'format_underlined', style: 'UNDERLINE' })} + {this.colorPicker()} + {this.formatButton({icon: 'format_list_numbered', style: 'ordered-list-item', block: true })} + {this.formatButton({icon: 'format_list_bulleted', style: 'unordered-list-item', block: true })} + {this.formatButton({icon: 'format_align_left', style: 'unstyled', block: true })} + {this.formatButton({icon: 'format_align_center', style: 'center', block: true })} + {this.formatButton({icon: 'format_align_right', style: 'right', block: true })} + {this.increaseFontSize(false)} + {this.increaseFontSize(true)} +

+ +
+ ); + } +} + +export default Main; diff --git a/reactApp/components/Register.js b/reactApp/components/Register.js new file mode 100644 index 0000000000..bee035a38b --- /dev/null +++ b/reactApp/components/Register.js @@ -0,0 +1,80 @@ +import React from 'react'; +import { Route, HashRouter, Link } from 'react-router-dom' +import Login from './Login' +import axios from 'axios' + +class Register extends React.Component { + constructor(props) { + super(props); + this.state = { + username: '', + password: '', + firstName: '', + lastName: '' + }; + + this.handleChange = this.handleChange.bind(this); + this.handleSubmit = this.handleSubmit.bind(this); + } + + handleChange(event) { + const target = event.target; + const value = target.value; + const name = target.name; + this.setState({ + [name]: value + }); + } + + handleSubmit(event) { + event.preventDefault(); + axios.post('http://localhost:3000/register', { + username: this.state.username, + password: this.state.password, + firstName: this.state.firstName, + lastName: this.state.lastName, + }, { + withCredentials: true + }) + .then((resp) => { + if(resp.data.success === true) { + this.props.history.push('/login'); + } else { + console.log('error', resp.data.error) + this.setState({ + username: '', + password: '', + firstName: '', + lastName: '' + }) + } + }) + .catch((err) => console.log(err)) + } + + render() { + return ( + // +
+

Register

+
+
+
+ Username:
+ Password:
+ First Name:
+ Last Name:

+ + {/* */} + + Login +
+
+
+ //
+ ); + } +} + + +export default Register; diff --git a/reactApp/components/app.js b/reactApp/components/app.js new file mode 100644 index 0000000000..1a074c5080 --- /dev/null +++ b/reactApp/components/app.js @@ -0,0 +1,25 @@ +var React = require('react'); +var ReactDOM = require('react-dom'); +import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'; +import Register from './Register'; +import Main from './MainComponent'; +import Login from './Login' +import Docs from './Docs' +import ModalApp from './Modal' +import { Router, HashRouter, Route, Switch } from 'react-router-dom'; +import { render } from 'react-dom' + +require('../css/main.css'); + +const Root = () => + + + + + + + + + +ReactDOM.render(, + document.getElementById('root')); diff --git a/reactApp/css/main.css b/reactApp/css/main.css new file mode 100644 index 0000000000..4ba3a46efb --- /dev/null +++ b/reactApp/css/main.css @@ -0,0 +1,20 @@ +h1 { + color: red; +} + +.toolbar { + border-bottom: 2px solid gray; + padding: 10px; +} + +.toolbar > * { + margin-right: 5px; +} + +.center-align { + text-align: center; +} + +.right-align { + text-align: right; +} diff --git a/reactApp/models.js b/reactApp/models.js new file mode 100644 index 0000000000..8af9fd1552 --- /dev/null +++ b/reactApp/models.js @@ -0,0 +1,50 @@ +// YOUR CODE HERE +var mongoose = require('mongoose'); + +var userSchema = mongoose.Schema({ + username: { + type: String, + required: true, + unique: true + }, + password: { + type: String, + required: true + }, + firstName: String, + lastName: String +}); + +var documentSchema = mongoose.Schema({ + title: { + type: String, + required: true, + unique: true + }, + ownerid: { + type: mongoose.Schema.Types.ObjectId, + ref: 'User' + }, + owner: { + type: String + }, + collaborators: { + type: Array, + required: false + }, + password: { + type: String, + required: true + }, + body: { + type: String + }, + inlineStyles: { + type: Object + } +}); + +module.exports = { + User: mongoose.model('User', userSchema), + Document: mongoose.model('Document', documentSchema) +}; diff --git a/reactApp/oldFiles/app2.js b/reactApp/oldFiles/app2.js new file mode 100644 index 0000000000..ce06bc68f1 --- /dev/null +++ b/reactApp/oldFiles/app2.js @@ -0,0 +1,71 @@ +var React = require('react'); +var ReactDOM = require('react-dom'); +// import {Editor, EditorState, RichUtils} from 'draft-js'; +// import { ColorEditor, StyleButton } from './colorEditor'; + +/* This can check if your electron app can communicate with your backend */ +// fetch('http://localhost:3000') +// .then(resp => resp.text()) +// .then(text => console.log(text)) +// .catch(err => {throw err}) + +require('./css/main.css'); + +class MyEditor extends React.Component { + constructor(props) { + super(props); + this.state = {editorState: EditorState.createEmpty()}; + this.onChange = (editorState) => this.setState({editorState}); + this.handleKeyCommand = this.handleKeyCommand.bind(this); + } + handleKeyCommand(command, editorState) { + const newState = RichUtils.handleKeyCommand(editorState, command); + if (newState) { + this.onChange(newState); + return 'handled'; + } + return 'not-handled'; + } + + _onBoldClick() { + this.onChange(RichUtils.toggleInlineStyle(this.state.editorState, 'BOLD')); + } + + _onItalClick() { + this.onChange(RichUtils.toggleInlineStyle(this.state.editorState, 'ITALIC')); + } + + _onUndClick() { + this.onChange(RichUtils.toggleInlineStyle(this.state.editorState, 'UNDERLINE')); + } + + _onCodeClick() { + this.onChange(RichUtils.toggleInlineStyle(this.state.editorState, 'CODE')); + } + + render() { + return ( +
+

DOCUMENT TITLE

+
+
+ + + + +
+
+ + + +
+ ); + } +} + +// ReactDOM.render(, +// document.getElementById('root')); diff --git a/reactApp/oldFiles/app3.js b/reactApp/oldFiles/app3.js new file mode 100644 index 0000000000..04bd37a66f --- /dev/null +++ b/reactApp/oldFiles/app3.js @@ -0,0 +1,345 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import { Editor, convertToRaw, EditorState, RichUtils } from 'draft-js'; +import { stateToHTML } from 'draft-js-export-html'; +import Raw from 'draft-js-raw-content-state'; +import createStyles from 'draft-js-custom-styles'; + +const customStyleMap = { + MARK: { + backgroundColor: 'Yellow', + fontStyle: 'italic', + }, +}; + +// Passing the customStyleMap is optional +const { styles, customStyleFn, exporter } = createStyles(['font-size', 'color', 'text-transform'], 'CUSTOM_', customStyleMap); + +class RichEditor extends React.Component { + constructor(props) { + super(props); + this.state = { + editorState: new Raw().addBlock('Hello World', 'header-two').toEditorState(), + readOnly: false, + }; + this.updateEditorState = editorState => this.setState({ editorState }); + this.handleKeyCommand = this.handleKeyCommand.bind(this); + } + + handleKeyCommand(command, editorState) { + const newState = RichUtils.handleKeyCommand(editorState, command); + if (newState) { + this.updateEditorState(newState); + return 'handled'; + } + return 'not-handled'; + } + + _onBoldClick() { + this.updateEditorState(RichUtils.toggleInlineStyle(this.state.editorState, 'BOLD')); + } + + _onItalClick() { + this.updateEditorState(RichUtils.toggleInlineStyle(this.state.editorState, 'ITALIC')); + } + + _onUndClick() { + this.updateEditorState(RichUtils.toggleInlineStyle(this.state.editorState, 'UNDERLINE')); + } + + _onCodeClick() { + this.updateEditorState(RichUtils.toggleInlineStyle(this.state.editorState, 'CODE')); + } + + _onLeftClick() { + this.updateEditorState(RichUtils.toggleInlineStyle(this.state.editorState, 'LEFT')); + } + + _onRightClick() { + this.updateEditorState(RichUtils.toggleInlineStyle(this.state.editorState, 'RIGHT')); + } + + _onCenterClick() { + this.updateEditorState(RichUtils.toggleInlineStyle(this.state.editorState, 'CENTER')); + } + + toggleFontSize(fontSize) { + const newEditorState = styles.fontSize.toggle(this.state.editorState, fontSize); + return this.updateEditorState(newEditorState); + } + + toggleColor(color) { + const newEditorState = styles.color.toggle(this.state.editorState, color); + return this.updateEditorState(newEditorState); + } + + render() { + const { editorState } = this.state; + const inlineStyles = exporter(this.state.editorState); + const html = stateToHTML(this.state.editorState.getCurrentContent(), { inlineStyles }); + const options = x => x.map(fontSize => { + return ; + }); + return ( +
+
+ + + +
+ +
+ + + +
+
+

Draft-JS Editor

+ +
+ {/*
+

Exported To HTML

+
+
*/} + {/*
+

ContentState

+
+
+              {JSON.stringify(convertToRaw(this.state.editorState.getCurrentContent()), null, 2)}
+            
+
+
*/} +
+ ); + } +} + +ReactDOM.render(, + document.getElementById('root')); + +// var React = require('react'); +// var ReactDOM = require('react-dom'); +// import {Editor, EditorState, Modifier, RichUtils} from 'draft-js'; +// +// /* This can check if your electron app can communicate with your backend */ +// // fetch('http://localhost:3000') +// // .then(resp => resp.text()) +// // .then(text => console.log(text)) +// // .catch(err => {throw err}) +// +// class MyEditor extends React.Component { +// constructor(props) { +// super(props); +// this.state = {editorState: EditorState.createEmpty()}; +// this.focus = () => this.editor.focus(); +// this.onChange = (editorState) => this.setState({editorState}); +// this.handleKeyCommand = this.handleKeyCommand.bind(this); +// this.toggleColor = (toggledColor) => this._toggleColor(toggledColor); +// } +// +// handleKeyCommand(command, editorState) { +// const newState = RichUtils.handleKeyCommand(editorState, command); +// if (newState) { +// this.onChange(newState); +// return 'handled'; +// } +// return 'not-handled'; +// } +// +// _onBoldClick() { +// this.onChange(RichUtils.toggleInlineStyle(this.state.editorState, 'BOLD')); +// } +// +// _onItalClick() { +// this.onChange(RichUtils.toggleInlineStyle(this.state.editorState, 'ITALIC')); +// } +// +// _onUndClick() { +// this.onChange(RichUtils.toggleInlineStyle(this.state.editorState, 'UNDERLINE')); +// } +// +// _onCodeClick() { +// this.onChange(RichUtils.toggleInlineStyle(this.state.editorState, 'CODE')); +// } +// +// _toggleColor(toggledColor) { +// const {editorState} = this.state; +// const selection = editorState.getSelection(); +// // Let's just allow one color at a time. Turn off all active colors. +// const nextContentState = Object.keys(colorStyleMap) +// .reduce((contentState, color) => { +// return Modifier.removeInlineStyle(contentState, selection, color) +// }, editorState.getCurrentContent()); +// let nextEditorState = EditorState.push( +// editorState, +// nextContentState, +// 'change-inline-style' +// ); +// const currentStyle = editorState.getCurrentInlineStyle(); +// // Unset style override for current color. +// if (selection.isCollapsed()) { +// nextEditorState = currentStyle.reduce((state, color) => { +// return RichUtils.toggleInlineStyle(state, color); +// }, nextEditorState); +// } +// // If the color is being toggled on, apply it. +// if (!currentStyle.has(toggledColor)) { +// nextEditorState = RichUtils.toggleInlineStyle( +// nextEditorState, +// toggledColor +// ); +// } +// this.onChange(nextEditorState); +// } +// +// render() { +// const {editorState} = this.state; +// return ( +//
+//

DOCUMENT TITLE

+//
+//
+// +// +// +// +//
+//

+// +//
+// +// this.editor = ref} +// /> +//
+//
+// ); +// } +// } +// +// class StyleButton extends React.Component { +// constructor(props) { +// super(props); +// this.onToggle = (e) => { +// e.preventDefault(); +// this.props.onToggle(this.props.style); +// }; +// } +// +// render() { +// let style; +// if (this.props.active) { +// style = colorStyleMap[this.props.style]; +// } else { +// style = styles.styleButton; +// } +// return ( +// +// {this.props.label} +// +// ); +// } +// } +// +// var COLORS = [ +// {label: 'Red', style: 'red'}, +// {label: 'Orange', style: 'orange'}, +// {label: 'Yellow', style: 'yellow'}, +// {label: 'Green', style: 'green'}, +// {label: 'Blue', style: 'blue'}, +// {label: 'Indigo', style: 'indigo'}, +// {label: 'Violet', style: 'violet'}, +// ]; +// const ColorControls = (props) => { +// var currentStyle = props.editorState.getCurrentInlineStyle(); +// return ( +//
+// {COLORS.map(type => +// +// )} +//
+// ); +// }; +// // This object provides the styling information for our custom color +// // styles. +// const colorStyleMap = { +// red: { +// color: 'rgba(255, 0, 0, 1.0)', +// }, +// orange: { +// color: 'rgba(255, 127, 0, 1.0)', +// }, +// yellow: { +// color: 'rgba(180, 180, 0, 1.0)', +// }, +// green: { +// color: 'rgba(0, 180, 0, 1.0)', +// }, +// blue: { +// color: 'rgba(0, 0, 255, 1.0)', +// }, +// indigo: { +// color: 'rgba(75, 0, 130, 1.0)', +// }, +// violet: { +// color: 'rgba(127, 0, 255, 1.0)', +// }, +// }; +// const styles = { +// root: { +// fontFamily: '\'Georgia\', serif', +// fontSize: 14, +// padding: 20, +// width: 600, +// }, +// editor: { +// borderTop: '1px solid #ddd', +// cursor: 'text', +// fontSize: 16, +// marginTop: 20, +// minHeight: 400, +// paddingTop: 20, +// }, +// controls: { +// fontFamily: '\'Helvetica\', sans-serif', +// fontSize: 14, +// marginBottom: 10, +// userSelect: 'none', +// }, +// styleButton: { +// color: '#999', +// cursor: 'pointer', +// marginRight: 16, +// padding: '2px 0', +// }, +// }; +// +// ReactDOM.render(, +// document.getElementById('root')); diff --git a/webpack.config.js b/webpack.config.js index 6da65c11ce..be8af061c2 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -1,7 +1,7 @@ const webpack = require('webpack'); module.exports = { - entry: './reactApp/app.js', + entry: './reactApp/components/app.js', output: { path: __dirname + '/build', filename: 'app.bundle.js' @@ -17,6 +17,10 @@ module.exports = { presets: ['react', 'es2015'] } } + }, + { + test: /\.css$/, + use: ['style-loader', 'css-loader'] } ] },