Skip to content
Please note that GitHub no longer supports Internet Explorer.

We recommend upgrading to the latest Microsoft Edge, Google Chrome, or Firefox.

Learn more
This repository has been archived by the owner. It is now read-only.
Permalink
Branch: master
Find file Copy path
Find file Copy path
2 contributors

Users who have contributed to this file

@superyarik @ho1234c
163 lines (142 sloc) 5.46 KB
// This file contains the sagas used for async actions in our app. It's divided into
// "effects" that the sagas call (`authorize` and `logout`) and the actual sagas themselves,
// which listen for actions.
// Sagas help us gather all our side effects (network requests in this case) in one place
import {hashSync} from 'bcryptjs'
import genSalt from '../auth/salt'
import {browserHistory} from 'react-router'
import {take, call, put, fork, race} from 'redux-saga/effects'
import auth from '../auth'
import {
SENDING_REQUEST,
LOGIN_REQUEST,
REGISTER_REQUEST,
SET_AUTH,
LOGOUT,
CHANGE_FORM,
REQUEST_ERROR
} from '../actions/constants'
/**
* Effect to handle authorization
* @param {string} username The username of the user
* @param {string} password The password of the user
* @param {object} options Options
* @param {boolean} options.isRegistering Is this a register request?
*/
export function * authorize ({username, password, isRegistering}) {
// We send an action that tells Redux we're sending a request
yield put({type: SENDING_REQUEST, sending: true})
// We then try to register or log in the user, depending on the request
try {
const salt = genSalt(username)
const hash = hashSync(password, salt)
let response
// For either log in or registering, we call the proper function in the `auth`
// module, which is asynchronous. Because we're using generators, we can work
// as if it's synchronous because we pause execution until the call is done
// with `yield`!
if (isRegistering) {
response = yield call(auth.register, username, hash)
} else {
response = yield call(auth.login, username, hash)
}
return response
} catch (error) {
console.log('hi')
// If we get an error we send Redux the appropiate action and return
yield put({type: REQUEST_ERROR, error: error.message})
return false
} finally {
// When done, we tell Redux we're not in the middle of a request any more
yield put({type: SENDING_REQUEST, sending: false})
}
}
/**
* Effect to handle logging out
*/
export function * logout () {
// We tell Redux we're in the middle of a request
yield put({type: SENDING_REQUEST, sending: true})
// Similar to above, we try to log out by calling the `logout` function in the
// `auth` module. If we get an error, we send an appropiate action. If we don't,
// we return the response.
try {
const response = yield call(auth.logout)
yield put({type: SENDING_REQUEST, sending: false})
return response
} catch (error) {
yield put({type: REQUEST_ERROR, error: error.message})
}
}
/**
* Log in saga
*/
export function * loginFlow () {
// Because sagas are generators, doing `while (true)` doesn't block our program
// Basically here we say "this saga is always listening for actions"
while (true) {
// And we're listening for `LOGIN_REQUEST` actions and destructuring its payload
const request = yield take(LOGIN_REQUEST)
const {username, password} = request.data
// A `LOGOUT` action may happen while the `authorize` effect is going on, which may
// lead to a race condition. This is unlikely, but just in case, we call `race` which
// returns the "winner", i.e. the one that finished first
const winner = yield race({
auth: call(authorize, {username, password, isRegistering: false}),
logout: take(LOGOUT)
})
// If `authorize` was the winner...
if (winner.auth) {
// ...we send Redux appropiate actions
yield put({type: SET_AUTH, newAuthState: true}) // User is logged in (authorized)
yield put({type: CHANGE_FORM, newFormState: {username: '', password: ''}}) // Clear form
forwardTo('/dashboard') // Go to dashboard page
}
}
}
/**
* Log out saga
* This is basically the same as the `if (winner.logout)` of above, just written
* as a saga that is always listening to `LOGOUT` actions
*/
export function * logoutFlow () {
while (true) {
yield take(LOGOUT)
yield put({type: SET_AUTH, newAuthState: false})
yield call(logout)
forwardTo('/')
}
}
/**
* Register saga
* Very similar to log in saga!
*/
export function * registerFlow () {
while (true) {
// We always listen to `REGISTER_REQUEST` actions
const request = yield take(REGISTER_REQUEST)
const {username, password} = request.data
// We call the `authorize` task with the data, telling it that we are registering a user
// This returns `true` if the registering was successful, `false` if not
const wasSuccessful = yield call(authorize, {username, password, isRegistering: true})
// If we could register a user, we send the appropiate actions
if (wasSuccessful) {
yield put({type: SET_AUTH, newAuthState: true}) // User is logged in (authorized) after being registered
yield put({type: CHANGE_FORM, newFormState: {username: '', password: ''}}) // Clear form
forwardTo('/dashboard') // Go to dashboard page
}
}
}
// The root saga is what we actually send to Redux's middleware. In here we fork
// each saga so that they are all "active" and listening.
// Sagas are fired once at the start of an app and can be thought of as processes running
// in the background, watching actions dispatched to the store.
export default function * root () {
yield fork(loginFlow)
yield fork(logoutFlow)
yield fork(registerFlow)
}
// Little helper function to abstract going to different pages
function forwardTo (location) {
browserHistory.push(location)
}
You can’t perform that action at this time.