diff --git a/src/actions/helpers.js b/src/actions/helpers.js
index d6a1938db..3531a8729 100644
--- a/src/actions/helpers.js
+++ b/src/actions/helpers.js
@@ -36,7 +36,6 @@ export function bindClientFunc(clientFunc, request, success, failure, ...args) {
try {
const data = await clientFunc(...args);
-
dispatch(requestSuccess(success, data), getState);
} catch (err) {
dispatch(requestFailure(failure, err));
diff --git a/src/actions/logout.js b/src/actions/logout.js
new file mode 100644
index 000000000..265d9674a
--- /dev/null
+++ b/src/actions/logout.js
@@ -0,0 +1,15 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import {LogoutTypes as types} from 'constants';
+import Client from 'client/client_instance';
+import {bindClientFunc} from 'actions/helpers.js';
+
+export function logout() {
+ return bindClientFunc(
+ Client.logout,
+ types.LOGOUT_REQUEST,
+ types.LOGOUT_SUCCESS,
+ types.LOGOUT_FAILURE
+ );
+}
diff --git a/src/client/client.js b/src/client/client.js
index a60fdc652..ad55c9391 100644
--- a/src/client/client.js
+++ b/src/client/client.js
@@ -182,6 +182,17 @@ export default class Client {
return data;
}
+ logout = async () => {
+ const response = await this.doFetchWithResponse(
+ `${this.getUsersRoute()}/logout`,
+ {method: 'post'}
+ );
+ if (response.ok) {
+ this.token = '';
+ }
+ return response;
+ }
+
getInitialLoad = async () => {
return this.doFetch(
`${this.getUsersRoute()}/initial_load`,
diff --git a/src/components/logout.js b/src/components/logout.js
new file mode 100644
index 000000000..3a5459fa5
--- /dev/null
+++ b/src/components/logout.js
@@ -0,0 +1,49 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import React, {Component, PropTypes} from 'react';
+import {TouchableHighlight, Text} from 'react-native';
+import * as logoutActions from 'actions/logout';
+import {bindActionCreators} from 'redux';
+import {connect} from 'react-redux';
+import {Actions as Routes} from 'react-native-router-flux';
+
+const propTypes = {
+ logout: PropTypes.object.isRequired,
+ actions: PropTypes.object.isRequired
+};
+
+class Logout extends Component {
+ static propTypes = propTypes;
+
+ componentWillReceiveProps(newProps) {
+ if (this.props.logout.status === 'fetching' &&
+ newProps.logout.status === 'fetched') {
+ Routes.popTo('goToLogin');
+ }
+ }
+
+ logout = () => this.props.actions.logout();
+
+ render() {
+ return (
+
+ {'logout'}
+
+ );
+ }
+}
+
+function mapStateToProps(state) {
+ return {
+ logout: state.views.logout
+ };
+}
+
+function mapDispatchToProps(dispatch) {
+ return {
+ actions: bindActionCreators(logoutActions, dispatch)
+ };
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(Logout);
diff --git a/src/constants/index.js b/src/constants/index.js
index efc5f12f9..0a5c947bc 100644
--- a/src/constants/index.js
+++ b/src/constants/index.js
@@ -2,6 +2,7 @@ import * as ChannelsTypes from './channels';
import * as DeviceTypes from './device';
import * as GeneralTypes from './general';
import * as LoginTypes from './login';
+import * as LogoutTypes from './logout';
import * as TeamsTypes from './teams';
import * as PostsTypes from './posts';
@@ -9,6 +10,7 @@ export {
DeviceTypes,
GeneralTypes,
LoginTypes,
+ LogoutTypes,
TeamsTypes,
ChannelsTypes,
PostsTypes
diff --git a/src/constants/logout.js b/src/constants/logout.js
new file mode 100644
index 000000000..69939a85f
--- /dev/null
+++ b/src/constants/logout.js
@@ -0,0 +1,3 @@
+export const LOGOUT_REQUEST = 'LOGOUT_REQUEST';
+export const LOGOUT_SUCCESS = 'LOGOUT_SUCCESS';
+export const LOGOUT_FAILURE = 'LOGOUT_FAILURE';
diff --git a/src/reducers/channels.js b/src/reducers/channels.js
index fb6bd45ed..7b0886b05 100644
--- a/src/reducers/channels.js
+++ b/src/reducers/channels.js
@@ -37,6 +37,8 @@ export default function reduceChannels(state = initState, action) {
error: action.error
};
+ case types.LOGOUT_SUCCESS:
+ return initState;
default:
return state;
}
diff --git a/src/reducers/index.js b/src/reducers/index.js
index c5bc47b83..1f00e943e 100644
--- a/src/reducers/index.js
+++ b/src/reducers/index.js
@@ -7,6 +7,7 @@ import channels from './channels';
import device from './device.js';
import general from './general.js';
import login from './login';
+import logout from './logout';
import teams from './teams';
import posts from './posts';
@@ -19,7 +20,8 @@ const entities = combineReducers({
const views = combineReducers({
device,
- login
+ login,
+ logout
});
export default combineReducers({
diff --git a/src/reducers/login.js b/src/reducers/login.js
index ff11db8cf..59040d0a1 100644
--- a/src/reducers/login.js
+++ b/src/reducers/login.js
@@ -26,6 +26,8 @@ export default function reduceLogin(state = initState, action) {
error: action.error
};
+ case types.LOGOUT_SUCCESS:
+ return initState;
default:
return state;
}
diff --git a/src/reducers/logout.js b/src/reducers/logout.js
new file mode 100644
index 000000000..24070d878
--- /dev/null
+++ b/src/reducers/logout.js
@@ -0,0 +1,31 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import {LogoutTypes as types} from 'constants';
+
+export const initState = {
+ status: 'not fetched',
+ error: null
+};
+
+export default function reduceLogout(state = initState, action) {
+ switch (action.type) {
+ case types.LOGOUT_REQUEST:
+ return {...state,
+ status: 'fetching',
+ error: null
+ };
+ case types.LOGOUT_SUCCESS:
+ return {...state,
+ status: 'fetched'
+ };
+ case types.LOGOUT_FAILURE:
+ return {...state,
+ status: 'failed',
+ error: action.error
+ };
+
+ default:
+ return state;
+ }
+}
diff --git a/src/reducers/posts.js b/src/reducers/posts.js
index 5f9b3a80d..c79858705 100644
--- a/src/reducers/posts.js
+++ b/src/reducers/posts.js
@@ -25,6 +25,8 @@ export default function reducePosts(state = initState, action) {
error: action.error
};
+ case types.LOGOUT_SUCCESS:
+ return initState;
default:
return state;
}
diff --git a/src/reducers/teams.js b/src/reducers/teams.js
index f6e6cd8a1..c4cd7d17f 100644
--- a/src/reducers/teams.js
+++ b/src/reducers/teams.js
@@ -31,6 +31,8 @@ export default function reduceTeams(state = initState, action) {
error: action.error
};
+ case types.LOGOUT_SUCCESS:
+ return initState;
default:
return state;
}
diff --git a/src/routes.js b/src/routes.js
index 5e0b7e9c6..dee447023 100644
--- a/src/routes.js
+++ b/src/routes.js
@@ -10,6 +10,8 @@ import LoginContainer from 'containers/login_container';
import SelectServerContainer from 'containers/select_server_container';
import SelectTeamContainer from 'containers/select_team_container';
import PostsListContainer from 'containers/posts_list_container';
+import Logout from 'components/logout';
+import * as logout from 'actions/logout';
import {injectIntl, intlShape} from 'react-intl';
@@ -39,16 +41,25 @@ class Routes extends Component {
key='goToChannelsList'
component={ChannelsListContainer}
title={formatMessage({id: 'routes.channels', defaultMessage: 'Channels'})}
+ renderRightButton={() =>
+
+ }
/>
+
+ }
/>
+
+ }
/>
diff --git a/test/general.test.js b/test/reducers/general.test.js
similarity index 100%
rename from test/general.test.js
rename to test/reducers/general.test.js
diff --git a/test/reducers/logout.test.js b/test/reducers/logout.test.js
new file mode 100644
index 000000000..47810b67b
--- /dev/null
+++ b/test/reducers/logout.test.js
@@ -0,0 +1,75 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import assert from 'assert';
+import reduceLogout, {initState} from 'reducers/logout';
+import {LogoutTypes as types} from 'constants';
+
+describe('logout reducer', () => {
+ describe('Init', () => {
+ let store;
+ let expectedStore;
+ before(() => {
+ store = reduceLogout(store, {type: ''});
+ expectedStore = {...initState};
+ });
+ it('should be initial state', () => {
+ assert.equal(typeof store, 'object');
+ });
+ it('have a specifc initial state', () => {
+ assert.deepEqual(store, expectedStore);
+ });
+ });
+ describe(`when ${types.LOGOUT_REQUEST}`, () => {
+ let store;
+ let expectedStore;
+ before(() => {
+ store = reduceLogout(store, {
+ type: types.LOGOUT_REQUEST
+ });
+ expectedStore = {
+ ...initState,
+ status: 'fetching'
+ };
+ });
+ it('should set status to fetching', () => {
+ assert.deepEqual(store, expectedStore);
+ });
+ });
+ describe(`when ${types.LOGOUT_SUCCESS}`, () => {
+ let store;
+ let expectedStore;
+ before(() => {
+ store = reduceLogout(store, {
+ type: types.LOGOUT_SUCCESS
+ });
+ expectedStore = {
+ ...initState,
+ status: 'fetched'
+ };
+ });
+ it('should set status to fetched and data', () => {
+ assert.deepEqual(store, expectedStore);
+ });
+ });
+ describe(`when ${types.LOGOUT_FAILURE}`, () => {
+ let store;
+ let error;
+ let expectedStore;
+ before(() => {
+ error = {id: 'the.error.id', message: 'Something went wrong'};
+ store = reduceLogout(store, {
+ type: types.LOGOUT_FAILURE,
+ error
+ });
+ expectedStore = {
+ ...initState,
+ status: 'failed',
+ error
+ };
+ });
+ it('should set status to failed and error', () => {
+ assert.deepEqual(store, expectedStore);
+ });
+ });
+});