diff --git a/package.json b/package.json index bb7d450..2b630ea 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "babel-polyfill": "^6.16.0", "bootstrap": "^3.3.6", "font-awesome": "^4.7.0", + "history": "^4.4.0", "jquery": "^2.2.4", "jwt-decode": "^2.1.0", "react": "^15.3.2", @@ -23,7 +24,6 @@ "redux": "^3.6.0", "redux-auth-wrapper": "^0.9.0", "redux-logger": "^2.7.4", - "redux-promise-middleware": "^4.1.0", "redux-thunk": "^2.1.0" }, "devDependencies": { diff --git a/src/public/js/actions/actionCreators.js b/src/public/js/actions/actionCreators.js index de9f1b1..92b0d2a 100644 --- a/src/public/js/actions/actionCreators.js +++ b/src/public/js/actions/actionCreators.js @@ -1,8 +1,7 @@ -export const ADD_MOVIES = 'ADD_MOVIES' -export const SELECT_MOVIE = 'SELECT_MOVIE' -export const IS_USER_LOGGED_IN = 'IS_USER_LOGGED_IN' +import axios from 'axios' // Select Movie +export const SELECT_MOVIE = 'SELECT_MOVIE' export function selectMovie(index) { return { type: SELECT_MOVIE, @@ -10,6 +9,7 @@ export function selectMovie(index) { } } +export const ADD_MOVIES = 'ADD_MOVIES' export function addMovies(movies) { return { type: ADD_MOVIES, @@ -17,8 +17,66 @@ export function addMovies(movies) { } } +export const IS_USER_LOGGED_IN = 'IS_USER_LOGGED_IN' export function isUserLoggedIn() { return { type: IS_USER_LOGGED_IN, } } + +export const ADD_ERROR = 'ADD_ERROR' +export function addError(message) { + return { + type: ADD_ERROR, + payload: { + message, + } + } +} + +export const DISMISS_ERROR = 'DISMISS_ERROR' +export function dismissError() { + return { + type: DISMISS_ERROR, + } +} + +export const USER_LOGOUT = 'USER_LOGOUT' +export function userLogout() { + return { + type: USER_LOGOUT, + } +} + +export const USER_LOGIN_FULFILLED = 'USER_LOGIN_FULFILLED', + USER_LOGIN_PENDING = 'USER_LOGIN_PENDING'; +export function loginUser(username, password) { + return function(dispatch) { + dispatch({ + type: USER_LOGIN_PENDING, + }) + axios.post( + '/users/login', + { + username: username, + password: password, + }, + ).then(response => { + if (response.data && response.data.type && response.data.type === 'error') + { + dispatch({ + type: ADD_ERROR, + payload: { + message: response.data.message, + } + }) + } + dispatch({ + type: USER_LOGIN_FULFILLED, + payload: response, + }) + }).catch(error => { + console.log(error) + }) + } +} diff --git a/src/public/js/app.js b/src/public/js/app.js index 17d76b6..c4ba719 100644 --- a/src/public/js/app.js +++ b/src/public/js/app.js @@ -19,6 +19,7 @@ import store, { history } from './store' import NavBar from './components/navbar.jsx' import MovieList from './components/movie-list.jsx' import UserLoginForm from './components/user-login.jsx' +import Error from './components/errors.jsx' class Main extends React.Component { componentWillMount() { @@ -30,6 +31,7 @@ class Main extends React.Component {
+
{React.cloneElement(this.props.children, this.props)} @@ -44,6 +46,7 @@ function mapStateToProps(state) { return { movieStore: state.movieStore, userStore: state.userStore, + errors: state.errors, } } @@ -61,7 +64,6 @@ const UserIsAuthenticated = UserAuthWrapper({ predicate: user => user.isLogged, failureRedirectPath: '/users/login', }) -const Authenticated = UserIsAuthenticated((props) => props.children); ReactDOM.render(( @@ -69,9 +71,7 @@ ReactDOM.render(( - - - + diff --git a/src/public/js/components/errors.jsx b/src/public/js/components/errors.jsx new file mode 100644 index 0000000..6cd9f94 --- /dev/null +++ b/src/public/js/components/errors.jsx @@ -0,0 +1,19 @@ +import React from 'react' + +export default function Error(props) { + if (!props.errors.message) { + return null + } + return ( +
+
+
+ +

{props.errors.message}

+
+
+
+ ) +} diff --git a/src/public/js/components/navbar.jsx b/src/public/js/components/navbar.jsx index 8ddd4b2..07c0344 100644 --- a/src/public/js/components/navbar.jsx +++ b/src/public/js/components/navbar.jsx @@ -30,7 +30,7 @@ export default class NavBar extends React.Component {
  • Login
  • } {isLoggedIn && -
  • Logout
  • +
  • Logout
  • }
    diff --git a/src/public/js/components/user-login.jsx b/src/public/js/components/user-login.jsx index 16eb337..5036199 100644 --- a/src/public/js/components/user-login.jsx +++ b/src/public/js/components/user-login.jsx @@ -1,6 +1,4 @@ import React from 'react' -import axios from 'axios' -import store from '../store' export default class UserLoginForm extends React.Component { constructor(props) { @@ -14,16 +12,7 @@ export default class UserLoginForm extends React.Component { } const username = this.refs.username.value; const password = this.refs.password.value; - store.dispatch({ - type: "USER_LOGIN", - payload: axios.post( - '/users/login', - { - username: username, - password: password, - }, - ) - }) + this.props.loginUser(username, password); } render() { return ( diff --git a/src/public/js/reducers/errors.js b/src/public/js/reducers/errors.js new file mode 100644 index 0000000..7853485 --- /dev/null +++ b/src/public/js/reducers/errors.js @@ -0,0 +1,14 @@ +import { ADD_ERROR, DISMISS_ERROR } from '../actions/actionCreators' + +export default function error(state = {}, action) { + switch (action.type) { + case ADD_ERROR: + return Object.assign({}, state, { + message: action.payload.message, + }) + case DISMISS_ERROR: + return {}; + default: + return state; + } +} diff --git a/src/public/js/reducers/index.js b/src/public/js/reducers/index.js index 3595843..15d6dd4 100644 --- a/src/public/js/reducers/index.js +++ b/src/public/js/reducers/index.js @@ -3,11 +3,13 @@ import { routerReducer } from 'react-router-redux' import movieStore from './movie-store' import userStore from './users' +import errors from './errors' const rootReducer = combineReducers({ routing: routerReducer, movieStore, - userStore + userStore, + errors, }) export default rootReducer; diff --git a/src/public/js/reducers/users.js b/src/public/js/reducers/users.js index d718533..5c046dc 100644 --- a/src/public/js/reducers/users.js +++ b/src/public/js/reducers/users.js @@ -1,4 +1,4 @@ -import { IS_USER_LOGGED_IN } from '../actions/actionCreators' +import { IS_USER_LOGGED_IN, USER_LOGIN_PENDING, USER_LOGIN_FULFILLED, USER_LOGOUT } from '../actions/actionCreators' import jwtDecode from 'jwt-decode' const defaultState = { @@ -8,25 +8,26 @@ const defaultState = { isLogged: false, }; -// This actions are generated from the promise middleware - export default function userStore(state = defaultState, action) { switch (action.type) { - case 'USER_LOGIN_PENDING': + case USER_LOGIN_PENDING: return Object.assign({}, state, { userLoading: true, }) - case 'USER_LOGIN_FULFILLED': - if (action.payload.data.type === "error") { + case USER_LOGIN_FULFILLED: + const data = action.payload.data; + if (data && data.type === "error") { return logoutUser(state) } - return updateFromToken(state, action.payload.data.token) + return updateFromToken(state, data.token) case IS_USER_LOGGED_IN: let localToken = localStorage.getItem('token'); if (!localToken || localToken === "") { return state; } return updateFromToken(state, localToken) + case USER_LOGOUT: + return logoutUser(state) default: return state; } diff --git a/src/public/js/store.js b/src/public/js/store.js index 930f969..861331a 100644 --- a/src/public/js/store.js +++ b/src/public/js/store.js @@ -2,7 +2,6 @@ import { createStore, applyMiddleware, compose } from 'redux'; import { syncHistoryWithStore } from 'react-router-redux' import { hashHistory } from 'react-router' import thunk from 'redux-thunk' -import promise from 'redux-promise-middleware' import { routerMiddleware } from 'react-router-redux' // Import the root reducer @@ -10,7 +9,7 @@ import rootReducer from './reducers/index' const routingMiddleware = routerMiddleware(hashHistory) -const middlewares = [promise(), thunk, routingMiddleware]; +const middlewares = [thunk, routingMiddleware]; // Only use in development mode (set in webpack) if (process.env.NODE_ENV === `development`) { diff --git a/yarn.lock b/yarn.lock index d2ddea0..297bd59 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1568,6 +1568,16 @@ hawk@~3.1.3: hoek "2.x.x" sntp "1.x.x" +history: + version "4.4.0" + resolved "https://registry.yarnpkg.com/history/-/history-4.4.0.tgz#b1369588cb9e5d80219d0b1f866b0ac62c14a7f8" + dependencies: + invariant "^2.2.1" + loose-envify "^1.2.0" + resolve-pathname "^2.0.0" + value-equal "^0.1.1" + warning "^3.0.0" + history@^3.0.0: version "3.2.1" resolved "https://registry.yarnpkg.com/history/-/history-3.2.1.tgz#71c7497f4e6090363d19a6713bb52a1bfcdd99aa" @@ -2716,10 +2726,6 @@ redux-logger: dependencies: deep-diff "0.3.4" -redux-promise-middleware: - version "4.1.0" - resolved "https://registry.yarnpkg.com/redux-promise-middleware/-/redux-promise-middleware-4.1.0.tgz#8477866fa09837c1f08f5869c473747577f5446a" - redux-thunk: version "2.1.0" resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.1.0.tgz#c724bfee75dbe352da2e3ba9bc14302badd89a98" @@ -2807,6 +2813,10 @@ resolve-dir@^0.1.0: expand-tilde "^1.2.2" global-modules "^0.2.3" +resolve-pathname@^2.0.0: + version "2.0.2" + resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-2.0.2.tgz#e55c016eb2e9df1de98e85002282bfb38c630436" + resolve@^1.1.6, resolve@^1.1.7: version "1.1.7" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" @@ -3144,6 +3154,10 @@ validate-npm-package-license@^3.0.1: spdx-correct "~1.0.0" spdx-expression-parse "~1.0.0" +value-equal@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-0.1.1.tgz#b174df21f203c81e17f2e4d59d3a900024cbef7b" + verror@1.3.6: version "1.3.6" resolved "https://registry.yarnpkg.com/verror/-/verror-1.3.6.tgz#cff5df12946d297d2baaefaa2689e25be01c005c"