From 981056cc47534845c820804d59ebbab691f3fa30 Mon Sep 17 00:00:00 2001 From: jeff Date: Thu, 9 Nov 2017 14:36:10 -0800 Subject: [PATCH 01/19] commit b4 merge w rob --- .eslintrc | 2 +- backend/auth.js | 77 ++++++++++++++++++++++++++++++++++ backend/models.js | 59 ++++++++++++++++++++++++++ backend/routes.js | 8 ++-- backend/sync.js | 13 ++++++ env.sh | 2 + frontend/components/NewPost.js | 50 ++++++++++++++++++++++ package.json | 8 ++++ server.js | 17 ++++++-- 9 files changed, 227 insertions(+), 9 deletions(-) create mode 100644 backend/auth.js create mode 100644 backend/models.js create mode 100644 backend/sync.js create mode 100644 env.sh create mode 100644 frontend/components/NewPost.js diff --git a/.eslintrc b/.eslintrc index cd8434b..a9aeb0c 100644 --- a/.eslintrc +++ b/.eslintrc @@ -123,7 +123,7 @@ */ "indent": [ 2, - 4, + 2, { "SwitchCase": 1 } ], // http://eslint.org/docs/rules/indent "brace-style": [ diff --git a/backend/auth.js b/backend/auth.js new file mode 100644 index 0000000..28dc0fe --- /dev/null +++ b/backend/auth.js @@ -0,0 +1,77 @@ +const express = require('express'); +const router = express.Router(); +const {User} = require('./models'); + +router.post('/register', (req, res) => { + User.create({username: req.body.username, password: req.body.password}) + .then(user => { + res.json({user}); + }) + .catch(err => { + res.json({err}); + }); +}); + +const passport = require('passport'); +const LocalStrategy = require('passport-local').Strategy; + +passport.serializeUser((user, done) => { + done(null, user.id); +}); + +passport.deserializeUser((id, done) => { + User.findById(id) + .then(user => { + const doneUser = Object.assign({}, user.dataValues, {password: null}); + done(null, doneUser); + }) + .catch(err => { + done(err, null); + }); +}); + +passport.use(new LocalStrategy((username, password, done) => { + User.findOne({where: {username}}) + .then(user => { + if (!user) { + return done('no user found with that username', null); + } + if (user.password !== password) { + return done('passwords do not match', null); + } + return done(null, user); + }) + .catch(err => { + return done(err, null); + }); +})); + +router.use(passport.initialize()); +router.use(passport.session()); + +router.post('/login', passport.authenticate('local'), (req, res) => { + const resUser = Object.assign({}, req.user.dataValues, {password: null}); + res.json({user: resUser}); +}); + +router.get('/logout', (req, res) => { + req.logout(); + res.redirect('/'); +}); + +router.get('/:username', (req, res) => { + User.findOne({where: {username: req.params.username}}) + .then(user => { + const resUser = Object.assign({}, user.dataValues, {password: null}); + res.json({user: resUser}); + }) + .catch(err => { + res.json({err}); + }); +}); + +router.get('/', (req, res) => { + res.json({user: req.user}); +}); + +module.exports = router; diff --git a/backend/models.js b/backend/models.js new file mode 100644 index 0000000..3ec78eb --- /dev/null +++ b/backend/models.js @@ -0,0 +1,59 @@ +"use strict"; + +var Sequelize = require('sequelize'); +var sequelize = new Sequelize(process.env.DATABASE_NAME, 'postgres', process.env.DATABASE_PASSWORD, { + dialect: 'postgres' +}); + +sequelize +.authenticate() +.then(() => { + console.log('Connection has been established successfully.'); +}) +.catch(err => { + console.error('Unable to connect to the database:', err); +}); + +// MODELS GO HERE +const User = sequelize.define('user', { + id: { + type: Sequelize.INTEGER, + primaryKey: true, + autoIncrement: true + }, + username: { + type: Sequelize.STRING, + allowNull: false, + unique: true + }, + password: { + type: Sequelize.STRING, + allowNull: false + }, +}); + +const Post = sequelize.define('post', { + id: { + type: Sequelize.INTEGER, + primaryKey: true, + autoIncrement: true + }, + title: { + type: Sequelize.STRING, + allowNull: false, + }, + body: { + type: Sequelize.STRING, + }, + parentId: { + type: Sequelize.INTEGER,// can reference other posts. null if a parent post + }, +}); + +Post.belongsTo(User); + +module.exports = { + sequelize, + User, + Post, +}; diff --git a/backend/routes.js b/backend/routes.js index 9dd2215..1febdaa 100644 --- a/backend/routes.js +++ b/backend/routes.js @@ -1,11 +1,11 @@ const express = require('express'); const router = express.Router(); +const {User} = require('./models'); // YOUR API ROUTES HERE - -// SAMPLE ROUTE -router.use('/users', (req, res) => { - res.json({ success: true }); +router.post('/post/new', (req, res) => { + console.log(req.user); + // Post.create({userId: req.user }); module.exports = router; diff --git a/backend/sync.js b/backend/sync.js new file mode 100644 index 0000000..6d81270 --- /dev/null +++ b/backend/sync.js @@ -0,0 +1,13 @@ +"use strict"; + +var models = require('./models'); + +models.sequelize.sync({ force: true }) +.then(function() { + console.log('Successfully updated database tables!'); + process.exit(0); +}) +.catch(function(error) { + console.log('Error updating database tables', error); + process.exit(1); +}); diff --git a/env.sh b/env.sh new file mode 100644 index 0000000..6ac5e29 --- /dev/null +++ b/env.sh @@ -0,0 +1,2 @@ +export DATABASE_URL=postgresql://postgres@localhost/steemit +export DATABASE_NAME=steemit diff --git a/frontend/components/NewPost.js b/frontend/components/NewPost.js new file mode 100644 index 0000000..b55fadf --- /dev/null +++ b/frontend/components/NewPost.js @@ -0,0 +1,50 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import axios from 'axios'; +import {TextField, RaisedButton} from 'material-ui'; + +class NewPost extends React.Component { + constructor(props) { + super(props); + this.state = { + title: '', + body: '', + } + this.handleInputChange = this.handleInputChange.bind(this); + this.handleSubmitPost = this.handleSubmitPost.bind(this); + } + + handleInputChange(event) { + const target = event.target; + const value = target.type === 'checkbox' ? target.checked : target.value; + const name = target.name; + this.setState({ + [name]: value + }); + } + + handleSubmitPost() { + axios.post('/api/post/new', this.state) + .then(resp => { + console.log(resp); + // TODO reroute user to front page + }) + .catch(err => {console.log(err)}); + } + + render() { + return ( +
+ + +
+ +
+ +
+
+ ); + } +} + +export default NewPost; diff --git a/package.json b/package.json index 54019e8..30d6bb9 100644 --- a/package.json +++ b/package.json @@ -14,16 +14,23 @@ "author": "", "license": "ISC", "dependencies": { + "axios": "^0.17.0", "babel-core": "^6.24.1", "babel-loader": "^6.4.1", "babel-preset-es2015": "^6.24.1", "babel-preset-react": "^6.24.1", + "body-parser": "^1.18.2", + "cookie-session": "^2.0.0-beta.3", "css-loader": "^0.28.0", "enzyme": "^2.8.1", "expect": "^1.20.2", "express": "^4.15.2", "mocha": "^3.2.0", "node-sass": "^4.5.2", + "passport": "^0.4.0", + "passport-local": "^1.0.0", + "pg": "^7.4.0", + "pg-hstore": "^2.3.2", "prop-types": "^15.5.8", "react": "^15.5.4", "react-dom": "^15.5.4", @@ -33,6 +40,7 @@ "redux-devtools-dock-monitor": "^1.1.2", "redux-devtools-log-monitor": "^1.3.0", "sass-loader": "^6.0.3", + "sequelize": "^4.22.5", "style-loader": "^0.16.1", "webpack": "^2.3.3" }, diff --git a/server.js b/server.js index ac6e4f3..5bc6f9f 100644 --- a/server.js +++ b/server.js @@ -1,19 +1,28 @@ const path = require('path'); const express = require('express'); const app = express(); +const bodyParser = require('body-parser'); const PORT = process.env.PORT || 3000; +const auth = require('./backend/auth'); const api = require('./backend/routes'); +const cookieSession = require('cookie-session'); app.use(express.static(path.join(__dirname, 'public'))); +app.use(bodyParser.json()); +app.use(bodyParser.urlencoded({ extended: false })); +app.use(cookieSession({ + keys: [process.env.SECRET || 'super duper secret string'] +})); app.get('/', (request, response) => { - response.sendFile(__dirname + '/public/index.html'); // For React/Redux + response.sendFile(__dirname + '/public/index.html'); // For React/Redux }); +app.use('/api/user', auth); app.use('/api', api); app.listen(PORT, error => { - error - ? console.error(error) - : console.info(`==> 🌎 Listening on port ${PORT}. Visit http://localhost:${PORT}/ in your browser.`); + error + ? console.error(error) + : console.info(`==> 🌎 Listening on port ${PORT}. Visit http://localhost:${PORT}/ in your browser.`); }); From ac9709f016e58aaf42ca7dc98d15e569f32a5173 Mon Sep 17 00:00:00 2001 From: robertdurst Date: Thu, 9 Nov 2017 14:36:45 -0800 Subject: [PATCH 02/19] Designed front page with react and modal for login/register --- frontend/assets/stylesheets/appcontainer.css | 33 ++++ frontend/assets/stylesheets/feed.css | 12 ++ frontend/assets/stylesheets/header.css | 29 +++ frontend/assets/stylesheets/sidebar.css | 47 +++++ frontend/components/Feed.js | 16 ++ frontend/components/Header.js | 19 ++ frontend/components/Sidebar.js | 190 +++++++++++++++++++ frontend/containers/AppContainer.js | 20 +- frontend/containers/Root.dev.js | 17 +- frontend/index.js | 1 + package.json | 2 + 11 files changed, 377 insertions(+), 9 deletions(-) create mode 100644 frontend/assets/stylesheets/appcontainer.css create mode 100644 frontend/assets/stylesheets/feed.css create mode 100644 frontend/assets/stylesheets/header.css create mode 100644 frontend/assets/stylesheets/sidebar.css create mode 100644 frontend/components/Feed.js create mode 100644 frontend/components/Header.js create mode 100644 frontend/components/Sidebar.js diff --git a/frontend/assets/stylesheets/appcontainer.css b/frontend/assets/stylesheets/appcontainer.css new file mode 100644 index 0000000..abaa5c1 --- /dev/null +++ b/frontend/assets/stylesheets/appcontainer.css @@ -0,0 +1,33 @@ +html, body { + width: 100%; + height: 100%; + padding: 0; + margin: 0; + background-color: #BDBDBD; +} + +#root { + width: 100%; + height: 100%; +} + +.appcontainer_container { + display: flex; + width: 100%; + height: 100%; + flex-direction: column; +} + +.appcontainer_header_container { + display: flex; + width: 100%; + height: 75px; + flex: 1; +} + +.appcontainer_body_container { + display: flex; + width: 100%; + height: 100%; + flex: 10; +} diff --git a/frontend/assets/stylesheets/feed.css b/frontend/assets/stylesheets/feed.css new file mode 100644 index 0000000..4cb2874 --- /dev/null +++ b/frontend/assets/stylesheets/feed.css @@ -0,0 +1,12 @@ +.feed_container { + background-color: #BDBDBD; + width: 100%; + flex: 5; +} + +.feed_body { + height: 97%; + width: 96%; + margin: auto; + margin-top: 2%; +} diff --git a/frontend/assets/stylesheets/header.css b/frontend/assets/stylesheets/header.css new file mode 100644 index 0000000..a4d12b8 --- /dev/null +++ b/frontend/assets/stylesheets/header.css @@ -0,0 +1,29 @@ +.header_container { + width: 100%; + overflow: hidden; + flex: 1; + position: fixed; + height: 100px; +} + +.header_content { + height: 98px; + width: 100%; + display: flex; +} + +.header_container_logo { + max-height: 100%; + max-width: 30%; + width: auto; + height: auto; + margin-left: 5px; + padding: 10px +} + + .header_container_title{ + color: #06d6a8; + font-family: helvetica, sans-serif; + font-size: 40px; + font-weight: bold; + } diff --git a/frontend/assets/stylesheets/sidebar.css b/frontend/assets/stylesheets/sidebar.css new file mode 100644 index 0000000..ac07c9f --- /dev/null +++ b/frontend/assets/stylesheets/sidebar.css @@ -0,0 +1,47 @@ +.sidebar_container { + width: 100%; + flex: 1; + height: 100%; + margin-right: 10px; +} + +.sidebar_content { + width: 100%; + height: 30%; + margin-top: 10%; +} + +.submit_post_button { + width: 100%; +} + +.login_register_button_container { + margin-top: 10px; + display: flex; +} + +.login_button { + flex: 1; + margin-right: 5px; +} + +.register_button { + flex: 1; +} + +.description_right_container { + display: flex; + flex-direction: column; + margin-top: 10px; +} + +.right_sidebar_description_title { + flex: 1; + margin: 5px; +} + +.right_sidebar_description_content { + flex: 1; + margin-top: 0; + margin: 5px; +} diff --git a/frontend/components/Feed.js b/frontend/components/Feed.js new file mode 100644 index 0000000..2d0d2f5 --- /dev/null +++ b/frontend/components/Feed.js @@ -0,0 +1,16 @@ +import React from 'react'; +import styles from '../assets/stylesheets/feed.css' +import Paper from 'material-ui/Paper'; + +const Feed = () => { + return ( +
+ +
+ ); +}; + +export default Feed; diff --git a/frontend/components/Header.js b/frontend/components/Header.js new file mode 100644 index 0000000..a97f54f --- /dev/null +++ b/frontend/components/Header.js @@ -0,0 +1,19 @@ +import React from 'react'; +import styles from '../assets/stylesheets/header.css' +import Paper from 'material-ui/Paper'; + +const Header = () => { + return ( +
+ + +

steemit

+
+
+ ); +}; + +export default Header; diff --git a/frontend/components/Sidebar.js b/frontend/components/Sidebar.js new file mode 100644 index 0000000..588b194 --- /dev/null +++ b/frontend/components/Sidebar.js @@ -0,0 +1,190 @@ +import React from 'react'; +import styles from '../assets/stylesheets/sidebar.css' +import PropTypes from 'prop-types'; +import { RaisedButton, Paper, FlatButton, Dialog, TextField } from 'material-ui'; +import LoginIcon from 'material-ui/svg-icons/social/person-add'; +import RegisterIcon from 'material-ui/svg-icons/social/person'; +import SubmitPostIcon from 'material-ui/svg-icons/editor/border-color'; + +export default class Sidebar extends React.Component { + constructor(props) { + super(props); + this.state = { + open_login: false, + open_register: false, + login_pass: "", + login_user: "", + register_pass: "", + register_user: "", + } + } + + handleOpen_login(){ + this.setState({open_login: true}); + }; + + handleClose_login(){ + this.setState({open_login: false}); + this.setState({ + login_pass: "", + login_user: "" + }) + }; + + handleOpen_register(){ + this.setState({open_register: true}); + }; + + handleClose_register(){ + this.setState({open_register: false}); + this.setState({ + register_pass: "", + register_user: "" + }) + }; + + handleChangeUser_register(event){ + const target = event.target; + const value = target.value; + this.setState({ + register_user: value + }) + } + + handleChangeUser_login(event){ + const target = event.target; + const value = target.value; + console.log(value); + this.setState({ + login_user: value + }) + } + + handleChangePass_register(event){ + const target = event.target; + const value = target.value; + this.setState({ + register_pass: value + }) + } + + handleChangePass_login(event){ + const target = event.target; + const value = target.value; + this.setState({ + login_pass: value + }) + } + + render() { + const actions_login = [ + , + , + ]; + const actions_register = [ + , + , + ]; + if(this.props.side == "left") { + return ( +
+
+ ); + } else { + return ( +
+
+ } + label="Submit Post" + backgroundColor="black" + labelColor="white" + /> +
+ } + label="Login" + labelColor="#06d6a8" + onClick={this.handleOpen_login.bind(this)} + backgroundColor="white" + /> + } + label="Register" + labelColor="white" + onClick={this.handleOpen_register.bind(this)} + backgroundColor="#06d6a8" + /> +
+ +

+ Description +

+

+ This is the description of Steemit, an up and coming social media platform built on the Steem blockchain. Handling + hundreds of thousands of transactions a day with 3 second confirmmations and no fee's, this is a blockchain you don't want + to miss. +

+
+
+
+ +
+ +
+
+ +
+ +
+ +
+
+
+ ); + } + } +} diff --git a/frontend/containers/AppContainer.js b/frontend/containers/AppContainer.js index 7c29205..1dc3775 100644 --- a/frontend/containers/AppContainer.js +++ b/frontend/containers/AppContainer.js @@ -1,12 +1,26 @@ import PropTypes from 'prop-types'; import React from 'react'; import { connect } from 'react-redux'; -import Title from '../components/Title'; + +// Import Components +import Header from '../components/Header'; +import Feed from '../components/Feed'; +import Sidebar from '../components/Sidebar'; + +// Import Styling +import styles from '../assets/stylesheets/appcontainer.css' const AppContainer = ({ name }) => { return ( -
- + <div className="appcontainer_container"> + <div className="appcontainer_header_container"> + <Header /> + </div> + <div className="appcontainer_body_container"> + <Sidebar side={'left'}/> + <Feed /> + <Sidebar /> + </div> </div> ); }; diff --git a/frontend/containers/Root.dev.js b/frontend/containers/Root.dev.js index c0f8d69..ade3a7d 100644 --- a/frontend/containers/Root.dev.js +++ b/frontend/containers/Root.dev.js @@ -3,15 +3,20 @@ import React from 'react'; import {Provider} from 'react-redux'; import AppContainer from './AppContainer.js'; import DevTools from './DevTools'; +import { HashRouter, Route, Switch } from 'react-router-dom'; +import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'; export default function Root({ store }) { return ( - <Provider store={store}> - <div> - <AppContainer /> - <DevTools /> - </div> - </Provider> + <Provider store={store}> + <MuiThemeProvider> + <HashRouter> + <Switch> + <Route exact path='/' component={AppContainer}/> + </Switch> + </HashRouter> + </MuiThemeProvider> + </Provider> ); } diff --git a/frontend/index.js b/frontend/index.js index 411485c..0c07068 100644 --- a/frontend/index.js +++ b/frontend/index.js @@ -3,6 +3,7 @@ import { render } from 'react-dom'; import { configureStore, history } from './store/configureStore'; import Root from './containers/Root'; +// Import styles import './assets/stylesheets/base.scss'; const store = configureStore(); diff --git a/package.json b/package.json index 54019e8..0206b7b 100644 --- a/package.json +++ b/package.json @@ -22,12 +22,14 @@ "enzyme": "^2.8.1", "expect": "^1.20.2", "express": "^4.15.2", + "material-ui": "^0.19.4", "mocha": "^3.2.0", "node-sass": "^4.5.2", "prop-types": "^15.5.8", "react": "^15.5.4", "react-dom": "^15.5.4", "react-redux": "^5.0.5", + "react-router-dom": "^4.2.2", "redux": "^3.7.2", "redux-devtools": "^3.4.0", "redux-devtools-dock-monitor": "^1.1.2", From 3378818304b226ea22e402cb98512b5fc4ab794a Mon Sep 17 00:00:00 2001 From: robertdurst <rsdurst@colby.edu> Date: Thu, 9 Nov 2017 14:40:34 -0800 Subject: [PATCH 03/19] Changed login button look --- frontend/components/Sidebar.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/frontend/components/Sidebar.js b/frontend/components/Sidebar.js index 588b194..c84c511 100644 --- a/frontend/components/Sidebar.js +++ b/frontend/components/Sidebar.js @@ -2,8 +2,8 @@ import React from 'react'; import styles from '../assets/stylesheets/sidebar.css' import PropTypes from 'prop-types'; import { RaisedButton, Paper, FlatButton, Dialog, TextField } from 'material-ui'; -import LoginIcon from 'material-ui/svg-icons/social/person-add'; -import RegisterIcon from 'material-ui/svg-icons/social/person'; +import LoginIcon from 'material-ui/svg-icons/social/person'; +import RegisterIcon from 'material-ui/svg-icons/social/person-add'; import SubmitPostIcon from 'material-ui/svg-icons/editor/border-color'; export default class Sidebar extends React.Component { @@ -123,7 +123,6 @@ export default class Sidebar extends React.Component { <RaisedButton className="login_button" icon={<LoginIcon />} - label="Login" labelColor="#06d6a8" onClick={this.handleOpen_login.bind(this)} backgroundColor="white" @@ -131,7 +130,6 @@ export default class Sidebar extends React.Component { <RaisedButton className="register_button" icon={<RegisterIcon />} - label="Register" labelColor="white" onClick={this.handleOpen_register.bind(this)} backgroundColor="#06d6a8" From dcf042de6a2ad13ae12099d1ba074c515729f07b Mon Sep 17 00:00:00 2001 From: robertdurst <rsdurst@colby.edu> Date: Thu, 9 Nov 2017 15:02:14 -0800 Subject: [PATCH 04/19] Login testing --- frontend/components/Sidebar.js | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/frontend/components/Sidebar.js b/frontend/components/Sidebar.js index c84c511..e12531d 100644 --- a/frontend/components/Sidebar.js +++ b/frontend/components/Sidebar.js @@ -5,6 +5,7 @@ import { RaisedButton, Paper, FlatButton, Dialog, TextField } from 'material-ui' import LoginIcon from 'material-ui/svg-icons/social/person'; import RegisterIcon from 'material-ui/svg-icons/social/person-add'; import SubmitPostIcon from 'material-ui/svg-icons/editor/border-color'; +import axios from 'axios'; export default class Sidebar extends React.Component { constructor(props) { @@ -19,6 +20,29 @@ export default class Sidebar extends React.Component { } } + handleClickSubmit_login() { + axios({ + url: 'http://localhost:3000/api/user/login', + method: 'post', + data: { + username: this.state.login_user, + password: this.state.login_pass + } + }) + } + + handleClickSubmit_register() { + axios({ + url: 'http://localhost:3000/api/user/register', + method: 'post', + data: { + username: this.state.register_user, + password: this.state.register_pass + } + }).then( x => console.log(x)) + .catch( err => console.log(err)) + } + handleOpen_login(){ this.setState({open_login: true}); }; @@ -87,7 +111,7 @@ export default class Sidebar extends React.Component { label="Login" primary={true} disabled={!(this.state.login_pass && this.state.login_user)} - onClick={this.handleClose_login.bind(this)} + onClick={this.handleClickSubmit_login.bind(this)} />, ]; const actions_register = [ @@ -100,7 +124,7 @@ export default class Sidebar extends React.Component { label="Register" primary={true} disabled={!(this.state.register_pass && this.state.register_user)} - onClick={this.handleClose_register.bind(this)} + onClick={this.handleClickSubmit_register.bind(this)} />, ]; if(this.props.side == "left") { From 607c3cc99b42324d85610675db22e082dfd7ab67 Mon Sep 17 00:00:00 2001 From: jeff <tangj1122@gmail.com> Date: Thu, 9 Nov 2017 15:37:17 -0800 Subject: [PATCH 05/19] begin refactoring home --- frontend/components/Sidebar.js | 13 ++- frontend/components/Sidebar2.js | 130 ++++++++++++++++++++++++++++ frontend/containers/AppContainer.js | 5 +- 3 files changed, 142 insertions(+), 6 deletions(-) create mode 100644 frontend/components/Sidebar2.js diff --git a/frontend/components/Sidebar.js b/frontend/components/Sidebar.js index e12531d..d49fce0 100644 --- a/frontend/components/Sidebar.js +++ b/frontend/components/Sidebar.js @@ -28,7 +28,12 @@ export default class Sidebar extends React.Component { username: this.state.login_user, password: this.state.login_pass } + }).then((resp) => { + if (resp.data.user) { + // redirect / refresh page and show logged in view + } }) + .catch( err => console.log(err)) } handleClickSubmit_register() { @@ -45,7 +50,7 @@ export default class Sidebar extends React.Component { handleOpen_login(){ this.setState({open_login: true}); - }; + } handleClose_login(){ this.setState({open_login: false}); @@ -53,11 +58,11 @@ export default class Sidebar extends React.Component { login_pass: "", login_user: "" }) - }; + } handleOpen_register(){ this.setState({open_register: true}); - }; + } handleClose_register(){ this.setState({open_register: false}); @@ -65,7 +70,7 @@ export default class Sidebar extends React.Component { register_pass: "", register_user: "" }) - }; + } handleChangeUser_register(event){ const target = event.target; diff --git a/frontend/components/Sidebar2.js b/frontend/components/Sidebar2.js new file mode 100644 index 0000000..d79fb65 --- /dev/null +++ b/frontend/components/Sidebar2.js @@ -0,0 +1,130 @@ +import React from 'react'; +import styles from '../assets/stylesheets/sidebar.css' +import PropTypes from 'prop-types'; +import { RaisedButton, Paper, FlatButton, Dialog, TextField } from 'material-ui'; +import LoginIcon from 'material-ui/svg-icons/social/person'; +import RegisterIcon from 'material-ui/svg-icons/social/person-add'; +import SubmitPostIcon from 'material-ui/svg-icons/editor/border-color'; +import axios from 'axios'; +const baseUrl = 'http://localhost:3000/api/user'; + +export default class Sidebar2 extends React.Component { + constructor(props) { + super(props); + this.state = { + modalIsOpen: false, + modalType: null, + username: "", + password: "", + } + } + + handleSubmit() { + const endUrl = modalType === 'login' ? '/login' : 'register' ? '/register' : null; + axios({ + url: baseUrl + endUrl, + method: 'post', + data: { + username: this.state.username, + password: this.state.password + } + }) + } + + handleOpenModal(){ + this.setState({modalIsOpen: true}); + } + + handleCloseModal(){ + this.setState({modalIsOpen: false}); + } + + handleInputChange(event) { + const target = event.target; + const value = target.value; + const name = target.name; + this.setState({ + [name]: value, + }); + console.log(this.state); + } + + render() { + const actions_login = [ + <FlatButton + label="Cancel" + primary={true} + />, + <FlatButton + label="Submit" + primary={true} + />, + ]; + if (this.props.side == "left") { + return ( + <div className="sidebar_container"> + </div> + ); + } else { + return ( + <div className="sidebar_container"> + <div className="sidebar_content"> + <RaisedButton + className="submit_post_button" + icon={<SubmitPostIcon />} + label="Submit Post" + backgroundColor="black" + labelColor="white" + /> + <div className="login_register_button_container"> + <RaisedButton + className="login_button" + icon={<LoginIcon />} + labelColor="#06d6a8" + onClick={(e) => this.handleOpenModal.bind(this, e)} + backgroundColor="white" + /> + <RaisedButton + className="register_button" + icon={<RegisterIcon />} + labelColor="white" + onClick={(e) => { + console.log(e.target); + this.handleOpenModal.bind(this, e)} + } + backgroundColor="#06d6a8" + /> + </div> + <Paper className="description_right_container" zDepth={5}> + <h1 className="right_sidebar_description_title"> + Description + </h1> + <p className="right_sidebar_description_content"> + This is the description of Steemit, an up and coming social media platform built on the Steem blockchain. Handling + hundreds of thousands of transactions a day with 3 second confirmmations and no fee's, this is a blockchain you don't want + to miss. + </p> + </Paper> + </div> + <div> + <Dialog + title="Login" + actions={actions_login} + modal={true} + open={this.state.modalIsOpen} + > + <TextField + hintText="Username" + onChange={this.handleInputChange.bind(this)} + /> <br /> + <TextField + hintText="Password" + onChange={this.handleInputChange.bind(this)} + /> + </Dialog> + </div> + </div> + ); + } + } +} diff --git a/frontend/containers/AppContainer.js b/frontend/containers/AppContainer.js index 1dc3775..b59847f 100644 --- a/frontend/containers/AppContainer.js +++ b/frontend/containers/AppContainer.js @@ -6,6 +6,7 @@ import { connect } from 'react-redux'; import Header from '../components/Header'; import Feed from '../components/Feed'; import Sidebar from '../components/Sidebar'; +import Sidebar2 from '../components/Sidebar2'; // Import Styling import styles from '../assets/stylesheets/appcontainer.css' @@ -18,8 +19,8 @@ const AppContainer = ({ name }) => { </div> <div className="appcontainer_body_container"> <Sidebar side={'left'}/> - <Feed /> - <Sidebar /> + <Feed /> + <Sidebar2 /> </div> </div> ); From ce2cfa97ba1170269da682de746ca80e567b5c1b Mon Sep 17 00:00:00 2001 From: robertdurst <rsdurst@colby.edu> Date: Thu, 9 Nov 2017 16:32:44 -0800 Subject: [PATCH 06/19] getting the hash router to work --- backend/auth.js | 1 + backend/models.js | 2 +- frontend/assets/stylesheets/sidebar.css | 4 ++ frontend/components/NewPost.js | 13 +++- frontend/components/Sidebar.js | 88 +++++++++++++++++-------- frontend/containers/AppContainer.js | 5 +- frontend/containers/Root.dev.js | 11 ++-- frontend/store/configureStore.dev.js | 8 ++- package.json | 2 + 9 files changed, 96 insertions(+), 38 deletions(-) diff --git a/backend/auth.js b/backend/auth.js index 28dc0fe..48caec6 100644 --- a/backend/auth.js +++ b/backend/auth.js @@ -3,6 +3,7 @@ const router = express.Router(); const {User} = require('./models'); router.post('/register', (req, res) => { + console.log('process.env.DATABASE_NAME', process.env.DATABASE_NAME); User.create({username: req.body.username, password: req.body.password}) .then(user => { res.json({user}); diff --git a/backend/models.js b/backend/models.js index 3ec78eb..cf80e80 100644 --- a/backend/models.js +++ b/backend/models.js @@ -1,7 +1,7 @@ "use strict"; var Sequelize = require('sequelize'); -var sequelize = new Sequelize(process.env.DATABASE_NAME, 'postgres', process.env.DATABASE_PASSWORD, { +var sequelize = new Sequelize(process.env.DATABASE_NAME, 'postgres', null, { dialect: 'postgres' }); diff --git a/frontend/assets/stylesheets/sidebar.css b/frontend/assets/stylesheets/sidebar.css index ac07c9f..aaa8d6d 100644 --- a/frontend/assets/stylesheets/sidebar.css +++ b/frontend/assets/stylesheets/sidebar.css @@ -15,6 +15,10 @@ width: 100%; } +.logout_button { + width: 100%; +} + .login_register_button_container { margin-top: 10px; display: flex; diff --git a/frontend/components/NewPost.js b/frontend/components/NewPost.js index b55fadf..f895581 100644 --- a/frontend/components/NewPost.js +++ b/frontend/components/NewPost.js @@ -14,6 +14,10 @@ class NewPost extends React.Component { this.handleSubmitPost = this.handleSubmitPost.bind(this); } + componentWillReceiveProps() { + + } + handleInputChange(event) { const target = event.target; const value = target.type === 'checkbox' ? target.checked : target.value; @@ -33,15 +37,18 @@ class NewPost extends React.Component { } render() { + console.log("On the new post page."); return ( - <div className='NewPost-container'> - <TextField name='postTitle' onChange={this.handleInputChange} hintText='Post Title' /> + <div> + {/* <div className='NewPost-container'> */} + <h1>Hello World</h1> + {/* <TextField name='postTitle' onChange={this.handleInputChange} hintText='Post Title' /> <TextField name='postBody' onChange={this.handleInputChange} hintText='Post Body' multiLine={true} /> <br /> <RaisedButton label='Choose Attachments' secondary={true} /> <br /> <RaisedButton onClick={this.handleSubmitPost} label='Submit' primary={true} /> - <br /> + <br /> */} </div> ); } diff --git a/frontend/components/Sidebar.js b/frontend/components/Sidebar.js index d49fce0..8dbe730 100644 --- a/frontend/components/Sidebar.js +++ b/frontend/components/Sidebar.js @@ -17,9 +17,26 @@ export default class Sidebar extends React.Component { login_user: "", register_pass: "", register_user: "", + username: "", } } + handleClickSubmitPost() { + this.props.history.push('#/postnew'); + // window.location.hash = '/postnew' + } + + handleClickLogout() { + axios({ + url: "http://localhost:3000/api/user/logout", + method: "get" + }).then( () =>{ + this.setState({ + username: "" + }) + }) + } + handleClickSubmit_login() { axios({ url: 'http://localhost:3000/api/user/login', @@ -28,12 +45,12 @@ export default class Sidebar extends React.Component { username: this.state.login_user, password: this.state.login_pass } - }).then((resp) => { - if (resp.data.user) { - // redirect / refresh page and show logged in view - } + }).then(x => { + this.setState({ + username: x.data.user.username + }) + this.setState({open_login: false}); }) - .catch( err => console.log(err)) } handleClickSubmit_register() { @@ -44,13 +61,14 @@ export default class Sidebar extends React.Component { username: this.state.register_user, password: this.state.register_pass } - }).then( x => console.log(x)) - .catch( err => console.log(err)) + }).then( x => { + this.setState({open_register: false}); + }) } handleOpen_login(){ this.setState({open_login: true}); - } + }; handleClose_login(){ this.setState({open_login: false}); @@ -58,11 +76,11 @@ export default class Sidebar extends React.Component { login_pass: "", login_user: "" }) - } + }; handleOpen_register(){ this.setState({open_register: true}); - } + }; handleClose_register(){ this.setState({open_register: false}); @@ -70,7 +88,7 @@ export default class Sidebar extends React.Component { register_pass: "", register_user: "" }) - } + }; handleChangeUser_register(event){ const target = event.target; @@ -106,6 +124,7 @@ export default class Sidebar extends React.Component { } render() { + console.log(this.props); const actions_login = [ <FlatButton label="Cancel" @@ -147,23 +166,38 @@ export default class Sidebar extends React.Component { label="Submit Post" backgroundColor="black" labelColor="white" + onClick={this.handleClickSubmitPost.bind(this)} /> - <div className="login_register_button_container"> - <RaisedButton - className="login_button" - icon={<LoginIcon />} - labelColor="#06d6a8" - onClick={this.handleOpen_login.bind(this)} - backgroundColor="white" - /> - <RaisedButton - className="register_button" - icon={<RegisterIcon />} - labelColor="white" - onClick={this.handleOpen_register.bind(this)} - backgroundColor="#06d6a8" - /> - </div> + { + this.state.username ? + <div> + <h1>{this.state.username}</h1> + <RaisedButton + className="logout_button" + label="logout" + labelColor="#06d6a8" + onClick={this.handleClickLogout.bind(this)} + backgroundColor="white" + /> + </div> : + <div className="login_register_button_container"> + <RaisedButton + className="login_button" + icon={<LoginIcon />} + labelColor="#06d6a8" + onClick={this.handleOpen_login.bind(this)} + backgroundColor="white" + /> + <RaisedButton + className="register_button" + icon={<RegisterIcon />} + labelColor="white" + onClick={this.handleOpen_register.bind(this)} + backgroundColor="#06d6a8" + /> + </div> + } + <Paper className="description_right_container" zDepth={5}> <h1 className="right_sidebar_description_title"> Description diff --git a/frontend/containers/AppContainer.js b/frontend/containers/AppContainer.js index b59847f..332210c 100644 --- a/frontend/containers/AppContainer.js +++ b/frontend/containers/AppContainer.js @@ -11,7 +11,7 @@ import Sidebar2 from '../components/Sidebar2'; // Import Styling import styles from '../assets/stylesheets/appcontainer.css' -const AppContainer = ({ name }) => { +const AppContainer = ({ name, history }) => { return ( <div className="appcontainer_container"> <div className="appcontainer_header_container"> @@ -20,7 +20,7 @@ const AppContainer = ({ name }) => { <div className="appcontainer_body_container"> <Sidebar side={'left'}/> <Feed /> - <Sidebar2 /> + <Sidebar history={history}/> </div> </div> ); @@ -28,6 +28,7 @@ const AppContainer = ({ name }) => { AppContainer.propTypes = { name: PropTypes.string, + history: PropTypes.object }; const mapStateToProps = (state) => { diff --git a/frontend/containers/Root.dev.js b/frontend/containers/Root.dev.js index ade3a7d..13032e2 100644 --- a/frontend/containers/Root.dev.js +++ b/frontend/containers/Root.dev.js @@ -1,18 +1,20 @@ import PropTypes from 'prop-types'; import React from 'react'; import {Provider} from 'react-redux'; -import AppContainer from './AppContainer.js'; +import AppContainer from './AppContainer'; +import NewPost from '../components/NewPost' import DevTools from './DevTools'; import { HashRouter, Route, Switch } from 'react-router-dom'; import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'; -export default function Root({ store }) { +export default function Root({ store, history }) { return ( <Provider store={store}> <MuiThemeProvider> <HashRouter> <Switch> - <Route exact path='/' component={AppContainer}/> + <Route exact path='/' render={()=><AppContainer history={history}/>}/> + <Route exact path='/postnew' render={()=><NewPost history={history}/>}/> </Switch> </HashRouter> </MuiThemeProvider> @@ -21,5 +23,6 @@ export default function Root({ store }) { } Root.propTypes = { - store: PropTypes.object.isRequired + store: PropTypes.object.isRequired, + history: PropTypes.object.isRequired, }; diff --git a/frontend/store/configureStore.dev.js b/frontend/store/configureStore.dev.js index f87288c..24d7ae3 100644 --- a/frontend/store/configureStore.dev.js +++ b/frontend/store/configureStore.dev.js @@ -1,12 +1,18 @@ -import { createStore, compose } from 'redux'; +import createHistory from 'history/createBrowserHistory'; +import { applyMiddleware, createStore, compose } from 'redux'; +import { routerMiddleware } from 'react-router-redux'; import rootReducer from '../reducers'; import DevTools from '../containers/DevTools'; +export const history = createHistory(); +const middleware = routerMiddleware(history); + export function configureStore(initialState) { return createStore( rootReducer, initialState, compose( + applyMiddleware(middleware), DevTools.instrument() ) ); diff --git a/package.json b/package.json index eba6b75..245fe38 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "enzyme": "^2.8.1", "expect": "^1.20.2", "express": "^4.15.2", + "history": "^4.7.2", "material-ui": "^0.19.4", "mocha": "^3.2.0", "node-sass": "^4.5.2", @@ -37,6 +38,7 @@ "react-dom": "^15.5.4", "react-redux": "^5.0.5", "react-router-dom": "^4.2.2", + "react-router-redux": "^4.0.8", "redux": "^3.7.2", "redux-devtools": "^3.4.0", "redux-devtools-dock-monitor": "^1.1.2", From 5f38482c83a4795e3ed796a5edc6899bd9ad6b19 Mon Sep 17 00:00:00 2001 From: robertdurst <rsdurst@colby.edu> Date: Thu, 9 Nov 2017 17:04:35 -0800 Subject: [PATCH 07/19] getting the hash router to work --- backend/auth.js | 8 +++++--- backend/routes.js | 2 +- frontend/components/NewPost.js | 15 ++++----------- frontend/components/Sidebar.js | 3 +-- 4 files changed, 11 insertions(+), 17 deletions(-) diff --git a/backend/auth.js b/backend/auth.js index 48caec6..1b47f8a 100644 --- a/backend/auth.js +++ b/backend/auth.js @@ -17,14 +17,16 @@ const passport = require('passport'); const LocalStrategy = require('passport-local').Strategy; passport.serializeUser((user, done) => { - done(null, user.id); + const userId = user.get().id; + done(null, userId); }); passport.deserializeUser((id, done) => { User.findById(id) .then(user => { - const doneUser = Object.assign({}, user.dataValues, {password: null}); - done(null, doneUser); + // const doneUser = Object.assign({}, user.dataValues, {password: null}); + console.log(user.get()); + done(null, user.get()); }) .catch(err => { done(err, null); diff --git a/backend/routes.js b/backend/routes.js index 1febdaa..af8e126 100644 --- a/backend/routes.js +++ b/backend/routes.js @@ -4,7 +4,7 @@ const {User} = require('./models'); // YOUR API ROUTES HERE router.post('/post/new', (req, res) => { - console.log(req.user); + console.log(req); // Post.create({userId: req.user }); diff --git a/frontend/components/NewPost.js b/frontend/components/NewPost.js index f895581..39a7d37 100644 --- a/frontend/components/NewPost.js +++ b/frontend/components/NewPost.js @@ -14,10 +14,6 @@ class NewPost extends React.Component { this.handleSubmitPost = this.handleSubmitPost.bind(this); } - componentWillReceiveProps() { - - } - handleInputChange(event) { const target = event.target; const value = target.type === 'checkbox' ? target.checked : target.value; @@ -28,7 +24,7 @@ class NewPost extends React.Component { } handleSubmitPost() { - axios.post('/api/post/new', this.state) + axios.post('http://localhost:3000/api/post/new', this.state) .then(resp => { console.log(resp); // TODO reroute user to front page @@ -37,18 +33,15 @@ class NewPost extends React.Component { } render() { - console.log("On the new post page."); return ( - <div> - {/* <div className='NewPost-container'> */} - <h1>Hello World</h1> - {/* <TextField name='postTitle' onChange={this.handleInputChange} hintText='Post Title' /> + <div className='NewPost-container'> + <TextField name='postTitle' onChange={this.handleInputChange} hintText='Post Title' /> <TextField name='postBody' onChange={this.handleInputChange} hintText='Post Body' multiLine={true} /> <br /> <RaisedButton label='Choose Attachments' secondary={true} /> <br /> <RaisedButton onClick={this.handleSubmitPost} label='Submit' primary={true} /> - <br /> */} + <br /> </div> ); } diff --git a/frontend/components/Sidebar.js b/frontend/components/Sidebar.js index 8dbe730..ae4e9d6 100644 --- a/frontend/components/Sidebar.js +++ b/frontend/components/Sidebar.js @@ -22,8 +22,7 @@ export default class Sidebar extends React.Component { } handleClickSubmitPost() { - this.props.history.push('#/postnew'); - // window.location.hash = '/postnew' + window.location.hash = '/postnew' } handleClickLogout() { From 4f441ad7412f3df72a4161b180cbd1f5c0b1a46c Mon Sep 17 00:00:00 2001 From: jeff <tangj1122@gmail.com> Date: Thu, 9 Nov 2017 17:05:54 -0800 Subject: [PATCH 08/19] comits before merge w rob --- backend/auth.js | 4 ++-- frontend/containers/AppContainer.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/auth.js b/backend/auth.js index 28dc0fe..7a03f80 100644 --- a/backend/auth.js +++ b/backend/auth.js @@ -62,8 +62,8 @@ router.get('/logout', (req, res) => { router.get('/:username', (req, res) => { User.findOne({where: {username: req.params.username}}) .then(user => { - const resUser = Object.assign({}, user.dataValues, {password: null}); - res.json({user: resUser}); + // const resUser = Object.assign({}, user.dataValues, {password: null}); + res.json({user: user}); }) .catch(err => { res.json({err}); diff --git a/frontend/containers/AppContainer.js b/frontend/containers/AppContainer.js index b59847f..4a484dc 100644 --- a/frontend/containers/AppContainer.js +++ b/frontend/containers/AppContainer.js @@ -20,7 +20,7 @@ const AppContainer = ({ name }) => { <div className="appcontainer_body_container"> <Sidebar side={'left'}/> <Feed /> - <Sidebar2 /> + <Sidebar /> </div> </div> ); From cbd9a209fc4ffb1bef4736d70751c5d487b9536c Mon Sep 17 00:00:00 2001 From: robertdurst <rsdurst@colby.edu> Date: Thu, 9 Nov 2017 17:39:16 -0800 Subject: [PATCH 09/19] Day 1 done but with shitty code --- backend/auth.js | 13 ++++++++++++- backend/routes.js | 8 ++++---- frontend/components/Feed.js | 30 +++++++++++++++++++++++++++--- frontend/components/NewPost.js | 9 +++++---- 4 files changed, 48 insertions(+), 12 deletions(-) diff --git a/backend/auth.js b/backend/auth.js index 1b47f8a..7dfe568 100644 --- a/backend/auth.js +++ b/backend/auth.js @@ -1,6 +1,6 @@ const express = require('express'); const router = express.Router(); -const {User} = require('./models'); +const {User,Post} = require('./models'); router.post('/register', (req, res) => { console.log('process.env.DATABASE_NAME', process.env.DATABASE_NAME); @@ -77,4 +77,15 @@ router.get('/', (req, res) => { res.json({user: req.user}); }); +router.post('/post/new', (req, res) => { + Post.create({userId: req.user.id, body: req.body.body, title: req.body.title}) + .then((resp) => res.json(resp)); +}); + +router.get('/post/all', (req, res) => { + Post.findAll() + .then( resp => res.json(resp)); +}); + + module.exports = router; diff --git a/backend/routes.js b/backend/routes.js index af8e126..b7cf229 100644 --- a/backend/routes.js +++ b/backend/routes.js @@ -3,9 +3,9 @@ const router = express.Router(); const {User} = require('./models'); // YOUR API ROUTES HERE -router.post('/post/new', (req, res) => { - console.log(req); - // Post.create({userId: req.user -}); +// router.post('/post/new', (req, res) => { +// console.log(req); +// // Post.create({userId: req.user +// }); module.exports = router; diff --git a/frontend/components/Feed.js b/frontend/components/Feed.js index 2d0d2f5..41dac2a 100644 --- a/frontend/components/Feed.js +++ b/frontend/components/Feed.js @@ -1,16 +1,40 @@ import React from 'react'; import styles from '../assets/stylesheets/feed.css' import Paper from 'material-ui/Paper'; +import axios from 'axios'; -const Feed = () => { +class Feed extends React.Component { + constructor(props){ + super(props); + this.state = { + posts: [] + } + } + + componentDidMount() { + axios.get("http://localhost:3000/api/user/post/all") + .then( resp => { + this.setState({ + posts: resp.data + }) + }) + } + + render() { return ( <div className="feed_container"> <Paper className="feed_body" zDepth={2} - /> + > + <h1>All posts</h1> + { + this.state.posts.map( x => <div><h1>{x.title}</h1><p>{x.body}</p></div>) + } + </Paper> </div> ); -}; + } +} export default Feed; diff --git a/frontend/components/NewPost.js b/frontend/components/NewPost.js index 39a7d37..9824f6c 100644 --- a/frontend/components/NewPost.js +++ b/frontend/components/NewPost.js @@ -24,10 +24,11 @@ class NewPost extends React.Component { } handleSubmitPost() { - axios.post('http://localhost:3000/api/post/new', this.state) + console.log(this.state); + axios.post('http://localhost:3000/api/user/post/new', this.state) .then(resp => { console.log(resp); - // TODO reroute user to front page + window.location.hash = '/' }) .catch(err => {console.log(err)}); } @@ -35,8 +36,8 @@ class NewPost extends React.Component { render() { return ( <div className='NewPost-container'> - <TextField name='postTitle' onChange={this.handleInputChange} hintText='Post Title' /> - <TextField name='postBody' onChange={this.handleInputChange} hintText='Post Body' multiLine={true} /> + <TextField name='title' onChange={this.handleInputChange} hintText='Post Title' /> + <TextField name='body' onChange={this.handleInputChange} hintText='Post Body' multiLine={true} /> <br /> <RaisedButton label='Choose Attachments' secondary={true} /> <br /> From c8af19c822f7ea232fec071b83628152b4c861cb Mon Sep 17 00:00:00 2001 From: robertdurst <rsdurst@colby.edu> Date: Thu, 9 Nov 2017 18:47:15 -0800 Subject: [PATCH 10/19] Fixed the passport sessions issue --- backend/auth.js | 111 ++++++++++----------------------- backend/routes.js | 18 ++++-- frontend/components/Feed.js | 2 +- frontend/components/NewPost.js | 2 +- server.js | 43 ++++++++++++- 5 files changed, 89 insertions(+), 87 deletions(-) diff --git a/backend/auth.js b/backend/auth.js index 7dfe568..b20ea19 100644 --- a/backend/auth.js +++ b/backend/auth.js @@ -1,91 +1,44 @@ const express = require('express'); const router = express.Router(); -const {User,Post} = require('./models'); - -router.post('/register', (req, res) => { - console.log('process.env.DATABASE_NAME', process.env.DATABASE_NAME); - User.create({username: req.body.username, password: req.body.password}) - .then(user => { - res.json({user}); - }) - .catch(err => { - res.json({err}); +const {User} = require('./models'); + +module.exports = function(passport) { + router.post('/register', (req, res) => { + console.log('process.env.DATABASE_NAME', process.env.DATABASE_NAME); + User.create({username: req.body.username, password: req.body.password}) + .then(user => { + res.json({user}); + }) + .catch(err => { + res.json({err}); + }); }); -}); - -const passport = require('passport'); -const LocalStrategy = require('passport-local').Strategy; - -passport.serializeUser((user, done) => { - const userId = user.get().id; - done(null, userId); -}); -passport.deserializeUser((id, done) => { - User.findById(id) - .then(user => { - // const doneUser = Object.assign({}, user.dataValues, {password: null}); - console.log(user.get()); - done(null, user.get()); - }) - .catch(err => { - done(err, null); + router.post('/login', passport.authenticate('local'), (req, res) => { + const resUser = Object.assign({}, req.user.dataValues, {password: null}); + res.json({user: resUser}); }); -}); -passport.use(new LocalStrategy((username, password, done) => { - User.findOne({where: {username}}) - .then(user => { - if (!user) { - return done('no user found with that username', null); - } - if (user.password !== password) { - return done('passwords do not match', null); - } - return done(null, user); - }) - .catch(err => { - return done(err, null); + router.get('/logout', (req, res) => { + req.logout(); + res.redirect('/'); }); -})); - -router.use(passport.initialize()); -router.use(passport.session()); - -router.post('/login', passport.authenticate('local'), (req, res) => { - const resUser = Object.assign({}, req.user.dataValues, {password: null}); - res.json({user: resUser}); -}); -router.get('/logout', (req, res) => { - req.logout(); - res.redirect('/'); -}); - -router.get('/:username', (req, res) => { - User.findOne({where: {username: req.params.username}}) - .then(user => { - const resUser = Object.assign({}, user.dataValues, {password: null}); - res.json({user: resUser}); - }) - .catch(err => { - res.json({err}); + router.get('/:username', (req, res) => { + User.findOne({where: {username: req.params.username}}) + .then(user => { + const resUser = Object.assign({}, user.dataValues, {password: null}); + res.json({user: resUser}); + }) + .catch(err => { + res.json({err}); + }); }); -}); - -router.get('/', (req, res) => { - res.json({user: req.user}); -}); -router.post('/post/new', (req, res) => { - Post.create({userId: req.user.id, body: req.body.body, title: req.body.title}) - .then((resp) => res.json(resp)); -}); - -router.get('/post/all', (req, res) => { - Post.findAll() - .then( resp => res.json(resp)); -}); + router.get('/', (req, res) => { + res.json({user: req.user}); + }); + return router; -module.exports = router; +} diff --git a/backend/routes.js b/backend/routes.js index b7cf229..0ee4ae5 100644 --- a/backend/routes.js +++ b/backend/routes.js @@ -1,11 +1,19 @@ const express = require('express'); const router = express.Router(); -const {User} = require('./models'); +const {Post} = require('./models'); // YOUR API ROUTES HERE -// router.post('/post/new', (req, res) => { -// console.log(req); -// // Post.create({userId: req.user -// }); +router.post('/post/new', (req, res) => { + console.log(req.user); + console.log(req.session); + // Post.create({userId: req.user +}); + +router.get('/post/all', (req, res) => { + console.log(req.session); + console.log(req.user); + Post.findAll() + .then( resp => res.json(resp)); +}); module.exports = router; diff --git a/frontend/components/Feed.js b/frontend/components/Feed.js index 41dac2a..453baea 100644 --- a/frontend/components/Feed.js +++ b/frontend/components/Feed.js @@ -12,7 +12,7 @@ class Feed extends React.Component { } componentDidMount() { - axios.get("http://localhost:3000/api/user/post/all") + axios.get("http://localhost:3000/api/post/all") .then( resp => { this.setState({ posts: resp.data diff --git a/frontend/components/NewPost.js b/frontend/components/NewPost.js index 9824f6c..0021ab9 100644 --- a/frontend/components/NewPost.js +++ b/frontend/components/NewPost.js @@ -25,7 +25,7 @@ class NewPost extends React.Component { handleSubmitPost() { console.log(this.state); - axios.post('http://localhost:3000/api/user/post/new', this.state) + axios.post('http://localhost:3000/api/post/new', this.state) .then(resp => { console.log(resp); window.location.hash = '/' diff --git a/server.js b/server.js index 5bc6f9f..4c62a67 100644 --- a/server.js +++ b/server.js @@ -7,6 +7,8 @@ const auth = require('./backend/auth'); const api = require('./backend/routes'); const cookieSession = require('cookie-session'); +const { User } = require('./backend/models.js'); + app.use(express.static(path.join(__dirname, 'public'))); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: false })); @@ -14,11 +16,50 @@ app.use(cookieSession({ keys: [process.env.SECRET || 'super duper secret string'] })); +const passport = require('passport'); +const LocalStrategy = require('passport-local').Strategy; + +passport.serializeUser((user, done) => { + const userId = user.get().id; + done(null, userId); +}); + +passport.deserializeUser((id, done) => { + User.findById(id) + .then(user => { + // const doneUser = Object.assign({}, user.dataValues, {password: null}); + console.log(user.get()); + done(null, user.get()); + }) + .catch(err => { + done(err, null); + }); +}); + +passport.use(new LocalStrategy((username, password, done) => { + User.findOne({where: {username}}) + .then(user => { + if (!user) { + return done('no user found with that username', null); + } + if (user.password !== password) { + return done('passwords do not match', null); + } + return done(null, user); + }) + .catch(err => { + return done(err, null); + }); +})); + +app.use(passport.initialize()); +app.use(passport.session()); + app.get('/', (request, response) => { response.sendFile(__dirname + '/public/index.html'); // For React/Redux }); -app.use('/api/user', auth); +app.use('/api/user', auth(passport)); app.use('/api', api); app.listen(PORT, error => { From 4df042109a53a6dd4e0b059781d24dcd4d2c3078 Mon Sep 17 00:00:00 2001 From: jeff <tangj1122@gmail.com> Date: Thu, 9 Nov 2017 20:01:55 -0800 Subject: [PATCH 11/19] added keys to feed --- backend/routes.js | 6 ++++-- frontend/components/Feed.js | 12 +++++++----- frontend/components/NewPost.js | 12 +++++------- frontend/components/Sidebar2.js | 13 ++++++------- frontend/containers/AppContainer.js | 4 ++-- frontend/containers/Root.dev.js | 2 +- 6 files changed, 25 insertions(+), 24 deletions(-) diff --git a/backend/routes.js b/backend/routes.js index 0ab58c2..05f7a11 100644 --- a/backend/routes.js +++ b/backend/routes.js @@ -6,14 +6,16 @@ const {Post} = require('./models'); router.post('/post/new', (req, res) => { console.log(req.user); console.log(req.session); - // Post.create({userId: req.user + Post.create({userId: req.user.id, title: req.body.title, body: req.body.body}) + .then(() => res.json({msg: 'new post successful'})) + .catch(err => res.json({err})); }); router.get('/post/all', (req, res) => { console.log(req.session); console.log(req.user); Post.findAll() - .then( resp => res.json(resp)); + .then(resp => res.json({posts: resp})); }); module.exports = router; diff --git a/frontend/components/Feed.js b/frontend/components/Feed.js index 453baea..36c8f89 100644 --- a/frontend/components/Feed.js +++ b/frontend/components/Feed.js @@ -13,9 +13,9 @@ class Feed extends React.Component { componentDidMount() { axios.get("http://localhost:3000/api/post/all") - .then( resp => { + .then(resp => { this.setState({ - posts: resp.data + posts: resp.data.posts }) }) } @@ -28,9 +28,11 @@ class Feed extends React.Component { zDepth={2} > <h1>All posts</h1> - { - this.state.posts.map( x => <div><h1>{x.title}</h1><p>{x.body}</p></div>) - } + {this.state.posts.map(x => { + return ( + <div key={x.id}><h1>{x.title}</h1><p>{x.body}</p></div> + ); + })} </Paper> </div> ); diff --git a/frontend/components/NewPost.js b/frontend/components/NewPost.js index 0021ab9..9f10ff7 100644 --- a/frontend/components/NewPost.js +++ b/frontend/components/NewPost.js @@ -10,8 +10,6 @@ class NewPost extends React.Component { title: '', body: '', } - this.handleInputChange = this.handleInputChange.bind(this); - this.handleSubmitPost = this.handleSubmitPost.bind(this); } handleInputChange(event) { @@ -24,7 +22,6 @@ class NewPost extends React.Component { } handleSubmitPost() { - console.log(this.state); axios.post('http://localhost:3000/api/post/new', this.state) .then(resp => { console.log(resp); @@ -36,13 +33,14 @@ class NewPost extends React.Component { render() { return ( <div className='NewPost-container'> - <TextField name='title' onChange={this.handleInputChange} hintText='Post Title' /> - <TextField name='body' onChange={this.handleInputChange} hintText='Post Body' multiLine={true} /> + {this.state.title}, {this.state.body} <br /> - <RaisedButton label='Choose Attachments' secondary={true} /> + <TextField name='title' onChange={this.handleInputChange.bind(this)} hintText='Post Title' /> <br /> - <RaisedButton onClick={this.handleSubmitPost} label='Submit' primary={true} /> + <TextField name='body' onChange={this.handleInputChange.bind(this)} hintText='Post Body' multiLine={true} /> <br /> + <RaisedButton label='Choose Attachments' secondary={true} /> + <RaisedButton onClick={this.handleSubmitPost.bind(this)} label='Submit' primary={true} /> </div> ); } diff --git a/frontend/components/Sidebar2.js b/frontend/components/Sidebar2.js index a6303c1..e0cd695 100644 --- a/frontend/components/Sidebar2.js +++ b/frontend/components/Sidebar2.js @@ -18,10 +18,9 @@ export default class Sidebar2 extends React.Component { password: "", user: null, } - this.handleSubmit = this.handleSubmit.bind(this); } - handleSubmit() { + handleSubmitUser() { const endUrl = this.state.modalTitle === 'Login' ? '/login' : '/register'; axios.post(baseUrl + endUrl, this.state) .then(resp => { @@ -35,7 +34,6 @@ export default class Sidebar2 extends React.Component { handleInputChange(key) { return (e) => { - console.log(this.state); const state = {}; state[key] = e.target.value; this.setState(state); @@ -47,9 +45,9 @@ export default class Sidebar2 extends React.Component { .then(() => this.setState({user: null})); } - // handleClickSubmitPost() { - // window.location.hash = '/postnew' - // } + handleClickSubmitPost() { + window.location.hash = '/post/new' + } render() { const actionButtons = [ @@ -61,7 +59,7 @@ export default class Sidebar2 extends React.Component { <FlatButton label="Submit" primary={true} - onClick={this.handleSubmit} + onClick={this.handleSubmitUser.bind(this)} />, ]; if (this.props.side == "left") { @@ -79,6 +77,7 @@ export default class Sidebar2 extends React.Component { label="Submit Post" backgroundColor="black" labelColor="white" + onClick={this.handleClickSubmitPost.bind(this)} /> { this.state.user ? diff --git a/frontend/containers/AppContainer.js b/frontend/containers/AppContainer.js index 8d2967e..38a03a8 100644 --- a/frontend/containers/AppContainer.js +++ b/frontend/containers/AppContainer.js @@ -5,7 +5,6 @@ import { connect } from 'react-redux'; // Import Components import Header from '../components/Header'; import Feed from '../components/Feed'; -import Sidebar from '../components/Sidebar'; import Sidebar2 from '../components/Sidebar2'; // Import Styling @@ -18,7 +17,8 @@ const AppContainer = ({ name, history }) => { <Header /> </div> <div className="appcontainer_body_container"> - <Sidebar side={'left'}/> + <Sidebar2 side={'left'}/> + <Feed /> <Sidebar2 history={history}/> </div> </div> diff --git a/frontend/containers/Root.dev.js b/frontend/containers/Root.dev.js index 13032e2..4f9b968 100644 --- a/frontend/containers/Root.dev.js +++ b/frontend/containers/Root.dev.js @@ -14,7 +14,7 @@ export default function Root({ store, history }) { <HashRouter> <Switch> <Route exact path='/' render={()=><AppContainer history={history}/>}/> - <Route exact path='/postnew' render={()=><NewPost history={history}/>}/> + <Route exact path='/post/new' render={()=><NewPost history={history}/>}/> </Switch> </HashRouter> </MuiThemeProvider> From fcdbe5f4c26ee4f8972acfac6f679fc396ecfd7f Mon Sep 17 00:00:00 2001 From: jeff <tangj1122@gmail.com> Date: Thu, 9 Nov 2017 20:21:26 -0800 Subject: [PATCH 12/19] fin day 1, for real this time --- backend/routes.js | 4 ---- frontend/components/Sidebar2.js | 37 ++++++++++------------------- frontend/containers/AppContainer.js | 4 ++-- 3 files changed, 15 insertions(+), 30 deletions(-) diff --git a/backend/routes.js b/backend/routes.js index 05f7a11..3c741f6 100644 --- a/backend/routes.js +++ b/backend/routes.js @@ -4,16 +4,12 @@ const {Post} = require('./models'); // YOUR API ROUTES HERE router.post('/post/new', (req, res) => { - console.log(req.user); - console.log(req.session); Post.create({userId: req.user.id, title: req.body.title, body: req.body.body}) .then(() => res.json({msg: 'new post successful'})) .catch(err => res.json({err})); }); router.get('/post/all', (req, res) => { - console.log(req.session); - console.log(req.user); Post.findAll() .then(resp => res.json({posts: resp})); }); diff --git a/frontend/components/Sidebar2.js b/frontend/components/Sidebar2.js index e0cd695..1d338f6 100644 --- a/frontend/components/Sidebar2.js +++ b/frontend/components/Sidebar2.js @@ -24,7 +24,6 @@ export default class Sidebar2 extends React.Component { const endUrl = this.state.modalTitle === 'Login' ? '/login' : '/register'; axios.post(baseUrl + endUrl, this.state) .then(resp => { - console.log(resp); this.setState({modalIsOpen: false, user: resp.data.user}); }) .catch(err => { @@ -40,15 +39,6 @@ export default class Sidebar2 extends React.Component { }; } - handleLogout() { - axios.get(baseUrl + '/logout') - .then(() => this.setState({user: null})); - } - - handleClickSubmitPost() { - window.location.hash = '/post/new' - } - render() { const actionButtons = [ <FlatButton @@ -62,12 +52,7 @@ export default class Sidebar2 extends React.Component { onClick={this.handleSubmitUser.bind(this)} />, ]; - if (this.props.side == "left") { - return ( - <div className="sidebar_container"> - </div> - ); - } else { + if (this.props.side == "right") { return ( <div className="sidebar_container"> <div className="sidebar_content"> @@ -77,7 +62,10 @@ export default class Sidebar2 extends React.Component { label="Submit Post" backgroundColor="black" labelColor="white" - onClick={this.handleClickSubmitPost.bind(this)} + onClick={() => this.state.user ? + window.location.hash = '/post/new' : + this.setState({modalIsOpen: true, modalTitle: 'Login'}) + } /> { this.state.user ? @@ -87,7 +75,7 @@ export default class Sidebar2 extends React.Component { className="logout_button" label="logout" labelColor="#06d6a8" - onClick={this.handleLogout.bind(this)} + onClick={() => axios.get(baseUrl + '/logout').then(() => this.setState({user: null}))} backgroundColor="white" /> </div> : @@ -97,9 +85,7 @@ export default class Sidebar2 extends React.Component { icon={<LoginIcon />} labelColor="#06d6a8" label='login' - onClick={(e) => { - this.setState({modalIsOpen: true, modalTitle: 'Login'}); - }} + onClick={() => this.setState({modalIsOpen: true, modalTitle: 'Login'})} backgroundColor="white" /> <RaisedButton @@ -107,9 +93,7 @@ export default class Sidebar2 extends React.Component { icon={<RegisterIcon />} labelColor="white" label='register' - onClick={(e) => { - this.setState({modalIsOpen: true, modalTitle: 'Register'}); - }} + onClick={() => this.setState({modalIsOpen: true, modalTitle: 'Register'})} backgroundColor="#06d6a8" /> </div> @@ -145,6 +129,11 @@ export default class Sidebar2 extends React.Component { </div> </div> ); + } else { + return ( + <div className="sidebar_container"> + </div> + ); } } } diff --git a/frontend/containers/AppContainer.js b/frontend/containers/AppContainer.js index 38a03a8..55b7ae3 100644 --- a/frontend/containers/AppContainer.js +++ b/frontend/containers/AppContainer.js @@ -17,9 +17,9 @@ const AppContainer = ({ name, history }) => { <Header /> </div> <div className="appcontainer_body_container"> - <Sidebar2 side={'left'}/> + <Sidebar2 side='left' /> <Feed /> - <Sidebar2 history={history}/> + <Sidebar2 side='right' history={history}/> </div> </div> ); From 8fb519fc391914affe6ae4d834d8fb282c852b8a Mon Sep 17 00:00:00 2001 From: jeff <tangj1122@gmail.com> Date: Fri, 10 Nov 2017 11:00:02 -0800 Subject: [PATCH 13/19] get front end for unique post --- frontend/components/Feed.js | 16 +- frontend/components/NewPost.js | 2 +- frontend/components/PostPage.js | 60 ++++++++ frontend/components/Sidebar.js | 250 -------------------------------- frontend/components/Sidebar2.js | 2 +- frontend/containers/Root.dev.js | 10 +- 6 files changed, 80 insertions(+), 260 deletions(-) create mode 100644 frontend/components/PostPage.js delete mode 100644 frontend/components/Sidebar.js diff --git a/frontend/components/Feed.js b/frontend/components/Feed.js index 36c8f89..9e9ce30 100644 --- a/frontend/components/Feed.js +++ b/frontend/components/Feed.js @@ -1,6 +1,7 @@ import React from 'react'; +import {Link} from 'react-router-dom'; import styles from '../assets/stylesheets/feed.css' -import Paper from 'material-ui/Paper'; +import {Paper, RaisedButton} from 'material-ui'; import axios from 'axios'; class Feed extends React.Component { @@ -12,12 +13,12 @@ class Feed extends React.Component { } componentDidMount() { - axios.get("http://localhost:3000/api/post/all") + axios.get(localStorage.getItem('webAddress') + '/api/post/all') .then(resp => { this.setState({ posts: resp.data.posts }) - }) + }); } render() { @@ -27,10 +28,13 @@ class Feed extends React.Component { className="feed_body" zDepth={2} > - <h1>All posts</h1> - {this.state.posts.map(x => { + <h1 style={{paddingTop: '1em'}}>All posts</h1> + {this.state.posts.map(post => { return ( - <div key={x.id}><h1>{x.title}</h1><p>{x.body}</p></div> + <div key={post.id}> + <Link to={`/post/${post.id}`}><h2>{post.title}</h2></Link> + <p>{post.body}</p> + </div> ); })} </Paper> diff --git a/frontend/components/NewPost.js b/frontend/components/NewPost.js index 9f10ff7..c074a86 100644 --- a/frontend/components/NewPost.js +++ b/frontend/components/NewPost.js @@ -22,7 +22,7 @@ class NewPost extends React.Component { } handleSubmitPost() { - axios.post('http://localhost:3000/api/post/new', this.state) + axios.post(localStorage.getItem('webAddress') + '/api/post/new', this.state) .then(resp => { console.log(resp); window.location.hash = '/' diff --git a/frontend/components/PostPage.js b/frontend/components/PostPage.js new file mode 100644 index 0000000..92de246 --- /dev/null +++ b/frontend/components/PostPage.js @@ -0,0 +1,60 @@ +import React from 'react'; +import axios from 'axios'; +import {TextField, RaisedButton} from 'material-ui'; + +class NewPost extends React.Component { + constructor(props) { + super(props); + this.state = { + comment: '', + post: {title: 'TestTitle', body: 'TestBody'}, // object + } + } + + componentDidMount() { + // console.log( this.props.postId ); + // axios.get(localStorage.getItem('webAddress') + '/api/post/' + this.props.postId) + // .then(resp => { + // console.log('componentDidMount, GET post/:postId', resp); + // }) + // .catch(err => { + // console.log(err); + // }); + } + + handleInputChange(event) { + const target = event.target; + const value = target.type === 'checkbox' ? target.checked : target.value; + const name = target.name; + this.setState({ + [name]: value + }); + } + + handleSubmitPost() { + axios.post(localStorage.getItem('webAddress') + '/api/post/new', this.state) + .then(resp => { + console.log(resp); + window.location.hash = '/' + }) + .catch(err => {console.log(err)}); + } + + render() { + return ( + <div className='NewPost-container'> + <h1>{this.state.post.title} (id: {this.props.postId})</h1> + <p>{this.state.post.body}</p> + <br /> + <br /> + <TextField name='comment' onChange={this.handleInputChange.bind(this)} hintText='Comment' multiLine={true} /> + <br /> + <RaisedButton label='Choose Attachments' secondary={true} /> + <RaisedButton onClick={this.handleSubmitPost.bind(this)} label='Submit' primary={true} /> + </div> + ); + } + +} + +export default NewPost; diff --git a/frontend/components/Sidebar.js b/frontend/components/Sidebar.js deleted file mode 100644 index ae4e9d6..0000000 --- a/frontend/components/Sidebar.js +++ /dev/null @@ -1,250 +0,0 @@ -import React from 'react'; -import styles from '../assets/stylesheets/sidebar.css' -import PropTypes from 'prop-types'; -import { RaisedButton, Paper, FlatButton, Dialog, TextField } from 'material-ui'; -import LoginIcon from 'material-ui/svg-icons/social/person'; -import RegisterIcon from 'material-ui/svg-icons/social/person-add'; -import SubmitPostIcon from 'material-ui/svg-icons/editor/border-color'; -import axios from 'axios'; - -export default class Sidebar extends React.Component { - constructor(props) { - super(props); - this.state = { - open_login: false, - open_register: false, - login_pass: "", - login_user: "", - register_pass: "", - register_user: "", - username: "", - } - } - - handleClickSubmitPost() { - window.location.hash = '/postnew' - } - - handleClickLogout() { - axios({ - url: "http://localhost:3000/api/user/logout", - method: "get" - }).then( () =>{ - this.setState({ - username: "" - }) - }) - } - - handleClickSubmit_login() { - axios({ - url: 'http://localhost:3000/api/user/login', - method: 'post', - data: { - username: this.state.login_user, - password: this.state.login_pass - } - }).then(x => { - this.setState({ - username: x.data.user.username - }) - this.setState({open_login: false}); - }) - } - - handleClickSubmit_register() { - axios({ - url: 'http://localhost:3000/api/user/register', - method: 'post', - data: { - username: this.state.register_user, - password: this.state.register_pass - } - }).then( x => { - this.setState({open_register: false}); - }) - } - - handleOpen_login(){ - this.setState({open_login: true}); - }; - - handleClose_login(){ - this.setState({open_login: false}); - this.setState({ - login_pass: "", - login_user: "" - }) - }; - - handleOpen_register(){ - this.setState({open_register: true}); - }; - - handleClose_register(){ - this.setState({open_register: false}); - this.setState({ - register_pass: "", - register_user: "" - }) - }; - - handleChangeUser_register(event){ - const target = event.target; - const value = target.value; - this.setState({ - register_user: value - }) - } - - handleChangeUser_login(event){ - const target = event.target; - const value = target.value; - console.log(value); - this.setState({ - login_user: value - }) - } - - handleChangePass_register(event){ - const target = event.target; - const value = target.value; - this.setState({ - register_pass: value - }) - } - - handleChangePass_login(event){ - const target = event.target; - const value = target.value; - this.setState({ - login_pass: value - }) - } - - render() { - console.log(this.props); - const actions_login = [ - <FlatButton - label="Cancel" - primary={true} - onClick={this.handleClose_login.bind(this)} - />, - <FlatButton - label="Login" - primary={true} - disabled={!(this.state.login_pass && this.state.login_user)} - onClick={this.handleClickSubmit_login.bind(this)} - />, - ]; - const actions_register = [ - <FlatButton - label="Cancel" - primary={true} - onClick={this.handleClose_register.bind(this)} - />, - <FlatButton - label="Register" - primary={true} - disabled={!(this.state.register_pass && this.state.register_user)} - onClick={this.handleClickSubmit_register.bind(this)} - />, - ]; - if(this.props.side == "left") { - return ( - <div className="sidebar_container"> - </div> - ); - } else { - return ( - <div className="sidebar_container"> - <div className="sidebar_content"> - <RaisedButton - className="submit_post_button" - icon={<SubmitPostIcon />} - label="Submit Post" - backgroundColor="black" - labelColor="white" - onClick={this.handleClickSubmitPost.bind(this)} - /> - { - this.state.username ? - <div> - <h1>{this.state.username}</h1> - <RaisedButton - className="logout_button" - label="logout" - labelColor="#06d6a8" - onClick={this.handleClickLogout.bind(this)} - backgroundColor="white" - /> - </div> : - <div className="login_register_button_container"> - <RaisedButton - className="login_button" - icon={<LoginIcon />} - labelColor="#06d6a8" - onClick={this.handleOpen_login.bind(this)} - backgroundColor="white" - /> - <RaisedButton - className="register_button" - icon={<RegisterIcon />} - labelColor="white" - onClick={this.handleOpen_register.bind(this)} - backgroundColor="#06d6a8" - /> - </div> - } - - <Paper className="description_right_container" zDepth={5}> - <h1 className="right_sidebar_description_title"> - Description - </h1> - <p className="right_sidebar_description_content"> - This is the description of Steemit, an up and coming social media platform built on the Steem blockchain. Handling - hundreds of thousands of transactions a day with 3 second confirmmations and no fee's, this is a blockchain you don't want - to miss. - </p> - </Paper> - </div> - <div> - <Dialog - title="Login" - actions={actions_login} - modal={true} - open={this.state.open_login} - > - <TextField - hintText="Username" - onChange={this.handleChangeUser_login.bind(this)} - /> <br /> - <TextField - hintText="Password" - onChange={this.handleChangePass_login.bind(this)} - /> - </Dialog> - </div> - - <div> - <Dialog - title="Register" - actions={actions_register} - modal={true} - open={this.state.open_register} - > - <TextField - hintText="Username" - onChange={this.handleChangeUser_register.bind(this)} - /> <br /> - <TextField - hintText="Password" - onChange={this.handleChangePass_register.bind(this)} - /> - </Dialog> - </div> - </div> - ); - } - } -} diff --git a/frontend/components/Sidebar2.js b/frontend/components/Sidebar2.js index 1d338f6..c078a34 100644 --- a/frontend/components/Sidebar2.js +++ b/frontend/components/Sidebar2.js @@ -6,7 +6,7 @@ import LoginIcon from 'material-ui/svg-icons/social/person'; import RegisterIcon from 'material-ui/svg-icons/social/person-add'; import SubmitPostIcon from 'material-ui/svg-icons/editor/border-color'; import axios from 'axios'; -const baseUrl = 'http://localhost:3000/api/user'; +const baseUrl = localStorage.getItem('webAddress') + '/api/user'; export default class Sidebar2 extends React.Component { constructor(props) { diff --git a/frontend/containers/Root.dev.js b/frontend/containers/Root.dev.js index 4f9b968..95797a3 100644 --- a/frontend/containers/Root.dev.js +++ b/frontend/containers/Root.dev.js @@ -1,13 +1,16 @@ import PropTypes from 'prop-types'; import React from 'react'; import {Provider} from 'react-redux'; +import { HashRouter, Route, Switch } from 'react-router-dom'; +import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'; import AppContainer from './AppContainer'; import NewPost from '../components/NewPost' +import PostPage from '../components/PostPage' import DevTools from './DevTools'; -import { HashRouter, Route, Switch } from 'react-router-dom'; -import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'; export default function Root({ store, history }) { + // localStorage.setItem('webAddress', 'http://10.2.106.68:3000'); + localStorage.setItem('webAddress', 'http://localhost:3000'); return ( <Provider store={store}> <MuiThemeProvider> @@ -15,6 +18,9 @@ export default function Root({ store, history }) { <Switch> <Route exact path='/' render={()=><AppContainer history={history}/>}/> <Route exact path='/post/new' render={()=><NewPost history={history}/>}/> + <Route exact path='/post/:postId' render={(props) => { + return <PostPage postId={props.match.params.postId} /> + }} /> </Switch> </HashRouter> </MuiThemeProvider> From a25eaa52bf82d03481a1e3c32e637b29e1535fc0 Mon Sep 17 00:00:00 2001 From: jeff <tangj1122@gmail.com> Date: Fri, 10 Nov 2017 13:12:57 -0800 Subject: [PATCH 14/19] commit b4 merge rob recursion --- backend/auth.js | 4 +++ frontend/components/PostPage.js | 62 ++++++++++++++++++++++++--------- frontend/components/Sidebar2.js | 21 +++++++++-- frontend/containers/Root.dev.js | 4 +-- frontend/reducers/index.js | 18 +++++++--- public/index.html | 8 ++++- 6 files changed, 90 insertions(+), 27 deletions(-) diff --git a/backend/auth.js b/backend/auth.js index d0b195a..f12ce4b 100644 --- a/backend/auth.js +++ b/backend/auth.js @@ -1,11 +1,15 @@ const express = require('express'); const router = express.Router(); const {User} = require('./models'); +const axios = require('axios'); module.exports = (passport) => { router.post('/register', (req, res) => { User.create({username: req.body.username, password: req.body.password}) .then(user => { + // axios.post('/login', {user}) + // .then(resp => {console.log(resp)}) + // .catch(err => {console.log(err)}) res.json({user}); }) .catch(err => { diff --git a/frontend/components/PostPage.js b/frontend/components/PostPage.js index 92de246..641347e 100644 --- a/frontend/components/PostPage.js +++ b/frontend/components/PostPage.js @@ -7,19 +7,20 @@ class NewPost extends React.Component { super(props); this.state = { comment: '', - post: {title: 'TestTitle', body: 'TestBody'}, // object + data: [ { title: 'loading', body: 'loading', id: '0', userId: '0' } ], } } componentDidMount() { // console.log( this.props.postId ); - // axios.get(localStorage.getItem('webAddress') + '/api/post/' + this.props.postId) - // .then(resp => { - // console.log('componentDidMount, GET post/:postId', resp); - // }) - // .catch(err => { - // console.log(err); - // }); + axios.get(localStorage.getItem('webAddress') + '/api/post/' + this.props.postId) + .then(resp => { + console.log(resp); + this.setState({data: resp.data}); + }) + .catch(err => { + console.log(err); + }); } handleInputChange(event) { @@ -41,18 +42,45 @@ class NewPost extends React.Component { } render() { + + $(window).on('load', () => { + this.state.data.forEach(post => { + $('.parent-post-container').append(` + <div>hi<div/> + `) + }); + }); + return ( - <div className='NewPost-container'> - <h1>{this.state.post.title} (id: {this.props.postId})</h1> - <p>{this.state.post.body}</p> - <br /> - <br /> - <TextField name='comment' onChange={this.handleInputChange.bind(this)} hintText='Comment' multiLine={true} /> - <br /> - <RaisedButton label='Choose Attachments' secondary={true} /> - <RaisedButton onClick={this.handleSubmitPost.bind(this)} label='Submit' primary={true} /> + <div className='container'> + <div className='parent-post-container' style={{border: '1px solid black'}}> + <h1>{this.state.data[0].title} (postId: {this.props.postId})</h1> + <p>{this.state.data[0].body} - <i>userId {this.state.data[0].userId}</i></p> + <br /> + <TextField name='comment' onChange={this.handleInputChange.bind(this)} hintText='Comment' multiLine={true} /> + <RaisedButton onClick={this.handleSubmitPost.bind(this)} label='Submit' primary={true} /> + </div> + <div className='children-container' style={{marginLeft: '1em'}}> + { + + } + {/*this.state.data.children.map(child => { + return ( + <div key={child.id} className='child-container'> + <h4>{child.title}</h4> + <p>{child.body} (id: {child.id})</p> + <div className='subchildren-container' style={{marginLeft: '1em'}}> + {child.children.map(subchild => { + return <div key={subchild.id} className='subchild-container'>{subchild.body}</div> + })} + </div> + </div> + ) + })*/} + </div> </div> ); + } } diff --git a/frontend/components/Sidebar2.js b/frontend/components/Sidebar2.js index c078a34..e3c0cb7 100644 --- a/frontend/components/Sidebar2.js +++ b/frontend/components/Sidebar2.js @@ -1,6 +1,6 @@ import React from 'react'; import styles from '../assets/stylesheets/sidebar.css' -import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; import { RaisedButton, Paper, FlatButton, Dialog, TextField } from 'material-ui'; import LoginIcon from 'material-ui/svg-icons/social/person'; import RegisterIcon from 'material-ui/svg-icons/social/person-add'; @@ -8,7 +8,7 @@ import SubmitPostIcon from 'material-ui/svg-icons/editor/border-color'; import axios from 'axios'; const baseUrl = localStorage.getItem('webAddress') + '/api/user'; -export default class Sidebar2 extends React.Component { +class Sidebar2 extends React.Component { constructor(props) { super(props); this.state = { @@ -25,6 +25,7 @@ export default class Sidebar2 extends React.Component { axios.post(baseUrl + endUrl, this.state) .then(resp => { this.setState({modalIsOpen: false, user: resp.data.user}); + this.props.login(resp.data.user); }) .catch(err => { console.log(`err with ${endUrl}: ${err}`); @@ -137,3 +138,19 @@ export default class Sidebar2 extends React.Component { } } } + +const mapStateToProps = (state) => { + return { + }; +}; + +const mapDispatchToProps = (dispatch) => { + return { + login: (user) => dispatch({type: 'USER', user}), + }; +}; + +export default connect( + mapStateToProps, + mapDispatchToProps +)(Sidebar2); diff --git a/frontend/containers/Root.dev.js b/frontend/containers/Root.dev.js index 95797a3..81692a3 100644 --- a/frontend/containers/Root.dev.js +++ b/frontend/containers/Root.dev.js @@ -9,8 +9,8 @@ import PostPage from '../components/PostPage' import DevTools from './DevTools'; export default function Root({ store, history }) { - // localStorage.setItem('webAddress', 'http://10.2.106.68:3000'); - localStorage.setItem('webAddress', 'http://localhost:3000'); + localStorage.setItem('webAddress', 'http://10.2.106.68:3000'); + // localStorage.setItem('webAddress', 'http://localhost:3000'); return ( <Provider store={store}> <MuiThemeProvider> diff --git a/frontend/reducers/index.js b/frontend/reducers/index.js index 4d415fd..77a4aa9 100644 --- a/frontend/reducers/index.js +++ b/frontend/reducers/index.js @@ -1,8 +1,16 @@ -function rootReducer(state = {name: 'Horizons'}, action) { - switch (action.type) { - default: - return state; - } +const defaultState = { + name: 'Horizons', + user: null, +}; + +function rootReducer(state = defaultState, action) { + switch (action.type) { + case ('USER'): + console.log(action); + return {user: action.user || state.user}; + default: + return state; + } } export default rootReducer; diff --git a/public/index.html b/public/index.html index 9604aed..5f0e5b9 100644 --- a/public/index.html +++ b/public/index.html @@ -7,5 +7,11 @@ <body> <div id='root'/> </body> + <script + src="http://code.jquery.com/jquery-3.2.1.min.js" + integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" + crossorigin="anonymous"> + </script> <script src="/bundle.js"></script> -</html> \ No newline at end of file + +</html> From 97d46ca193c0e9f404f557569dfe6774d3ac0a4a Mon Sep 17 00:00:00 2001 From: robertdurst <rsdurst@colby.edu> Date: Fri, 10 Nov 2017 13:13:08 -0800 Subject: [PATCH 15/19] Recursion for the comment query --- backend/models.js | 2 +- backend/routes.js | 38 ++++++++++++++++++++++++++++++++++++++ server.js | 7 +++++++ 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/backend/models.js b/backend/models.js index cf80e80..c0c9fcf 100644 --- a/backend/models.js +++ b/backend/models.js @@ -45,7 +45,7 @@ const Post = sequelize.define('post', { body: { type: Sequelize.STRING, }, - parentId: { + parent_id: { type: Sequelize.INTEGER,// can reference other posts. null if a parent post }, }); diff --git a/backend/routes.js b/backend/routes.js index 3c741f6..640edc3 100644 --- a/backend/routes.js +++ b/backend/routes.js @@ -14,4 +14,42 @@ router.get('/post/all', (req, res) => { .then(resp => res.json({posts: resp})); }); +router.post('/post/:id', function(req, res){ + Post.create({userId: req.user.id, title: req.body.title, body: req.body.body, parent_id: req.params.id}) + .then( resp => res.json(resp)); +}) + +router.get('/post/:id', function(req, res){ + Post.findAll({where: {id: req.params.id}}) + .then( resp => { + let childrenObj = []; + let recurseLeft = 1; + + async function find(children) { + + recurseLeft --; + if(!children.length && !recurseLeft) { + res.json(childrenObj); + } + else if(!children.length){ + return childrenObj; + } + children.forEach( child => { + childrenObj.push(child.dataValues) + recurseLeft ++; + (async function x(){ + var resp = await Post.findAll({where: {parent_id: child.id}}) + var resp = await find(resp); + return resp; + console.log("here"); + })() + }) + + } + find(resp); +}) +}) + + + module.exports = router; diff --git a/server.js b/server.js index 66a84de..9aa9dc7 100644 --- a/server.js +++ b/server.js @@ -15,6 +15,13 @@ app.use(bodyParser.urlencoded({ extended: false })); app.use(cookieSession({ keys: [process.env.SECRET || 'super duper secret string'] })); +app.use((req, res, next) => { + res.header("Access-Control-Allow-Credentials", true); + res.header("Access-Control-Allow-Origin", "http://localhost:3000"); + res.header("Access-Control-Allow-Methods", "OPTIONS, POST, GET"); + res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); + next(); + }); const passport = require('passport'); const LocalStrategy = require('passport-local').Strategy; From 095e39fb18310afd6de8d571adcd7ac40f4545da Mon Sep 17 00:00:00 2001 From: jeff <tangj1122@gmail.com> Date: Fri, 10 Nov 2017 17:42:11 -0800 Subject: [PATCH 16/19] rage quit --- backend/routes.js | 4 +- frontend/components/PostPage.js | 105 +++++++++++++++++++++----------- public/index.html | 6 +- 3 files changed, 73 insertions(+), 42 deletions(-) diff --git a/backend/routes.js b/backend/routes.js index 640edc3..1bd52e0 100644 --- a/backend/routes.js +++ b/backend/routes.js @@ -1,6 +1,8 @@ const express = require('express'); const router = express.Router(); const {Post} = require('./models'); +const Sequelize = require('sequelize'); +const Op = Sequelize.Op; // YOUR API ROUTES HERE router.post('/post/new', (req, res) => { @@ -10,7 +12,7 @@ router.post('/post/new', (req, res) => { }); router.get('/post/all', (req, res) => { - Post.findAll() + Post.findAll({ where: { parent_id: null }}) .then(resp => res.json({posts: resp})); }); diff --git a/frontend/components/PostPage.js b/frontend/components/PostPage.js index 641347e..df42d68 100644 --- a/frontend/components/PostPage.js +++ b/frontend/components/PostPage.js @@ -1,22 +1,29 @@ import React from 'react'; import axios from 'axios'; import {TextField, RaisedButton} from 'material-ui'; +import FontIcon from 'material-ui/FontIcon'; +import IconButton from 'material-ui/IconButton'; +import KeyboardArrowUp from 'material-ui/svg-icons/hardware/keyboard-arrow-up'; +import KeyboardArrowDown from 'material-ui/svg-icons/hardware/keyboard-arrow-down'; + class NewPost extends React.Component { constructor(props) { super(props); this.state = { + title: '', comment: '', - data: [ { title: 'loading', body: 'loading', id: '0', userId: '0' } ], + data: { title: 'loading', body: 'loading', id: '0', userId: '0', parentId: 1, children: [] }, } + this.handleVoteClick = this.handleVoteClick.bind(this); + this.handleSubmitPost = this.handleSubmitPost.bind(this); } componentDidMount() { - // console.log( this.props.postId ); axios.get(localStorage.getItem('webAddress') + '/api/post/' + this.props.postId) .then(resp => { - console.log(resp); this.setState({data: resp.data}); + console.log(resp.data); }) .catch(err => { console.log(err); @@ -32,52 +39,78 @@ class NewPost extends React.Component { }); } - handleSubmitPost() { - axios.post(localStorage.getItem('webAddress') + '/api/post/new', this.state) + handleSubmitPost(postId) { + axios.post(localStorage.getItem('webAddress') + '/api/post/' + (postId || this.props.postId), this.state) .then(resp => { console.log(resp); - window.location.hash = '/' + window.location.hash = '/'; }) .catch(err => {console.log(err)}); } - render() { + handleVoteClick(voteType, postId) { + axios.get(localStorage.getItem('webAddress') + '/api/post/' + (postId || this.props.postId) + '/' + voteType) + .then(resp => console.log(resp)) + .catch(resp => console.log(resp)); + } - $(window).on('load', () => { - this.state.data.forEach(post => { - $('.parent-post-container').append(` - <div>hi<div/> - `) + render() { + const self = this; + function traverse(parent) { + if (!parent.children.length) { + return; + } + return parent.children.map(child => { + return ( + <div style={{marginLeft: '1.5em', border: '1px solid black'}} key={child.id}> + <h4>{child.title} - <i>user {child.userId}</i></h4> + <div className='vote-container' style={{display: 'inline', marginRight: '1em'}}> + {child.votecount} + <IconButton style={{display: 'inline-block', width: '3em', marginRight: '1em'}} + onClick={() => self.handleVoteClick('upvote', child.id)} + > + <KeyboardArrowUp /> + </IconButton> + <IconButton style={{display: 'inline-block', width: '3em', marginRight: '1em'}} + onClick={() => self.handleVoteClick('downvote', child.id)} + > + <KeyboardArrowDown /> + </IconButton> + </div> + {child.body} (id: {child.id}) + <br /> + <TextField name='title' onChange={(e) => self.handleInputChange(e)} hintText='Title' /> + <br /> + <TextField name='comment' onChange={(e) => self.handleInputChange(e)} hintText='Reply' multiLine={true} /> + <RaisedButton onClick={() => self.handleSubmitPost(post.id)} label='Reply' secondary={true} /> + {traverse(child)} + </div> + ); }); - }); + } return ( <div className='container'> - <div className='parent-post-container' style={{border: '1px solid black'}}> - <h1>{this.state.data[0].title} (postId: {this.props.postId})</h1> - <p>{this.state.data[0].body} - <i>userId {this.state.data[0].userId}</i></p> - <br /> - <TextField name='comment' onChange={this.handleInputChange.bind(this)} hintText='Comment' multiLine={true} /> - <RaisedButton onClick={this.handleSubmitPost.bind(this)} label='Submit' primary={true} /> + {this.state.comment} + <h2>{this.state.data.title} - <i>user {this.state.data.userId}</i></h2> + <p>{this.state.data.body} (postId: {this.state.data.id})</p> + <div className='vote-container' style={{display: 'inline', marginRight: '1em', marginLeft: '1em'}}> + {this.state.data.votecount} + <IconButton style={{display: 'inline-block', width: '3em', marginRight: '1em'}} + onClick={() => this.handleVoteClick('upvote')} + > + <KeyboardArrowUp /> + </IconButton> + <IconButton style={{display: 'inline-block', width: '3em', marginRight: '1em'}} + onClick={() => this.handleVoteClick('downvote')} + > + <KeyboardArrowDown /> + </IconButton> </div> - <div className='children-container' style={{marginLeft: '1em'}}> - { - } - {/*this.state.data.children.map(child => { - return ( - <div key={child.id} className='child-container'> - <h4>{child.title}</h4> - <p>{child.body} (id: {child.id})</p> - <div className='subchildren-container' style={{marginLeft: '1em'}}> - {child.children.map(subchild => { - return <div key={subchild.id} className='subchild-container'>{subchild.body}</div> - })} - </div> - </div> - ) - })*/} - </div> + <TextField name='comment' onChange={this.handleInputChange.bind(this)} hintText='Comment' multiLine={true} /> + <RaisedButton onClick={() => this.handleSubmitPost()} label='Comment' primary={true} /> + {traverse(this.state.data)} </div> ); diff --git a/public/index.html b/public/index.html index 5f0e5b9..249c296 100644 --- a/public/index.html +++ b/public/index.html @@ -7,11 +7,7 @@ <body> <div id='root'/> </body> - <script - src="http://code.jquery.com/jquery-3.2.1.min.js" - integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" - crossorigin="anonymous"> - </script> + <!-- <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"> --> <script src="/bundle.js"></script> </html> From c907eada6c8e1b8be7c15266576152686575c5ef Mon Sep 17 00:00:00 2001 From: robertdurst <rsdurst@colby.edu> Date: Fri, 10 Nov 2017 17:42:18 -0800 Subject: [PATCH 17/19] Upvoting --- backend/models.js | 16 +++++++++ backend/routes.js | 87 ++++++++++++++++++++++++++++++++++++----------- server.js | 9 +++-- 3 files changed, 90 insertions(+), 22 deletions(-) diff --git a/backend/models.js b/backend/models.js index c0c9fcf..24d30c4 100644 --- a/backend/models.js +++ b/backend/models.js @@ -50,10 +50,26 @@ const Post = sequelize.define('post', { }, }); +const Vote = sequelize.define('vote', { + id: { + type: Sequelize.INTEGER, + primaryKey: true, + autoIncrement: true + }, + isUpvote: { + type: Sequelize.INTEGER, + allowNull: false, + }, +}); + Post.belongsTo(User); +Vote.belongsTo(User); +Vote.belongsTo(Post); +Post.hasMany(Vote, { as: 'votes', foreignKey: 'postId' }); module.exports = { sequelize, User, Post, + Vote }; diff --git a/backend/routes.js b/backend/routes.js index 640edc3..d8bf37c 100644 --- a/backend/routes.js +++ b/backend/routes.js @@ -1,8 +1,10 @@ const express = require('express'); const router = express.Router(); -const {Post} = require('./models'); +const {Post, Vote} = require('./models'); +var sequelize = require('sequelize'); // YOUR API ROUTES HERE + router.post('/post/new', (req, res) => { Post.create({userId: req.user.id, title: req.body.title, body: req.body.body}) .then(() => res.json({msg: 'new post successful'})) @@ -15,41 +17,88 @@ router.get('/post/all', (req, res) => { }); router.post('/post/:id', function(req, res){ + Post.create({userId: req.user.id, title: req.body.title, body: req.body.body, parent_id: req.params.id}) .then( resp => res.json(resp)); }) router.get('/post/:id', function(req, res){ - Post.findAll({where: {id: req.params.id}}) + Post.findAll({ + where: {id: req.params.id}, + attributes: ['id', 'title', 'body', 'createdAt', 'userId', 'parent_id', [sequelize.fn('sum', sequelize.col('votes.isUpvote')), 'votecount']], + include: [ + { model: Vote, as: 'votes' } + ], + group: ['post.id','votes.id'] + }) .then( resp => { - let childrenObj = []; + let childrenObj = {} + childrenObj[resp[0].dataValues.id] = resp[0].dataValues + childrenObj[resp[0].dataValues.id].children = []; let recurseLeft = 1; - - async function find(children) { - + find=(children)=>{ recurseLeft --; - if(!children.length && !recurseLeft) { - res.json(childrenObj); - } - else if(!children.length){ - return childrenObj; - } + if(!children.length && !recurseLeft) res.json(childrenObj[req.params.id]); children.forEach( child => { - childrenObj.push(child.dataValues) + if(child.dataValues.id != req.params.id) { + childrenObj[child.dataValues.parent_id].children.push(child.dataValues); + childrenObj[child.dataValues.id] = child.dataValues; + childrenObj[child.dataValues.id].children = []; + } recurseLeft ++; (async function x(){ - var resp = await Post.findAll({where: {parent_id: child.id}}) + var resp = await Post.findAll({ + where: {parent_id: child.id}, + attributes: ['id', 'title', 'body', 'createdAt', 'userId', 'parent_id', [sequelize.fn('sum', sequelize.col('votes.isUpvote')), 'votecount']], + include: [ + { model: Vote, as: 'votes' } + ], + group: ['post.id','votes.id'] + }) var resp = await find(resp); return resp; console.log("here"); })() - }) - - } - find(resp); -}) + })} + find(resp); + }) }) +router.get('/post/:id/:vote', function(req, res){ + console.log(req.user); + Vote.findOne({where: {postId: req.params.id, userId: req.user.id}}) + .then( (resp) => { + + if(resp){ + // They upvoted an upvote --> destroy vote + if(resp.dataValues.isUpvote && req.params.vote === 'upvote'){ + Vote.destroy({where: {postId: req.params.id, userId: req.user.id}}) + .then(() => res.json({success: 'true'})); + } + + // They downvoted an upvote --> change isUpvote to false + else if(resp.dataValues.isUpvote && req.params.vote === 'downvote') { + Vote.update({isUpvote: -1}, {where: {postId: req.params.id, userId: req.user.id}}) + .then(() => res.json({success: 'true'})); + } + + // They upvoted a downvote --> change isUpvote to true + if(!resp.dataValues.isUpvote && req.params.vote === 'upvote'){ + Vote.update({isUpvote: 1}, {where: {postId: req.params.id, userId: req.user.id}}) + .then(() => res.json({success: 'true'})); + } + + // They downvoted a downvote --> destroy vote + else if(!resp.dataValues.isUpvote && req.params.vote === 'downvote') { + Vote.destroy({where: {postId: req.params.id, userId: req.user.id}}) + .then(() => res.json({success: 'true'})); + } + } else { + Vote.create({postId: req.params.id, userId: req.user.id, isUpvote: req.params.vote === 'upvote' ? 1 : -1}) + .then(() => res.json({success: 'true'})); + } + }) +}); module.exports = router; diff --git a/server.js b/server.js index 9aa9dc7..c3ca67f 100644 --- a/server.js +++ b/server.js @@ -15,19 +15,22 @@ app.use(bodyParser.urlencoded({ extended: false })); app.use(cookieSession({ keys: [process.env.SECRET || 'super duper secret string'] })); + app.use((req, res, next) => { res.header("Access-Control-Allow-Credentials", true); res.header("Access-Control-Allow-Origin", "http://localhost:3000"); res.header("Access-Control-Allow-Methods", "OPTIONS, POST, GET"); res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); next(); - }); + }); + + const passport = require('passport'); const LocalStrategy = require('passport-local').Strategy; passport.serializeUser((user, done) => { - // console.log('\nSERIALIZE', user); + console.log('\nSERIALIZE', user); const userId = user.id; done(null, userId); }); @@ -35,7 +38,7 @@ passport.serializeUser((user, done) => { passport.deserializeUser((id, done) => { User.findById(id) .then(user => { - // console.log('\nDESERIALIZE', user); + console.log('\nDESERIALIZE', user); done(null, user); }) .catch(err => { From bc2304116d1431001e0521eab197eba1e93fd638 Mon Sep 17 00:00:00 2001 From: jeff <tangj1122@gmail.com> Date: Fri, 10 Nov 2017 20:13:40 -0800 Subject: [PATCH 18/19] refactor a bit of backend --- .eslintrc | 2 +- backend/routes.js | 66 +++++++++++++++++++++-------------------------- server.js | 22 +++++++--------- 3 files changed, 41 insertions(+), 49 deletions(-) diff --git a/.eslintrc b/.eslintrc index a9aeb0c..1c86c5e 100644 --- a/.eslintrc +++ b/.eslintrc @@ -14,7 +14,7 @@ "__DEV__": true }, parserOptions: { - ecmaVersion: 6, + ecmaVersion: 8, # sourceType: 'module', ecmaFeatures: { "experimentalObjectRestSpread": true, diff --git a/backend/routes.js b/backend/routes.js index 8c4030a..aac8745 100644 --- a/backend/routes.js +++ b/backend/routes.js @@ -1,11 +1,8 @@ const express = require('express'); const router = express.Router(); -const Sequelize = require('sequelize'); const {Post, Vote} = require('./models'); var sequelize = require('sequelize'); -// YOUR API ROUTES HERE - router.post('/post/new', (req, res) => { Post.create({userId: req.user.id, title: req.body.title, body: req.body.body}) .then(() => res.json({msg: 'new post successful'})) @@ -17,58 +14,56 @@ router.get('/post/all', (req, res) => { .then(resp => res.json({posts: resp})); }); -router.post('/post/:id', function(req, res){ - +router.post('/post/:id', (req, res) => { Post.create({userId: req.user.id, title: req.body.title, body: req.body.body, parent_id: req.params.id}) .then( resp => res.json(resp)); -}) +}); -router.get('/post/:id', function(req, res){ +router.get('/post/:id', (req, res) => { Post.findAll({ where: {id: req.params.id}, - attributes: ['id', 'title', 'body', 'createdAt', 'userId', 'parent_id', [sequelize.fn('sum', sequelize.col('votes.isUpvote')), 'votecount']], - include: [ - { model: Vote, as: 'votes' } + attributes: ['id', 'title', 'body', 'createdAt', 'userId', 'parent_id', + [sequelize.fn('sum', sequelize.col('votes.isUpvote')), 'votecount'] ], - group: ['post.id','votes.id'] + include: [ { model: Vote, as: 'votes' } ], + group: ['post.id', 'votes.id'] }) - .then( resp => { - let childrenObj = {} - childrenObj[resp[0].dataValues.id] = resp[0].dataValues + .then( resp => {// TODO @ROB EXPLAIN TO ME WHAT THE FUCK IS GOING ON HERE + const childrenObj = {}; + childrenObj[resp[0].dataValues.id] = resp[0].dataValues; childrenObj[resp[0].dataValues.id].children = []; let recurseLeft = 1; - find=(children)=>{ + const find = (children) => { recurseLeft --; - if(!children.length && !recurseLeft) res.json(childrenObj[req.params.id]); - children.forEach( child => { - if(child.dataValues.id != req.params.id) { + if (!children.length && !recurseLeft) res.json(childrenObj[req.params.id]); + children.forEach(child => { + if (child.dataValues.id !== parseInt(req.params.id, 10)) { childrenObj[child.dataValues.parent_id].children.push(child.dataValues); childrenObj[child.dataValues.id] = child.dataValues; childrenObj[child.dataValues.id].children = []; } recurseLeft ++; - (async function x(){ + (async function x() { var resp = await Post.findAll({ - where: {parent_id: child.id}, - attributes: ['id', 'title', 'body', 'createdAt', 'userId', 'parent_id', [sequelize.fn('sum', sequelize.col('votes.isUpvote')), 'votecount']], - include: [ - { model: Vote, as: 'votes' } - ], - group: ['post.id','votes.id'] - }) - var resp = await find(resp); - return resp; - console.log("here"); + where: {parent_id: child.id}, + attributes: ['id', 'title', 'body', 'createdAt', 'userId', 'parent_id', [sequelize.fn('sum', sequelize.col('votes.isUpvote')), 'votecount']], + include: [ + { model: Vote, as: 'votes' } + ], + group: ['post.id', 'votes.id'] + }); + var resp = await find(resp); + return resp; })() - })} + }); + }; find(resp); - }) -}) + }); +}); router.get('/post/:id/:vote', function(req, res){ - console.log(req.user); Vote.findOne({where: {postId: req.params.id, userId: req.user.id}}) - .then( (resp) => { + .then( resp => { if(resp){ // They upvoted an upvote --> destroy vote @@ -98,8 +93,7 @@ router.get('/post/:id/:vote', function(req, res){ Vote.create({postId: req.params.id, userId: req.user.id, isUpvote: req.params.vote === 'upvote' ? 1 : -1}) .then(() => res.json({success: 'true'})); } - }) + }); }); - module.exports = router; diff --git a/server.js b/server.js index c3ca67f..c58e27f 100644 --- a/server.js +++ b/server.js @@ -7,7 +7,7 @@ const auth = require('./backend/auth'); const api = require('./backend/routes'); const cookieSession = require('cookie-session'); -const { User } = require('./backend/models.js'); +const {User} = require('./backend/models.js'); app.use(express.static(path.join(__dirname, 'public'))); app.use(bodyParser.json()); @@ -17,20 +17,18 @@ app.use(cookieSession({ })); app.use((req, res, next) => { - res.header("Access-Control-Allow-Credentials", true); - res.header("Access-Control-Allow-Origin", "http://localhost:3000"); - res.header("Access-Control-Allow-Methods", "OPTIONS, POST, GET"); - res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); - next(); - }); - - + res.header("Access-Control-Allow-Credentials", true); + res.header("Access-Control-Allow-Origin", "http://localhost:3000"); + res.header("Access-Control-Allow-Methods", "OPTIONS, POST, GET"); + res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); + next(); +}); const passport = require('passport'); const LocalStrategy = require('passport-local').Strategy; passport.serializeUser((user, done) => { - console.log('\nSERIALIZE', user); + console.log('\nSERIALIZE', user.get()); const userId = user.id; done(null, userId); }); @@ -38,7 +36,7 @@ passport.serializeUser((user, done) => { passport.deserializeUser((id, done) => { User.findById(id) .then(user => { - console.log('\nDESERIALIZE', user); + console.log('\nDESERIALIZE', user.get()); done(null, user); }) .catch(err => { @@ -75,5 +73,5 @@ app.use('/api', api); app.listen(PORT, error => { error ? console.error(error) - : console.info(`==> 🌎 Listening on port ${PORT}. Visit http://localhost:${PORT}/ in your browser.`); + : console.info('==> 🌎 Listening🌎 '); }); From 642e13a1a9b6662ae48a40e3f9d566c72cbba93b Mon Sep 17 00:00:00 2001 From: jeff <tangj1122@gmail.com> Date: Fri, 10 Nov 2017 20:43:17 -0800 Subject: [PATCH 19/19] refactor front end a lil --- backend/models.js | 12 +- backend/routes.js | 8 +- frontend/components/Feed.js | 3 +- frontend/components/Header.js | 26 ++--- frontend/components/Sidebar2.js | 167 ++++++++++++---------------- frontend/containers/AppContainer.js | 32 +----- frontend/containers/Root.dev.js | 37 +++--- 7 files changed, 114 insertions(+), 171 deletions(-) diff --git a/backend/models.js b/backend/models.js index 24d30c4..4614afd 100644 --- a/backend/models.js +++ b/backend/models.js @@ -2,19 +2,17 @@ var Sequelize = require('sequelize'); var sequelize = new Sequelize(process.env.DATABASE_NAME, 'postgres', null, { - dialect: 'postgres' + dialect: 'postgres' }); -sequelize -.authenticate() +sequelize.authenticate() .then(() => { - console.log('Connection has been established successfully.'); + console.log('Connection has been established successfully.'); }) .catch(err => { - console.error('Unable to connect to the database:', err); + console.error('Unable to connect to the database:', err); }); -// MODELS GO HERE const User = sequelize.define('user', { id: { type: Sequelize.INTEGER, @@ -46,7 +44,7 @@ const Post = sequelize.define('post', { type: Sequelize.STRING, }, parent_id: { - type: Sequelize.INTEGER,// can reference other posts. null if a parent post + type: Sequelize.INTEGER, }, }); diff --git a/backend/routes.js b/backend/routes.js index aac8745..2a50f96 100644 --- a/backend/routes.js +++ b/backend/routes.js @@ -61,13 +61,13 @@ router.get('/post/:id', (req, res) => { }); }); -router.get('/post/:id/:vote', function(req, res){ +router.get('/post/:id/:vote', (req, res) => { Vote.findOne({where: {postId: req.params.id, userId: req.user.id}}) .then( resp => { - if(resp){ + if(resp) { // They upvoted an upvote --> destroy vote - if(resp.dataValues.isUpvote && req.params.vote === 'upvote'){ + if(resp.dataValues.isUpvote && req.params.vote === 'upvote') { Vote.destroy({where: {postId: req.params.id, userId: req.user.id}}) .then(() => res.json({success: 'true'})); } @@ -79,7 +79,7 @@ router.get('/post/:id/:vote', function(req, res){ } // They upvoted a downvote --> change isUpvote to true - if(!resp.dataValues.isUpvote && req.params.vote === 'upvote'){ + if(!resp.dataValues.isUpvote && req.params.vote === 'upvote') { Vote.update({isUpvote: 1}, {where: {postId: req.params.id, userId: req.user.id}}) .then(() => res.json({success: 'true'})); } diff --git a/frontend/components/Feed.js b/frontend/components/Feed.js index 9e9ce30..ab358f3 100644 --- a/frontend/components/Feed.js +++ b/frontend/components/Feed.js @@ -15,9 +15,10 @@ class Feed extends React.Component { componentDidMount() { axios.get(localStorage.getItem('webAddress') + '/api/post/all') .then(resp => { + console.log(resp); // TODO: add voteCounts to this GET route this.setState({ posts: resp.data.posts - }) + }); }); } diff --git a/frontend/components/Header.js b/frontend/components/Header.js index a97f54f..62654dc 100644 --- a/frontend/components/Header.js +++ b/frontend/components/Header.js @@ -2,18 +2,16 @@ import React from 'react'; import styles from '../assets/stylesheets/header.css' import Paper from 'material-ui/Paper'; -const Header = () => { - return ( - <div className="header_container"> - <Paper - className="header_content" - zDepth={4} - > - <img className="header_container_logo" src="https://steemitimages.com/0x0/0x0/https://steemitimages.com/DQmX2GQsxyaVnqaEFgYygvB6ABUXbpKSsRCupdXu5onAt9y/2017-11-01%2006.28.27.jpg"/> - <h1 className="header_container_title">steemit</h1> - </Paper> - </div> - ); +export default function Header() { + return ( + <div className="header_container"> + <Paper + className="header_content" + zDepth={4} + > + <img className="header_container_logo" src="https://steemitimages.com/0x0/0x0/https://steemitimages.com/DQmX2GQsxyaVnqaEFgYygvB6ABUXbpKSsRCupdXu5onAt9y/2017-11-01%2006.28.27.jpg"/> + <h1 className="header_container_title">steemit</h1> + </Paper> + </div> + ); }; - -export default Header; diff --git a/frontend/components/Sidebar2.js b/frontend/components/Sidebar2.js index e3c0cb7..2e87f18 100644 --- a/frontend/components/Sidebar2.js +++ b/frontend/components/Sidebar2.js @@ -1,6 +1,5 @@ import React from 'react'; import styles from '../assets/stylesheets/sidebar.css' -import { connect } from 'react-redux'; import { RaisedButton, Paper, FlatButton, Dialog, TextField } from 'material-ui'; import LoginIcon from 'material-ui/svg-icons/social/person'; import RegisterIcon from 'material-ui/svg-icons/social/person-add'; @@ -53,104 +52,84 @@ class Sidebar2 extends React.Component { onClick={this.handleSubmitUser.bind(this)} />, ]; - if (this.props.side == "right") { - return ( - <div className="sidebar_container"> - <div className="sidebar_content"> - <RaisedButton - className="submit_post_button" - icon={<SubmitPostIcon />} - label="Submit Post" - backgroundColor="black" - labelColor="white" - onClick={() => this.state.user ? - window.location.hash = '/post/new' : - this.setState({modalIsOpen: true, modalTitle: 'Login'}) - } - /> - { - this.state.user ? - <div> - <h1>{this.state.username}</h1> - <RaisedButton - className="logout_button" - label="logout" - labelColor="#06d6a8" - onClick={() => axios.get(baseUrl + '/logout').then(() => this.setState({user: null}))} - backgroundColor="white" - /> - </div> : - <div className="login_register_button_container"> - <RaisedButton - className="login_button" - icon={<LoginIcon />} - labelColor="#06d6a8" - label='login' - onClick={() => this.setState({modalIsOpen: true, modalTitle: 'Login'})} - backgroundColor="white" - /> - <RaisedButton - className="register_button" - icon={<RegisterIcon />} - labelColor="white" - label='register' - onClick={() => this.setState({modalIsOpen: true, modalTitle: 'Register'})} - backgroundColor="#06d6a8" - /> - </div> + + return (this.props.side === "left" + ? <div className="sidebar_container"></div> + : + <div className="sidebar_container"> + <div className="sidebar_content"> + <RaisedButton + className="submit_post_button" + icon={<SubmitPostIcon />} + label="Submit Post" + backgroundColor="black" + labelColor="white" + onClick={() => this.state.user ? + window.location.hash = '/post/new' : + this.setState({modalIsOpen: true, modalTitle: 'Login'}) } - <Paper className="description_right_container" zDepth={5}> - <h1 className="right_sidebar_description_title"> - Description - </h1> - <p className="right_sidebar_description_content"> - This is the description of Steemit, an up and coming social media platform built on the Steem blockchain. Handling - hundreds of thousands of transactions a day with 3 second confirmmations and no fee's, this is a blockchain you don't want - to miss. - </p> - </Paper> - </div> - <div> - <Dialog - title={this.state.modalTitle} - actions={actionButtons} - open={this.state.modalIsOpen} - > - <TextField - hintText="Username" - name={this.state.username} - onChange={this.handleInputChange('username')} + /> + {this.state.user ? + <div> + <h1>{this.state.username}</h1> + <RaisedButton + className="logout_button" + label="logout" + labelColor="#06d6a8" + onClick={() => axios.get(baseUrl + '/logout').then(() => this.setState({user: null}))} + backgroundColor="white" /> - <TextField - hintText="Password" - name={this.state.password} - onChange={this.handleInputChange('password')} + </div> + : + <div className="login_register_button_container"> + <RaisedButton + className="login_button" + icon={<LoginIcon />} + labelColor="#06d6a8" + label='login' + onClick={() => this.setState({modalIsOpen: true, modalTitle: 'Login'})} + backgroundColor="white" + /> + <RaisedButton + className="register_button" + icon={<RegisterIcon />} + labelColor="white" + label='register' + onClick={() => this.setState({modalIsOpen: true, modalTitle: 'Register'})} + backgroundColor="#06d6a8" /> - </Dialog> - </div> + </div> + } + <Paper className="description_right_container" zDepth={5}> + <h1 className="right_sidebar_description_title">Description</h1> + <p className="right_sidebar_description_content"> + This is the description of Steemit, an up and coming social media platform built on the Steem blockchain. Handling + hundreds of thousands of transactions a day with 3 second confirmmations and no fee's, this is a blockchain you don't want + to miss. + </p> + </Paper> </div> - ); - } else { - return ( - <div className="sidebar_container"> + <div> + <Dialog + title={this.state.modalTitle} + actions={actionButtons} + open={this.state.modalIsOpen} + > + <TextField + hintText="Username" + name={this.state.username} + onChange={this.handleInputChange('username')} + /> + <TextField + hintText="Password" + name={this.state.password} + onChange={this.handleInputChange('password')} + /> + </Dialog> </div> - ); - } + </div> + ); } } -const mapStateToProps = (state) => { - return { - }; -}; - -const mapDispatchToProps = (dispatch) => { - return { - login: (user) => dispatch({type: 'USER', user}), - }; -}; - -export default connect( - mapStateToProps, - mapDispatchToProps -)(Sidebar2); +export default Sidebar2; diff --git a/frontend/containers/AppContainer.js b/frontend/containers/AppContainer.js index 55b7ae3..04583f8 100644 --- a/frontend/containers/AppContainer.js +++ b/frontend/containers/AppContainer.js @@ -1,16 +1,11 @@ -import PropTypes from 'prop-types'; import React from 'react'; -import { connect } from 'react-redux'; - -// Import Components import Header from '../components/Header'; import Feed from '../components/Feed'; import Sidebar2 from '../components/Sidebar2'; -// Import Styling import styles from '../assets/stylesheets/appcontainer.css' -const AppContainer = ({ name, history }) => { +export default function AppContainer() { return ( <div className="appcontainer_container"> <div className="appcontainer_header_container"> @@ -19,29 +14,8 @@ const AppContainer = ({ name, history }) => { <div className="appcontainer_body_container"> <Sidebar2 side='left' /> <Feed /> - <Sidebar2 side='right' history={history}/> + <Sidebar2 side='right' /> </div> </div> -); -}; - -AppContainer.propTypes = { - name: PropTypes.string, - history: PropTypes.object -}; - -const mapStateToProps = (state) => { - return { - name: state.name - }; -}; - -const mapDispatchToProps = (/* dispatch */) => { - return { - }; + ); }; - -export default connect( - mapStateToProps, - mapDispatchToProps -)(AppContainer); diff --git a/frontend/containers/Root.dev.js b/frontend/containers/Root.dev.js index 95797a3..93eefae 100644 --- a/frontend/containers/Root.dev.js +++ b/frontend/containers/Root.dev.js @@ -1,4 +1,3 @@ -import PropTypes from 'prop-types'; import React from 'react'; import {Provider} from 'react-redux'; import { HashRouter, Route, Switch } from 'react-router-dom'; @@ -6,29 +5,23 @@ import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'; import AppContainer from './AppContainer'; import NewPost from '../components/NewPost' import PostPage from '../components/PostPage' -import DevTools from './DevTools'; export default function Root({ store, history }) { // localStorage.setItem('webAddress', 'http://10.2.106.68:3000'); localStorage.setItem('webAddress', 'http://localhost:3000'); - return ( - <Provider store={store}> - <MuiThemeProvider> - <HashRouter> - <Switch> - <Route exact path='/' render={()=><AppContainer history={history}/>}/> - <Route exact path='/post/new' render={()=><NewPost history={history}/>}/> - <Route exact path='/post/:postId' render={(props) => { - return <PostPage postId={props.match.params.postId} /> - }} /> - </Switch> - </HashRouter> - </MuiThemeProvider> - </Provider> - ); + return ( + <Provider store={store}> + <MuiThemeProvider> + <HashRouter> + <Switch> + <Route exact path='/' component={AppContainer} /> + <Route exact path='/post/new' component={NewPost} /> + <Route exact path='/post/:postId' render={(props) => ( + <PostPage postId={props.match.params.postId} /> + )} /> + </Switch> + </HashRouter> + </MuiThemeProvider> + </Provider> + ); } - -Root.propTypes = { - store: PropTypes.object.isRequired, - history: PropTypes.object.isRequired, -};