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
+
+
+
+
+
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 (
+ //
+
+ // 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 (
+ //
+
+ //
+ );
+ }
+}
+
+
+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
+
+
+ {/*
*/}
+ {/*
+
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']
}
]
},