diff --git a/.eslintrc b/.eslintrc
index cd8434b..1c86c5e 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -14,7 +14,7 @@
"__DEV__": true
},
parserOptions: {
- ecmaVersion: 6,
+ ecmaVersion: 8,
# sourceType: 'module',
ecmaFeatures: {
"experimentalObjectRestSpread": true,
@@ -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..f12ce4b
--- /dev/null
+++ b/backend/auth.js
@@ -0,0 +1,44 @@
+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 => {
+ res.json({err});
+ });
+ });
+
+ router.post('/login', passport.authenticate('local'), (req, res) => {
+ res.json({user: req.user});
+ });
+
+ router.get('/logout', (req, res) => {
+ req.logout();
+ res.redirect('/');
+ });
+
+ router.get('/:username', (req, res) => {
+ User.findOne({where: {username: req.params.username}})
+ .then(user => {
+ res.json({user});
+ })
+ .catch(err => {
+ res.json({err});
+ });
+ });
+
+ router.get('/', (req, res) => {
+ res.json({user: req.user});
+ });
+
+ return router;
+};
diff --git a/backend/models.js b/backend/models.js
new file mode 100644
index 0000000..4614afd
--- /dev/null
+++ b/backend/models.js
@@ -0,0 +1,73 @@
+"use strict";
+
+var Sequelize = require('sequelize');
+var sequelize = new Sequelize(process.env.DATABASE_NAME, 'postgres', null, {
+ dialect: 'postgres'
+});
+
+sequelize.authenticate()
+.then(() => {
+ console.log('Connection has been established successfully.');
+})
+.catch(err => {
+ console.error('Unable to connect to the database:', err);
+});
+
+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,
+ },
+ parent_id: {
+ type: Sequelize.INTEGER,
+ },
+});
+
+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 9dd2215..2a50f96 100644
--- a/backend/routes.js
+++ b/backend/routes.js
@@ -1,11 +1,99 @@
const express = require('express');
const router = express.Router();
+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'}))
+ .catch(err => res.json({err}));
+});
+
+router.get('/post/all', (req, res) => {
+ Post.findAll({ where: { parent_id: null }})
+ .then(resp => res.json({posts: resp}));
+});
+
+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', (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' } ],
+ group: ['post.id', 'votes.id']
+ })
+ .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;
+ const find = (children) => {
+ recurseLeft --;
+ 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() {
+ 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;
+ })()
+ });
+ };
+ find(resp);
+ });
+});
+
+router.get('/post/:id/:vote', (req, res) => {
+ 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'}));
+ }
-// SAMPLE ROUTE
-router.use('/users', (req, res) => {
- 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/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/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..aaa8d6d
--- /dev/null
+++ b/frontend/assets/stylesheets/sidebar.css
@@ -0,0 +1,51 @@
+.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%;
+}
+
+.logout_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..ab358f3
--- /dev/null
+++ b/frontend/components/Feed.js
@@ -0,0 +1,47 @@
+import React from 'react';
+import {Link} from 'react-router-dom';
+import styles from '../assets/stylesheets/feed.css'
+import {Paper, RaisedButton} from 'material-ui';
+import axios from 'axios';
+
+class Feed extends React.Component {
+ constructor(props){
+ super(props);
+ this.state = {
+ posts: []
+ }
+ }
+
+ 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
+ });
+ });
+ }
+
+ render() {
+ return (
+
+
+ All posts
+ {this.state.posts.map(post => {
+ return (
+
+
{post.title}
+
{post.body}
+
+ );
+ })}
+
+
+ );
+ }
+}
+
+export default Feed;
diff --git a/frontend/components/Header.js b/frontend/components/Header.js
new file mode 100644
index 0000000..62654dc
--- /dev/null
+++ b/frontend/components/Header.js
@@ -0,0 +1,17 @@
+import React from 'react';
+import styles from '../assets/stylesheets/header.css'
+import Paper from 'material-ui/Paper';
+
+export default function Header() {
+ return (
+
+
+
+ steemit
+
+
+ );
+};
diff --git a/frontend/components/NewPost.js b/frontend/components/NewPost.js
new file mode 100644
index 0000000..c074a86
--- /dev/null
+++ b/frontend/components/NewPost.js
@@ -0,0 +1,49 @@
+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: '',
+ }
+ }
+
+ 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 (
+
+ {this.state.title}, {this.state.body}
+
+
+
+
+
+
+
+
+ );
+ }
+}
+
+export default NewPost;
diff --git a/frontend/components/PostPage.js b/frontend/components/PostPage.js
new file mode 100644
index 0000000..df42d68
--- /dev/null
+++ b/frontend/components/PostPage.js
@@ -0,0 +1,121 @@
+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', parentId: 1, children: [] },
+ }
+ this.handleVoteClick = this.handleVoteClick.bind(this);
+ this.handleSubmitPost = this.handleSubmitPost.bind(this);
+ }
+
+ componentDidMount() {
+ axios.get(localStorage.getItem('webAddress') + '/api/post/' + this.props.postId)
+ .then(resp => {
+ this.setState({data: resp.data});
+ console.log(resp.data);
+ })
+ .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(postId) {
+ axios.post(localStorage.getItem('webAddress') + '/api/post/' + (postId || this.props.postId), this.state)
+ .then(resp => {
+ console.log(resp);
+ window.location.hash = '/';
+ })
+ .catch(err => {console.log(err)});
+ }
+
+ 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));
+ }
+
+ render() {
+ const self = this;
+ function traverse(parent) {
+ if (!parent.children.length) {
+ return;
+ }
+ return parent.children.map(child => {
+ return (
+
+
{child.title} - user {child.userId}
+
+ {child.votecount}
+ self.handleVoteClick('upvote', child.id)}
+ >
+
+
+ self.handleVoteClick('downvote', child.id)}
+ >
+
+
+
+ {child.body} (id: {child.id})
+
+
self.handleInputChange(e)} hintText='Title' />
+
+ self.handleInputChange(e)} hintText='Reply' multiLine={true} />
+ self.handleSubmitPost(post.id)} label='Reply' secondary={true} />
+ {traverse(child)}
+
+ );
+ });
+ }
+
+ return (
+
+ {this.state.comment}
+
{this.state.data.title} - user {this.state.data.userId}
+
{this.state.data.body} (postId: {this.state.data.id})
+
+ {this.state.data.votecount}
+ this.handleVoteClick('upvote')}
+ >
+
+
+ this.handleVoteClick('downvote')}
+ >
+
+
+
+
+
+
this.handleSubmitPost()} label='Comment' primary={true} />
+ {traverse(this.state.data)}
+
+ );
+
+ }
+
+}
+
+export default NewPost;
diff --git a/frontend/components/Sidebar2.js b/frontend/components/Sidebar2.js
new file mode 100644
index 0000000..2e87f18
--- /dev/null
+++ b/frontend/components/Sidebar2.js
@@ -0,0 +1,135 @@
+import React from 'react';
+import styles from '../assets/stylesheets/sidebar.css'
+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 = localStorage.getItem('webAddress') + '/api/user';
+
+class Sidebar2 extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ modalIsOpen: false,
+ modalTitle: null,
+ username: "",
+ password: "",
+ user: null,
+ }
+ }
+
+ handleSubmitUser() {
+ const endUrl = this.state.modalTitle === 'Login' ? '/login' : '/register';
+ 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}`);
+ });
+ }
+
+ handleInputChange(key) {
+ return (e) => {
+ const state = {};
+ state[key] = e.target.value;
+ this.setState(state);
+ };
+ }
+
+ render() {
+ const actionButtons = [
+ this.setState({modalIsOpen: false})}
+ />,
+ ,
+ ];
+
+ return (this.props.side === "left"
+ ?
+ :
+
+
+
}
+ 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 ?
+
+
{this.state.username}
+ axios.get(baseUrl + '/logout').then(() => this.setState({user: null}))}
+ backgroundColor="white"
+ />
+
+ :
+
+ }
+ labelColor="#06d6a8"
+ label='login'
+ onClick={() => this.setState({modalIsOpen: true, modalTitle: 'Login'})}
+ backgroundColor="white"
+ />
+ }
+ labelColor="white"
+ label='register'
+ onClick={() => this.setState({modalIsOpen: true, modalTitle: 'Register'})}
+ 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.
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
+
+export default Sidebar2;
diff --git a/frontend/containers/AppContainer.js b/frontend/containers/AppContainer.js
index 7c29205..04583f8 100644
--- a/frontend/containers/AppContainer.js
+++ b/frontend/containers/AppContainer.js
@@ -1,32 +1,21 @@
-import PropTypes from 'prop-types';
import React from 'react';
-import { connect } from 'react-redux';
-import Title from '../components/Title';
+import Header from '../components/Header';
+import Feed from '../components/Feed';
+import Sidebar2 from '../components/Sidebar2';
-const AppContainer = ({ name }) => {
- return (
-
-
-
- );
-};
-
-AppContainer.propTypes = {
- name: PropTypes.string,
-};
+import styles from '../assets/stylesheets/appcontainer.css'
-const mapStateToProps = (state) => {
- return {
- name: state.name
- };
+export default function AppContainer() {
+ return (
+
+ );
};
-
-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 c0f8d69..93eefae 100644
--- a/frontend/containers/Root.dev.js
+++ b/frontend/containers/Root.dev.js
@@ -1,20 +1,27 @@
-import PropTypes from 'prop-types';
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';
+import AppContainer from './AppContainer';
+import NewPost from '../components/NewPost'
+import PostPage from '../components/PostPage'
-export default function Root({ store }) {
- return (
-
-
-
- );
+export default function Root({ store, history }) {
+ // localStorage.setItem('webAddress', 'http://10.2.106.68:3000');
+ localStorage.setItem('webAddress', 'http://localhost:3000');
+ return (
+
+
+
+
+
+
+ (
+
+ )} />
+
+
+
+
+ );
}
-
-Root.propTypes = {
- store: PropTypes.object.isRequired
-};
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/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/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 54019e8..245fe38 100644
--- a/package.json
+++ b/package.json
@@ -14,25 +14,37 @@
"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",
+ "history": "^4.7.2",
+ "material-ui": "^0.19.4",
"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",
"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",
"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/public/index.html b/public/index.html
index 9604aed..249c296 100644
--- a/public/index.html
+++ b/public/index.html
@@ -7,5 +7,7 @@
+
-