diff --git a/package.json b/package.json
index 715e080ab..23b6c6e56 100644
--- a/package.json
+++ b/package.json
@@ -3,19 +3,23 @@
"version": "0.0.1",
"private": true,
"dependencies": {
+ "babel-preset-es2015": "6.18.0",
"intl": "1.2.5",
"isomorphic-fetch": "2.2.1",
"lodash": "4.16.4",
"react": "15.3.2",
+ "react-addons-pure-render-mixin": "15.3.2",
"react-intl": "2.1.5",
"react-native": "0.34.1",
"react-native-button": "1.7.1",
+ "react-native-drawer": "2.3.0",
"react-native-keyboard-spacer": "0.3.0",
"react-native-router-flux": "3.36.0",
+ "react-native-vector-icons": "2.1.0",
"react-redux": "4.4.5",
"redux": "3.6.0",
- "redux-thunk": "2.1.0",
- "react-native-vector-icons": "2.1.0"
+ "redux-batched-actions": "0.1.3",
+ "redux-thunk": "2.1.0"
},
"devDependencies": {
"babel-eslint": "7.0.0",
diff --git a/src/actions/channels.js b/src/actions/channels.js
index fcc5b61ce..75ae50cd8 100644
--- a/src/actions/channels.js
+++ b/src/actions/channels.js
@@ -1,22 +1,30 @@
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
-import {bindClientFunc} from './helpers.js';
-import Client from 'client/client_instance';
-import {ChannelsTypes} from 'constants';
+import {ChannelTypes} from 'constants';
+import {batchActions} from 'redux-batched-actions';
+import Client from 'client/client_instance.js';
-export function selectChannel(channel) {
- return {
- type: ChannelsTypes.SELECT_CHANNEL,
- channelId: channel.id
+export function fetchMyChannelsAndMembers(teamId) {
+ return async (dispatch, getState) => {
+ try {
+ const channels = Client.getChannels(teamId);
+ const channelMembers = Client.getMyChannelMembers(teamId);
+
+ dispatch(batchActions([
+ {
+ type: ChannelTypes.CHANNELS_RECEIVED,
+ teamId,
+ channels: await channels
+ },
+ {
+ type: ChannelTypes.CHANNEL_MEMBERS_RECEIVED,
+ teamId,
+ channelMembers: await channelMembers
+ }
+ ]), getState);
+ } catch (err) {
+ console.error(err); // eslint-disable-line no-console
+ }
};
}
-
-export function fetchChannels() {
- return bindClientFunc(
- Client.fetchChannels,
- ChannelsTypes.FETCH_CHANNELS_REQUEST,
- ChannelsTypes.FETCH_CHANNELS_SUCCESS,
- ChannelsTypes.FETCH_CHANNELS_FAILURE
- );
-}
diff --git a/src/actions/teams.js b/src/actions/teams.js
index fe2b08968..2c9e80cc8 100644
--- a/src/actions/teams.js
+++ b/src/actions/teams.js
@@ -15,7 +15,7 @@ export function selectTeam(team) {
export function fetchTeams() {
return bindClientFunc(
- Client.fetchTeams,
+ Client.getAllTeams,
TeamsTypes.FETCH_TEAMS_REQUEST,
TeamsTypes.FETCH_TEAMS_SUCCESS,
TeamsTypes.FETCH_TEAMS_FAILURE
diff --git a/src/client/client.js b/src/client/client.js
index 08f2884ad..580a05212 100644
--- a/src/client/client.js
+++ b/src/client/client.js
@@ -11,7 +11,6 @@ const CONTENT_TYPE_JSON = 'application/json';
export default class Client {
constructor() {
- this.teamId = '';
this.logToConsole = false;
this.token = '';
this.url = '';
@@ -27,18 +26,6 @@ export default class Client {
this.url = url;
}
- setTeamId(id) {
- this.teamId = id;
- }
-
- getTeamId() {
- if (!this.teamId) {
- console.error('You are trying to use a route that requires a team_id, but you have not called setTeamId() in client.jsx'); // eslint-disable-line no-console
- }
-
- return this.teamId;
- }
-
getBaseRoute() {
return `${this.url}${this.urlVersion}`;
}
@@ -59,16 +46,16 @@ export default class Client {
return `${this.url}${this.urlVersion}/teams`;
}
- getTeamNeededRoute() {
- return `${this.url}${this.urlVersion}/teams/${this.getTeamId()}`;
+ getTeamNeededRoute(teamId) {
+ return `${this.url}${this.urlVersion}/teams/${teamId}`;
}
- getChannelsRoute() {
- return `${this.url}${this.urlVersion}/teams/${this.getTeamId()}/channels`;
+ getChannelsRoute(teamId) {
+ return `${this.url}${this.urlVersion}/teams/${teamId}/channels`;
}
- getChannelNameRoute(channelName) {
- return `${this.url}${this.urlVersion}/teams/${this.getTeamId()}/channels/name/${channelName}`;
+ getChannelNameRoute(teamId, channelName) {
+ return `${this.url}${this.urlVersion}/teams/${teamId}/channels/name/${channelName}`;
}
getChannelNeededRoute(channelId) {
@@ -209,7 +196,14 @@ export default class Client {
);
}
- fetchTeams = async () => {
+ getAllTeams = async() => {
+ return this.doFetch(
+ `${this.getTeamsRoute()}/all`,
+ {method: 'get'}
+ );
+ }
+
+ getAllTeamListings = async () => {
return this.doFetch(
`${this.getTeamsRoute()}/all_team_listings`,
{method: 'get'}
@@ -225,9 +219,16 @@ export default class Client {
);
}
- fetchChannels = async () => {
+ getChannels = async (teamId) => {
return this.doFetch(
- `${this.getChannelsRoute()}/`,
+ `${this.getChannelsRoute(teamId)}/`,
+ {method: 'get'}
+ );
+ }
+
+ getMyChannelMembers = async (teamId) => {
+ return this.doFetch(
+ `${this.getChannelsRoute(teamId)}/members`,
{method: 'get'}
);
}
diff --git a/src/components/channel_sidebar.js b/src/components/channel_sidebar.js
new file mode 100644
index 000000000..655c5958a
--- /dev/null
+++ b/src/components/channel_sidebar.js
@@ -0,0 +1,59 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import React from 'react';
+
+// import {bindActionCreators} from 'redux';
+import {connect} from 'react-redux';
+
+import {Text, View} from 'react-native';
+
+class ChannelSidebar extends React.Component {
+ static propTypes = {
+ currentTeam: React.PropTypes.object.isRequired,
+ channels: React.PropTypes.array.isRequired,
+ onSelectChannel: React.PropTypes.func
+ };
+
+ onSelectChannel = (channel) => {
+ console.log('clicked channel ' + channel.name); // eslint-disable-line no-console
+
+ // this.props.onSelectChannel(channel);
+ }
+
+ render() {
+ const channels = this.props.channels.map((channel) => {
+ return (
+ this.onSelectChannel(channel)}
+ >
+ {channel.name}
+
+ );
+ });
+
+ return (
+
+ {this.props.currentTeam.name}
+ {channels}
+
+ );
+ }
+}
+
+function mapStateToProps(state, ownProps) {
+ return {
+ ...ownProps,
+ channels: Object.keys(state.entities.channel.channelIdsByTeamId[ownProps.currentTeam.id] || {}).map((channelId) => state.entities.channel.channels[channelId])
+ };
+}
+
+export default connect(mapStateToProps)(ChannelSidebar);
diff --git a/src/components/channels_list_view.js b/src/components/channels_list_view.js
deleted file mode 100644
index e9c02fbb7..000000000
--- a/src/components/channels_list_view.js
+++ /dev/null
@@ -1,66 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-import React, {Component, PropTypes} from 'react';
-import {View, Text} from 'react-native';
-import {Actions as Routes} from 'react-native-router-flux';
-import Button from 'react-native-button';
-import _ from 'lodash';
-
-import ErrorText from 'components/error_text';
-import FormattedText from 'components/formatted_text';
-import {GlobalStyles} from 'styles';
-
-const propTypes = {
- teams: PropTypes.object.isRequired,
- channels: PropTypes.object.isRequired,
- actions: PropTypes.object.isRequired
-};
-
-class ChannelsListView extends Component {
- static propTypes = propTypes;
-
- componentWillMount() {
- this.props.actions.fetchChannels();
- }
-
- componentWillReceiveProps(nextProps) {
- const {currentChannelId} = nextProps.channels;
- if (currentChannelId &&
- currentChannelId !== this.props.channels.currentChannelId) {
- Routes.goToPostsList();
- }
- }
-
- render() {
- const {data: teams, currentTeamId} = this.props.teams;
- const currentTeam = teams[currentTeamId];
- const channels = _.values(this.props.channels.data);
- return (
-
-
- {currentTeam.display_name}
-
-
- {_.map(channels, (channel) => (
-
- ))}
-
-
-
- );
- }
-}
-
-export default ChannelsListView;
diff --git a/src/components/drawer.js b/src/components/drawer.js
new file mode 100644
index 000000000..d3e53b316
--- /dev/null
+++ b/src/components/drawer.js
@@ -0,0 +1,24 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+import React from 'react';
+import BaseDrawer from 'react-native-drawer';
+
+// Extends react-native-drawer to allow better control over the open/closed state from the parent
+export default class Drawer extends BaseDrawer {
+ static propTypes = {
+ ...BaseDrawer.propTypes,
+ onRequestClose: React.PropTypes.func.isRequired
+ };
+
+ close = (type, callback) => {
+ this.props.onRequestClose();
+
+ if (typeof type === 'function') {
+ // This function can be called with only a callback as its only argument
+ type();
+ } else if (callback) {
+ callback();
+ }
+ }
+}
diff --git a/src/components/login_view.js b/src/components/login_view.js
index d004381a7..5b9b036df 100644
--- a/src/components/login_view.js
+++ b/src/components/login_view.js
@@ -14,28 +14,30 @@ import logo from 'images/logo.png';
import {injectIntl, intlShape} from 'react-intl';
-const propTypes = {
- intl: intlShape.isRequired,
- clientConfig: PropTypes.object.isRequired,
- login: PropTypes.object.isRequired,
- actions: PropTypes.object.isRequired
-};
-
class LoginView extends Component {
- static propTypes = propTypes;
+ static propTypes = {
+ intl: intlShape.isRequired,
+ clientConfig: PropTypes.object.isRequired,
+ login: PropTypes.object.isRequired,
+ actions: PropTypes.object.isRequired
+ };
+
state = {
loginId: '',
password: ''
};
+ componentWillMount() {
+ this.props.actions.getClientConfig();
+ }
+
componentWillReceiveProps(nextProps) {
- if (this.props.login.status === 'fetching' &&
- nextProps.login.status === 'fetched') {
+ if (this.props.login.status === 'fetching' && nextProps.login.status === 'fetched') {
Routes.goToSelectTeam();
}
}
- signIn() {
+ signIn = () => {
if (this.props.login.status !== 'fetching') {
const {loginId, password} = this.state;
this.props.actions.login(loginId, password);
@@ -44,7 +46,7 @@ class LoginView extends Component {
createLoginPlaceholder() {
const {formatMessage} = this.props.intl;
- const clientConfig = this.props.clientConfig.data;
+ const clientConfig = this.props.clientConfig;
const loginPlaceholders = [];
if (clientConfig.EnableSignInWithEmail === 'true') {
@@ -75,7 +77,7 @@ class LoginView extends Component {
}
render() {
- if (!this.props.clientConfig || !this.props.clientConfig.data) {
+ if (this.props.clientConfig.loading || this.props.clientConfig.error) {
return ;
}
@@ -86,7 +88,7 @@ class LoginView extends Component {
source={logo}
/>
- {this.props.clientConfig.data.SiteName}
+ {this.props.clientConfig.SiteName}
this.setState({loginId})}
style={GlobalStyles.inputBox}
@@ -111,8 +114,10 @@ class LoginView extends Component {
autoCorrect={false}
autoCapitalize='none'
underlineColorAndroid='transparent'
+ returnKeyType='go'
+ onSubmitEditing={this.signIn}
/>
-