Skip to content

Commit

Permalink
verify authentication for app start & favorite channel
Browse files Browse the repository at this point in the history
  • Loading branch information
Darmody committed Mar 26, 2016
1 parent 8446b5b commit b56da0e
Show file tree
Hide file tree
Showing 6 changed files with 148 additions and 45 deletions.
59 changes: 34 additions & 25 deletions app/containers/Favorite/Favorite.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import styles from './Favorite.scss';

@connect(
state => ({
currentUser: state.auth.user,
song: state.favorite.song,
playing: state.favorite.playing,
playList: state.favorite.playList,
Expand All @@ -18,6 +19,7 @@ import styles from './Favorite.scss';
)
export default class Favorite extends Component {
static propTypes = {
currentUser: PropTypes.object.isRequired,
song: PropTypes.object.isRequired,
playList: PropTypes.array.isRequired,
playing: PropTypes.bool.isRequired,
Expand Down Expand Up @@ -45,6 +47,12 @@ export default class Favorite extends Component {
this.props.fetchAll();
}

componentWillReceiveProps(nextProps) {
if (this.props.currentUser.id === 0 && nextProps.currentUser.id !== 0) {
this.props.fetchAll();
}
}

componentWillUnmount() {
shortcut.stop(this.state.shortcutListener);
}
Expand Down Expand Up @@ -83,37 +91,38 @@ export default class Favorite extends Component {
this.props.jump(song);
}

render() {
const { playing, song, playList } = this.props;
const content = (
<div className={styles.content} >
<div className="player">
<Player
song={song}
playList={playList}
playing={playing}
onControl={this.onControl}
onTaste={this.onTaste}
onBan={this.onBan}
onNext={this.onNext}
onEnd={this.onNext}
onJump={this.onJump}
/>
renderContent = (props) => {
const { playing, currentUser, song, playList } = props;
const available = currentUser.id !== 0 && song.id !== 0;

if (available) {
return (
<div className={styles.content} >
<div className="player">
<Player
song={song}
playList={playList}
playing={playing}
onControl={this.onControl}
onTaste={this.onTaste}
onBan={this.onBan}
onNext={this.onNext}
onEnd={this.onNext}
onJump={this.onJump}
/>
</div>
</div>
</div>
);
const mask = (
);
}
return (
<div className={styles.mask} >
<i className="material-icons" > announcement </i>
Oops, 还没有红心歌曲,是不是忘记登录了?
</div>
);
}

return (
<div>
{ song.id === 0 && mask }
{ song.id !== 0 && content }
</div>
);
render() {
return this.renderContent(this.props);
}
}
9 changes: 7 additions & 2 deletions app/containers/HomePage/HomePage.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { Component, PropTypes } from 'react';
import { bindActionCreators } from 'redux';
import { show } from 'redux-modal';
import { connect } from 'react-redux';
import { logout } from 'reducers/auth';
import { logout, verify } from 'reducers/auth';
import { fetch as fetchCaptcha } from 'reducers/captcha';
import Navbar from './Navbar/Navbar';

Expand All @@ -12,7 +12,7 @@ import Navbar from './Navbar/Navbar';
song: state.channel.song,
}),
dispatch => ({
...bindActionCreators({ show, fetchCaptcha, logout }, dispatch)
...bindActionCreators({ show, fetchCaptcha, logout, verify }, dispatch)
})
)
export default class HomePage extends Component {
Expand All @@ -23,13 +23,18 @@ export default class HomePage extends Component {
song: PropTypes.object,
show: PropTypes.func.isRequired,
logout: PropTypes.func.isRequired,
verify: PropTypes.func.isRequired,
fetchCaptcha: PropTypes.func.isRequired,
}

static contextTypes: {
router: React.PropTypes.object
}

componentDidMount() {
this.props.verify();
}

componentWillReceiveProps(nextProps) {
if (this.props.song.id !== nextProps.song.id) {
this.notice(nextProps.song);
Expand Down
2 changes: 1 addition & 1 deletion app/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const store = configureStore();
render(
<Provider store={store}>
<Router history={hashHistory}>
{routes}
{routes(store)}
</Router>
</Provider>,
document.getElementById('root')
Expand Down
45 changes: 44 additions & 1 deletion app/reducers/__tests__/auth.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import apiMiddlewareHook from '../../middlewares/apiMiddlewareHook';
import camelizeState from '../../middlewares/camelizeState';
import _last from 'lodash/last';
import _join from 'lodash/join';
import auth, { LOGIN_SUCCESS, LOGIN_FAILURE, LOGOUT, login, logout } from '../auth';
import auth, {
LOGIN_SUCCESS, LOGIN_FAILURE, LOGOUT, VERIFY_SUCCESS, VERIFY_FAILURE,
login, logout, verify,
} from '../auth';
import signForm from '../form/signin';

const middlewares = [
Expand Down Expand Up @@ -73,6 +76,34 @@ describe('Auth Actions', function actions() {
}, 20);
});

it('VERIFY_SUCCESS', function verifySuccess(done) {
nock('http://douban.fm/')
.get('/j/v2/user_info')
.reply(200, {
user_id: 1,
});

const store = mockStore({ user: { id: 0 } });
store.dispatch(verify());
setTimeout(() => {
expect(_last(store.getActions()).type).to.equal(VERIFY_SUCCESS);
done();
}, 20);
});

it('VERIFY_FAILURE', function verifyFailure(done) {
nock('http://douban.fm/')
.get('/j/v2/user_info')
.reply(200, { msg: 'need_permission' });

const store = mockStore({ user: { id: 0 } });
store.dispatch(verify());
setTimeout(() => {
expect(store.getActions()[1].type).to.equal(VERIFY_FAILURE);
done();
}, 20);
});

it('LOGOUT', function logoutSuccess() {
expect(logout()).to.deep.equal({ type: LOGOUT });
});
Expand All @@ -94,6 +125,18 @@ describe('Auth Reducers', function reducers() {
).to.equal('账号或密码不正确');
});

it('VERIFY_SUCCESS', function verifySuccess() {
expect(
auth({ user: { id: 1 } }, { type: VERIFY_SUCCESS })
).to.deep.equal({ user: { id: 1 } });
});

it('VERIFY_FAILURE', function verifyfailure() {
expect(
auth({ user: { id: 0 } }, { type: VERIFY_FAILURE })
).to.deep.equal({ user: { id: 0, token: '' } });
});

it('LOGOUT', function logoutSuccess() {
expect(
auth({ user: { id: 1, token: 'im token' } }, { type: LOGOUT })
Expand Down
53 changes: 45 additions & 8 deletions app/reducers/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,18 @@ import { CALL_API } from 'redux-api-middleware';
import { handleActions } from 'redux-actions';
import config from '../../config';

export const VERIFY_REQUEST = 'AUTH/VERIFY_REQUEST';
export const VERIFY_SUCCESS = 'AUTH/VERIFY_SUCCESS';
export const VERIFY_FAILURE = 'AUTH/VERIFY_FAILURE';
export const LOGIN_REQUEST = 'AUTH/LOGIN_REQUEST';
export const LOGIN_SUCCESS = 'AUTH/LOGIN_SUCCESS';
export const LOGIN_FAILURE = 'AUTH/LOGIN_FAIL';
export const LOGOUT = 'AUTH/LOGOUT';

const initialState = Immutable({
user: {
id: 0
id: 0,
token: ''
}
});

Expand All @@ -25,11 +29,7 @@ export default handleActions({

return {
...state,
user: {
...state.user,
id: 0,
token: '',
}
user: initialState.user,
};
},
[LOGIN_REQUEST]: (state) => ({
Expand All @@ -55,7 +55,12 @@ export default handleActions({
...state,
logged: false
};
}
},
[VERIFY_SUCCESS]: (state) => state,
[VERIFY_FAILURE]: (state) => ({
...state,
user: initialState.user,
})
}, initialState);

export function logout() {
Expand All @@ -77,7 +82,6 @@ export function login(data) {
};
};


return dispatch => {
dispatch({
[CALL_API]: {
Expand Down Expand Up @@ -109,3 +113,36 @@ export function login(data) {
});
};
}

export function verify() {
const verifyFailed = () => {
return {
type: VERIFY_FAILURE,
};
};

return dispatch => {
dispatch({
[CALL_API]: {
endpoint: 'http://douban.fm/j/v2/user_info',
method: 'GET',
credentials: 'include',
types: [
VERIFY_REQUEST,
{
type: VERIFY_SUCCESS,
payload: (action, state, response) => {
return response.json().then(json => {
if (json.msg) {
dispatch(verifyFailed(json.err_msg));
}
return;
});
},
},
VERIFY_FAILURE,
]
}
});
};
}
25 changes: 17 additions & 8 deletions app/routes.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
import React from 'react';
import { Route, IndexRoute } from 'react-router';
import { show as showModal } from 'redux-modal';
import { App, HomePage, Channel, Favorite } from 'containers';

export default (store) => {
const requireAuth = () => {
const { auth } = store.getState();
if (auth.user.id === 0) {
store.dispatch(showModal('signin'));
}
};

export default (
<Route component={App}>
<Route path="/" component={HomePage} >
<IndexRoute component={Channel} />
<Route path="channels/:id" component={Channel} />
<Route path="favorite" component={Favorite} />
return (
<Route component={App}>
<Route path="/" component={HomePage} >
<IndexRoute component={Channel} />
<Route path="channels/:id" component={Channel} />
<Route path="favorite" component={Favorite} onEnter={requireAuth} />
</Route>
</Route>
</Route>
);
);
};

0 comments on commit b56da0e

Please sign in to comment.