Added initial version of MainView/MainContainer
This commit is contained in:
parent
71c0707a90
commit
86b33e0164
30 changed files with 750 additions and 382 deletions
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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'}
|
||||
);
|
||||
}
|
||||
|
|
|
|||
59
src/components/channel_sidebar.js
Normal file
59
src/components/channel_sidebar.js
Normal file
|
|
@ -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 (
|
||||
<Text
|
||||
key={channel.id}
|
||||
style={{height: 30, width: 100}}
|
||||
onPress={() => this.onSelectChannel(channel)}
|
||||
>
|
||||
{channel.name}
|
||||
</Text>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: 'gray',
|
||||
flex: 1,
|
||||
paddingTop: 20
|
||||
}}
|
||||
>
|
||||
<Text style={{height: 60, width: 100}}>{this.props.currentTeam.name}</Text>
|
||||
{channels}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
|
@ -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 (
|
||||
<View style={GlobalStyles.container}>
|
||||
<Text style={GlobalStyles.header}>
|
||||
{currentTeam.display_name}
|
||||
</Text>
|
||||
<FormattedText
|
||||
style={GlobalStyles.subheader}
|
||||
id='mobile.components.channels_list_view.yourChannels'
|
||||
defaultMessage='Your channels:'
|
||||
/>
|
||||
{_.map(channels, (channel) => (
|
||||
<Button
|
||||
key={channel.id}
|
||||
onPress={() => this.props.actions.selectChannel(channel)}
|
||||
style={GlobalStyles.buttonListItemText}
|
||||
containerStyle={GlobalStyles.buttonListItem}
|
||||
>
|
||||
{channel.display_name}
|
||||
</Button>
|
||||
))}
|
||||
|
||||
<ErrorText error={this.props.channels.error}/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ChannelsListView;
|
||||
24
src/components/drawer.js
Normal file
24
src/components/drawer.js
Normal file
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 <Loading/>;
|
||||
}
|
||||
|
||||
|
|
@ -86,7 +88,7 @@ class LoginView extends Component {
|
|||
source={logo}
|
||||
/>
|
||||
<Text style={GlobalStyles.header}>
|
||||
{this.props.clientConfig.data.SiteName}
|
||||
{this.props.clientConfig.SiteName}
|
||||
</Text>
|
||||
<FormattedText
|
||||
style={GlobalStyles.subheader}
|
||||
|
|
@ -94,6 +96,7 @@ class LoginView extends Component {
|
|||
defaultMessage='All team communication in one place, searchable and accessible anywhere'
|
||||
/>
|
||||
<TextInput
|
||||
ref='loginId'
|
||||
value={this.state.loginId}
|
||||
onChangeText={(loginId) => 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}
|
||||
/>
|
||||
<Button onPress={() => this.signIn()}>
|
||||
<Button onPress={this.signIn}>
|
||||
<FormattedText
|
||||
id='login.signIn'
|
||||
defaultMessage='Sign in'
|
||||
|
|
|
|||
99
src/components/main_view.js
Normal file
99
src/components/main_view.js
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import PureRenderMixin from 'react-addons-pure-render-mixin';
|
||||
|
||||
import Drawer from 'react-native-drawer';
|
||||
import ChannelSidebar from './channel_sidebar.js';
|
||||
import Loading from './loading.js';
|
||||
import RightSidebarMenu from './right_sidebar_menu.js';
|
||||
import {StatusBar, Text, TouchableHighlight, View} from 'react-native';
|
||||
|
||||
export default class MainView extends React.Component {
|
||||
static propTypes = {
|
||||
actions: React.PropTypes.object.isRequired,
|
||||
currentTeam: React.PropTypes.object,
|
||||
currentChannel: React.PropTypes.object
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
|
||||
|
||||
this.state = {
|
||||
leftSidebarOpen: false,
|
||||
rightSidebarOpen: false
|
||||
};
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
this.props.actions.fetchMyChannelsAndMembers(this.props.currentTeam.Id);
|
||||
}
|
||||
|
||||
openLeftSidebar = () => {
|
||||
this.setState({leftSidebarOpen: true});
|
||||
}
|
||||
|
||||
closeLeftSidebar = () => {
|
||||
this.setState({leftSidebarOpen: false});
|
||||
}
|
||||
|
||||
openRightSidebar = () => {
|
||||
this.setState({rightSidebarOpen: true});
|
||||
}
|
||||
|
||||
closeRightSidebar = () => {
|
||||
this.setState({rightSidebarOpen: false});
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!this.props.currentChannel) {
|
||||
return <Loading/>;
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={{flex: 1}}>
|
||||
<StatusBar barStyle='default'/>
|
||||
<Drawer
|
||||
open={this.state.leftSidebarOpen}
|
||||
type='displace'
|
||||
content={<ChannelSidebar currentTeam={this.props.currentTeam}/>}
|
||||
side='left'
|
||||
tapToClose={true}
|
||||
onCloseStart={this.closeLeftSidebar}
|
||||
openDrawerOffset={0.2}
|
||||
>
|
||||
<Drawer
|
||||
open={this.state.rightSidebarOpen}
|
||||
type='displace'
|
||||
content={<RightSidebarMenu/>}
|
||||
side='right'
|
||||
tapToClose={true}
|
||||
onCloseStart={this.closeRightSidebar}
|
||||
openDrawerOffset={0.2}
|
||||
>
|
||||
<View style={{flex: 1, flexDirection: 'row', justifyContent: 'space-between', paddingTop: 20}}>
|
||||
<TouchableHighlight
|
||||
onPress={this.openLeftSidebar}
|
||||
style={{height: 50, width: 50}}
|
||||
>
|
||||
<Text>{'<'}</Text>
|
||||
</TouchableHighlight>
|
||||
<TouchableHighlight
|
||||
onPress={this.openRightSidebar}
|
||||
style={{height: 50, width: 50}}
|
||||
>
|
||||
<Text>{'>'}</Text>
|
||||
</TouchableHighlight>
|
||||
</View>
|
||||
<Text>{this.props.currentTeam.id + ' - ' + this.props.currentTeam.name}</Text>
|
||||
<Text>{this.props.currentChannel.id + ' - ' + this.props.currentChannel.name}</Text>
|
||||
</Drawer>
|
||||
</Drawer>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React, {Component, PropTypes} from 'react';
|
||||
|
||||
import {StyleSheet, Text, View} from 'react-native';
|
||||
import _ from 'lodash';
|
||||
import ErrorText from 'components/error_text';
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
paddingTop: 200,
|
||||
backgroundColor: 'white'
|
||||
}
|
||||
});
|
||||
|
||||
const propTypes = {
|
||||
posts: PropTypes.object.isRequired,
|
||||
currentTeamId: PropTypes.string.isRequired,
|
||||
currentChannelId: PropTypes.string.isRequired,
|
||||
actions: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
export default class PostsListView extends Component {
|
||||
static propTypes = propTypes;
|
||||
|
||||
componentWillMount() {
|
||||
this.props.actions.fetchPosts(
|
||||
this.props.currentTeamId,
|
||||
this.props.currentChannelId
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
const posts = _.values(this.props.posts.data);
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
{posts.map((post) => (
|
||||
<Text key={post.id}>
|
||||
{`${post.user_id} - ${post.message}`}
|
||||
</Text>
|
||||
))}
|
||||
<ErrorText error={this.props.posts.error}/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
208
src/components/right_sidebar_menu.js
Normal file
208
src/components/right_sidebar_menu.js
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import FormattedText from './formatted_text.js';
|
||||
import Icon from 'react-native-vector-icons/FontAwesome';
|
||||
import {
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableHighlight,
|
||||
View
|
||||
} from 'react-native';
|
||||
|
||||
const Styles = StyleSheet.create({
|
||||
container: {
|
||||
backgroundColor: '#2071a7',
|
||||
flex: 1,
|
||||
paddingTop: 20
|
||||
},
|
||||
item: {
|
||||
alignItems: 'center',
|
||||
height: 40,
|
||||
paddingLeft: 10,
|
||||
paddingRight: 10,
|
||||
flex: 1,
|
||||
flexDirection: 'row'
|
||||
},
|
||||
itemText: {
|
||||
color: 'white'
|
||||
},
|
||||
icon: {
|
||||
color: 'white',
|
||||
width: 25
|
||||
},
|
||||
mentionIcon: {
|
||||
fontSize: 17,
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
divider: {
|
||||
borderColor: 'rgba(255, 255, 255, 0.6)',
|
||||
borderTopWidth: StyleSheet.hairlineWidth
|
||||
}
|
||||
});
|
||||
|
||||
export default class RightSidebarMenu extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<ScrollView style={Styles.container}>
|
||||
<Item>
|
||||
<Text style={[Styles.icon, Styles.mentionIcon]}>{'@'}</Text>
|
||||
<FormattedText
|
||||
style={Styles.itemText}
|
||||
id='sidebar_right_menu.recentMentions'
|
||||
defaultMessage='Recent Mentions'
|
||||
/>
|
||||
</Item>
|
||||
<Item>
|
||||
<Icon
|
||||
style={Styles.icon}
|
||||
name='flag'
|
||||
/>
|
||||
<FormattedText
|
||||
style={Styles.itemText}
|
||||
id='sidebar_right_menu.flagged'
|
||||
defaultMessage='Flagged Posts'
|
||||
/>
|
||||
</Item>
|
||||
<Divider/>
|
||||
<Item>
|
||||
<Icon
|
||||
style={Styles.icon}
|
||||
name='cog'
|
||||
/>
|
||||
<FormattedText
|
||||
style={Styles.itemText}
|
||||
id='sidebar_right_menu.accountSettings'
|
||||
defaultMessage='Account Settings'
|
||||
/>
|
||||
</Item>
|
||||
<Item>
|
||||
<Icon
|
||||
style={Styles.icon}
|
||||
name='user-plus'
|
||||
/>
|
||||
<FormattedText
|
||||
style={Styles.itemText}
|
||||
id='sidebar_right_menu.inviteNew'
|
||||
defaultMessage='Invite New Member'
|
||||
/>
|
||||
</Item>
|
||||
<Item>
|
||||
<Icon
|
||||
style={Styles.icon}
|
||||
name='link'
|
||||
/>
|
||||
<FormattedText
|
||||
style={Styles.itemText}
|
||||
id='sidebar_right_menu.teamLink'
|
||||
defaultMessage='Get Team Invite Link'
|
||||
/>
|
||||
</Item>
|
||||
<Divider/>
|
||||
<Item>
|
||||
<Icon
|
||||
style={Styles.icon}
|
||||
name='globe'
|
||||
/>
|
||||
<FormattedText
|
||||
style={Styles.itemText}
|
||||
id='sidebar_right_menu.teamSettings'
|
||||
defaultMessage='Team Settings'
|
||||
/>
|
||||
</Item>
|
||||
<Item>
|
||||
<Icon
|
||||
style={Styles.icon}
|
||||
name='users'
|
||||
/>
|
||||
<FormattedText
|
||||
style={Styles.itemText}
|
||||
id='sidebar_right_menu.manageMembers'
|
||||
defaultMessage='Manage Members'
|
||||
/>
|
||||
</Item>
|
||||
<Item>
|
||||
<Icon
|
||||
style={Styles.icon}
|
||||
name='exchange'
|
||||
/>
|
||||
<FormattedText
|
||||
style={Styles.itemText}
|
||||
id='sidebar_right_menu.switch_team'
|
||||
defaultMessage='Team Selection'
|
||||
/>
|
||||
</Item>
|
||||
<Divider/>
|
||||
<Item>
|
||||
<Icon
|
||||
style={Styles.icon}
|
||||
name='question'
|
||||
/>
|
||||
<FormattedText
|
||||
style={Styles.itemText}
|
||||
id='sidebar_right_menu.help'
|
||||
defaultMessage='Help'
|
||||
/>
|
||||
</Item>
|
||||
<Item>
|
||||
<Icon
|
||||
style={Styles.icon}
|
||||
name='phone'
|
||||
/>
|
||||
<FormattedText
|
||||
style={Styles.itemText}
|
||||
id='sidebar_right_menu.report'
|
||||
defaultMessage='Report a Problem'
|
||||
/>
|
||||
</Item>
|
||||
<Item>
|
||||
<Icon
|
||||
style={Styles.icon}
|
||||
name='info'
|
||||
/>
|
||||
<FormattedText
|
||||
style={Styles.itemText}
|
||||
id='navbar_dropdown.about'
|
||||
defaultMessage='About Mattermost'
|
||||
/>
|
||||
</Item>
|
||||
<Divider/>
|
||||
<Item>
|
||||
<Icon
|
||||
style={Styles.icon}
|
||||
name='sign-out'
|
||||
/>
|
||||
<FormattedText
|
||||
style={Styles.itemText}
|
||||
id='sidebar_right_menu.logout'
|
||||
defaultMessage='Logout'
|
||||
/>
|
||||
</Item>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function Item({onPress, children}) {
|
||||
return (
|
||||
<TouchableHighlight
|
||||
underlayColor='rgba(255, 255, 255, 0.3)'
|
||||
onPress={onPress}
|
||||
>
|
||||
<View style={Styles.item}>
|
||||
{children}
|
||||
</View>
|
||||
</TouchableHighlight>
|
||||
);
|
||||
}
|
||||
Item.propTypes = {
|
||||
onPress: React.PropTypes.func.isRequired,
|
||||
children: React.PropTypes.node
|
||||
};
|
||||
|
||||
function Divider() {
|
||||
return <View style={Styles.divider}/>;
|
||||
}
|
||||
|
|
@ -35,16 +35,12 @@ class SelectServerView extends Component {
|
|||
|
||||
onClick = () => {
|
||||
Client.setUrl(this.state.serverUrl);
|
||||
this.props.actions.getClientConfig();
|
||||
Routes.goToLogin();
|
||||
|
||||
// this.props.actions.getPing().then(() => {
|
||||
// AsyncStorage.setItem('serverUrl', this.state.serverUrl, () => {
|
||||
// if (!this.props.ping.error) {
|
||||
// this.props.onProceed();
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
this.props.actions.getPing().then(() => {
|
||||
if (!this.props.ping.loading && !this.props.ping.error) {
|
||||
Routes.goToLogin();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
|
|
|
|||
|
|
@ -6,37 +6,54 @@ import React, {Component, PropTypes} from 'react';
|
|||
import {View, Image, Text} from 'react-native';
|
||||
import {Actions as Routes} from 'react-native-router-flux';
|
||||
import Button from 'react-native-button';
|
||||
import Loading from 'components/loading.js';
|
||||
import Icon from 'react-native-vector-icons/MaterialIcons';
|
||||
import _ from 'lodash';
|
||||
|
||||
import ErrorText from 'components/error_text';
|
||||
import {GlobalStyles} from 'styles';
|
||||
import logo from 'images/logo.png';
|
||||
import FormattedText from 'components/formatted_text';
|
||||
|
||||
const propTypes = {
|
||||
clientConfig: PropTypes.object.isRequired,
|
||||
teams: PropTypes.object.isRequired,
|
||||
actions: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
export default class SelectTeamView extends Component {
|
||||
static propTypes = propTypes;
|
||||
static propTypes = {
|
||||
clientConfig: PropTypes.object.isRequired,
|
||||
teams: PropTypes.object.isRequired,
|
||||
actions: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
componentWillMount() {
|
||||
this.props.actions.fetchTeams();
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const {currentTeamId} = nextProps.teams;
|
||||
if (currentTeamId &&
|
||||
currentTeamId !== this.props.teams.currentTeamId) {
|
||||
Routes.goToChannelsList();
|
||||
}
|
||||
onSelectTeam(team) {
|
||||
Routes.goToMain({currentTeamId: team.id});
|
||||
}
|
||||
|
||||
render() {
|
||||
const teams = _.values(this.props.teams.data);
|
||||
if (this.props.teams.status !== 'fetched') {
|
||||
return <Loading/>;
|
||||
}
|
||||
|
||||
const teams = [];
|
||||
for (const id of Object.keys(this.props.teams.data)) {
|
||||
const team = this.props.teams.data[id];
|
||||
|
||||
teams.push(
|
||||
<Button
|
||||
key={id}
|
||||
onPress={() => this.onSelectTeam(team)}
|
||||
style={GlobalStyles.buttonListItemText}
|
||||
containerStyle={GlobalStyles.buttonListItem}
|
||||
>
|
||||
{team.display_name}
|
||||
<Icon
|
||||
name='keyboard-arrow-right'
|
||||
size={24}
|
||||
color='#777'
|
||||
/>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={GlobalStyles.container}>
|
||||
|
|
@ -45,7 +62,7 @@ export default class SelectTeamView extends Component {
|
|||
source={logo}
|
||||
/>
|
||||
<Text style={GlobalStyles.header}>
|
||||
{this.props.clientConfig.data.SiteName}
|
||||
{this.props.clientConfig.SiteName}
|
||||
</Text>
|
||||
<FormattedText
|
||||
style={GlobalStyles.subheader}
|
||||
|
|
@ -57,21 +74,7 @@ export default class SelectTeamView extends Component {
|
|||
id='signup_team.choose'
|
||||
defaultMessage='Your teams:'
|
||||
/>
|
||||
{_.map(teams, (team) => (
|
||||
<Button
|
||||
key={team.id}
|
||||
onPress={() => this.props.actions.selectTeam(team)}
|
||||
style={GlobalStyles.buttonListItemText}
|
||||
containerStyle={GlobalStyles.buttonListItem}
|
||||
>
|
||||
{team.display_name}
|
||||
<Icon
|
||||
name='keyboard-arrow-right'
|
||||
size={24}
|
||||
color='#777'
|
||||
/>
|
||||
</Button>
|
||||
))}
|
||||
{teams}
|
||||
<ErrorText error={this.props.teams.error}/>
|
||||
</View>
|
||||
);
|
||||
|
|
|
|||
8
src/constants/channel.js
Normal file
8
src/constants/channel.js
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
export const CHANNEL_RECEIVED = 'mattermost/channels/channel_received';
|
||||
export const CHANNELS_RECEIVED = 'mattermost/channels/channels_received';
|
||||
|
||||
export const CHANNEL_MEMBER_RECEIVED = 'mattermost/channels/channel_member_received';
|
||||
export const CHANNEL_MEMBERS_RECEVED = 'mattermost/channels/channel_members_received';
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
export const SELECT_CHANNEL = 'SELECT_CHANNEL';
|
||||
|
||||
export const FETCH_CHANNELS_REQUEST = 'FETCH_CHANNELS_REQUEST';
|
||||
export const FETCH_CHANNELS_SUCCESS = 'FETCH_CHANNELS_SUCCESS';
|
||||
export const FETCH_CHANNELS_FAILURE = 'FETCH_CHANNELS_FAILURE';
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import * as ChannelsTypes from './channels';
|
||||
import * as ChannelTypes from './channel';
|
||||
import * as DeviceTypes from './device';
|
||||
import * as GeneralTypes from './general';
|
||||
import * as LoginTypes from './login';
|
||||
|
|
@ -15,6 +15,6 @@ export {
|
|||
LoginTypes,
|
||||
LogoutTypes,
|
||||
TeamsTypes,
|
||||
ChannelsTypes,
|
||||
ChannelTypes,
|
||||
PostsTypes
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,22 +0,0 @@
|
|||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {bindActionCreators} from 'redux';
|
||||
import {connect} from 'react-redux';
|
||||
import * as channelActions from 'actions/channels';
|
||||
import ChannelsListView from 'components/channels_list_view';
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
teams: state.entities.teams,
|
||||
channels: state.entities.channels
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators(channelActions, dispatch)
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ChannelsListView);
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
import {bindActionCreators} from 'redux';
|
||||
import {connect} from 'react-redux';
|
||||
import * as loginActions from 'actions/login';
|
||||
import {getClientConfig} from 'actions/general';
|
||||
import LoginView from 'components/login_view';
|
||||
|
||||
function mapStateToProps(state) {
|
||||
|
|
@ -15,7 +16,10 @@ function mapStateToProps(state) {
|
|||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators(loginActions, dispatch)
|
||||
actions: bindActionCreators({
|
||||
...loginActions,
|
||||
getClientConfig
|
||||
}, dispatch)
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
42
src/containers/main_container.js
Normal file
42
src/containers/main_container.js
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {bindActionCreators} from 'redux';
|
||||
import {connect} from 'react-redux';
|
||||
import {fetchMyChannelsAndMembers} from 'actions/channels.js';
|
||||
import MainView from 'components/main_view.js';
|
||||
|
||||
function mapStateToProps(state, ownProps) {
|
||||
let currentChannel;
|
||||
if (ownProps.currentChannelId) {
|
||||
currentChannel = state.entities.channel.channels[ownProps.currentChannelId];
|
||||
} else {
|
||||
// TODO figure out the town square id before this
|
||||
const channelIds = state.entities.channel.channelIdsByTeamId[ownProps.currentTeamId] || {};
|
||||
|
||||
for (const channelId of Object.keys(channelIds)) {
|
||||
const channel = state.entities.channel.channels[channelId];
|
||||
|
||||
if (channel.name === 'town-square') {
|
||||
currentChannel = channel;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...ownProps,
|
||||
currentTeam: state.entities.teams.data[ownProps.currentTeamId] || {},
|
||||
currentChannel
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({
|
||||
fetchMyChannelsAndMembers
|
||||
}, dispatch)
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(MainView);
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {bindActionCreators} from 'redux';
|
||||
import {connect} from 'react-redux';
|
||||
import * as postActions from 'actions/posts';
|
||||
import PostsListView from 'components/posts_list_view';
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
posts: state.entities.posts,
|
||||
currentTeamId: state.entities.teams.currentTeamId,
|
||||
currentChannelId: state.entities.channels.currentChannelId
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators(postActions, dispatch)
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(PostsListView);
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
import {bindActionCreators} from 'redux';
|
||||
import {connect} from 'react-redux';
|
||||
import {getPing, getClientConfig} from 'actions/general';
|
||||
import {getPing} from 'actions/general';
|
||||
import SelectServerView from 'components/select_server_view';
|
||||
|
||||
function mapStateToProps(state) {
|
||||
|
|
@ -15,7 +15,7 @@ function mapStateToProps(state) {
|
|||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators({getPing, getClientConfig}, dispatch)
|
||||
actions: bindActionCreators({getPing}, dispatch)
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
99
src/reducers/channel.js
Normal file
99
src/reducers/channel.js
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {ChannelTypes} from 'constants';
|
||||
import {combineReducers} from 'redux';
|
||||
|
||||
export function channels(state = {}, action) {
|
||||
switch (action.type) {
|
||||
case ChannelTypes.CHANNEL_RECEIVED:
|
||||
return {
|
||||
...state,
|
||||
[action.channel.id]: action.channel
|
||||
};
|
||||
|
||||
case ChannelTypes.CHANNELS_RECEIVED: {
|
||||
const nextState = {...state};
|
||||
|
||||
for (const channel of action.channels) {
|
||||
nextState[channel.id] = channel;
|
||||
}
|
||||
|
||||
return nextState;
|
||||
}
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
export function channelIdsByTeamId(state = {}, action) {
|
||||
switch (action.type) {
|
||||
case ChannelTypes.CHANNEL_RECEIVED: {
|
||||
const channel = action.channel;
|
||||
|
||||
return {
|
||||
...state,
|
||||
[channel.team_id]: {
|
||||
...state[channel.team_id],
|
||||
[channel.id]: channel.id
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
case ChannelTypes.CHANNELS_RECEIVED: {
|
||||
const nextState = {...state};
|
||||
|
||||
for (const channel of action.channels) {
|
||||
nextState[channel.team_id] = {
|
||||
...nextState[channel.team_id],
|
||||
[channel.id]: channel.id
|
||||
};
|
||||
}
|
||||
|
||||
return nextState;
|
||||
}
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
export function channelMembers(state = {}, action) {
|
||||
switch (action.type) {
|
||||
|
||||
case ChannelTypes.CHANNEL_MEMBER_RECEIVED: {
|
||||
const channelMember = action.channelMember;
|
||||
|
||||
return {
|
||||
...state,
|
||||
[`${channelMember.channel_id}-${channelMember.user_id}`]: channelMember
|
||||
};
|
||||
}
|
||||
|
||||
case ChannelTypes.CHANNEL_MEMBERS_RECEIVED: {
|
||||
const nextState = {...state};
|
||||
|
||||
for (const channelMember of action.channelMembers) {
|
||||
nextState[`${channelMember.channel_id}-${channelMember.user_id}`] = channelMember;
|
||||
}
|
||||
|
||||
return nextState;
|
||||
}
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
export default combineReducers({
|
||||
|
||||
// A map of channel IDs to channels
|
||||
channels,
|
||||
|
||||
// A map of team IDs to pseudo-sets of channelIds
|
||||
channelIdsByTeamId,
|
||||
|
||||
// A map of "channelId-userId" to channel members
|
||||
channelMembers
|
||||
});
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import _ from 'lodash';
|
||||
import {ChannelsTypes, LogoutTypes} from 'constants';
|
||||
|
||||
export const initState = {
|
||||
status: 'not fetched',
|
||||
error: null,
|
||||
data: {},
|
||||
currentChannelId: null
|
||||
};
|
||||
|
||||
// TODO: this can be extracted into a util
|
||||
function zipById(channels) {
|
||||
return _.zipObject(_.map(channels, 'id'), channels);
|
||||
}
|
||||
|
||||
export default function reduceChannels(state = initState, action) {
|
||||
switch (action.type) {
|
||||
|
||||
case ChannelsTypes.SELECT_CHANNEL:
|
||||
return {...state,
|
||||
currentChannelId: action.channelId
|
||||
};
|
||||
|
||||
case ChannelsTypes.FETCH_CHANNELS_REQUEST:
|
||||
return {...state,
|
||||
status: 'fetching',
|
||||
error: null
|
||||
};
|
||||
case ChannelsTypes.FETCH_CHANNELS_SUCCESS:
|
||||
return {...state,
|
||||
status: 'fetched',
|
||||
data: zipById(action.data.channels)
|
||||
};
|
||||
case ChannelsTypes.FETCH_CHANNELS_FAILURE:
|
||||
return {...state,
|
||||
status: 'failed',
|
||||
error: action.error
|
||||
};
|
||||
|
||||
case LogoutTypes.LOGOUT_SUCCESS:
|
||||
return initState;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
|
@ -2,34 +2,62 @@
|
|||
// See License.txt for license information.
|
||||
|
||||
import {combineReducers} from 'redux';
|
||||
import {initialState, handle} from './helpers.js';
|
||||
import {initialState} from './helpers.js';
|
||||
import {GeneralTypes} from 'constants';
|
||||
|
||||
export const initState = initialState();
|
||||
|
||||
// TODO: clientConfig should be cleared when the user logs out.
|
||||
// We can't do so until it's extracted into its own store with more reducers
|
||||
export function clientConfig(state = initState, action) {
|
||||
return handle(
|
||||
GeneralTypes.CLIENT_CONFIG_REQUEST,
|
||||
GeneralTypes.CLIENT_CONFIG_SUCCESS,
|
||||
GeneralTypes.CLIENT_CONFIG_FAILURE,
|
||||
state,
|
||||
action
|
||||
);
|
||||
function ping(state = {}, action) {
|
||||
switch (action.type) {
|
||||
case GeneralTypes.PING_REQUEST:
|
||||
return {
|
||||
...action.data,
|
||||
loading: true
|
||||
};
|
||||
case GeneralTypes.PING_SUCCESS:
|
||||
return {
|
||||
...action.data,
|
||||
loading: false,
|
||||
error: null
|
||||
};
|
||||
case GeneralTypes.PING_FAILURE:
|
||||
return {
|
||||
...action.data,
|
||||
loading: false,
|
||||
error: action.error
|
||||
};
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
export function ping(state = initialState(), action) {
|
||||
return handle(
|
||||
GeneralTypes.PING_REQUEST,
|
||||
GeneralTypes.PING_SUCCESS,
|
||||
GeneralTypes.PING_FAILURE,
|
||||
state,
|
||||
action
|
||||
);
|
||||
function clientConfig(state = {}, action) {
|
||||
switch (action.type) {
|
||||
case GeneralTypes.CLIENT_CONFIG_REQUEST:
|
||||
return {
|
||||
...action.data,
|
||||
loading: true
|
||||
};
|
||||
case GeneralTypes.CLIENT_CONFIG_SUCCESS:
|
||||
return {
|
||||
...action.data,
|
||||
loading: false,
|
||||
error: null
|
||||
};
|
||||
case GeneralTypes.CLIENT_CONFIG_FAILURE:
|
||||
return {
|
||||
...action.data,
|
||||
loading: false,
|
||||
error: action.error
|
||||
};
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
export default combineReducers({
|
||||
clientConfig,
|
||||
ping
|
||||
ping,
|
||||
clientConfig
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
import {combineReducers} from 'redux';
|
||||
|
||||
import channels from './channels';
|
||||
import channel from './channel.js';
|
||||
import device from './device.js';
|
||||
import general from './general.js';
|
||||
import login from './login';
|
||||
|
|
@ -12,7 +12,7 @@ import teams from './teams';
|
|||
import posts from './posts';
|
||||
|
||||
const entities = combineReducers({
|
||||
channels,
|
||||
channel,
|
||||
general,
|
||||
teams,
|
||||
posts
|
||||
|
|
|
|||
|
|
@ -12,16 +12,19 @@ export default function reduceLogin(state = initState, action) {
|
|||
switch (action.type) {
|
||||
|
||||
case LoginTypes.LOGIN_REQUEST:
|
||||
return {...state,
|
||||
return {
|
||||
...state,
|
||||
status: 'fetching',
|
||||
error: null
|
||||
};
|
||||
case LoginTypes.LOGIN_SUCCESS:
|
||||
return {...state,
|
||||
return {
|
||||
...state,
|
||||
status: 'fetched'
|
||||
};
|
||||
case LoginTypes.LOGIN_FAILURE:
|
||||
return {...state,
|
||||
return {
|
||||
...state,
|
||||
status: 'failed',
|
||||
error: action.error
|
||||
};
|
||||
|
|
|
|||
|
|
@ -14,22 +14,26 @@ export default function reduceTeams(state = initState, action) {
|
|||
switch (action.type) {
|
||||
|
||||
case TeamsTypes.SELECT_TEAM:
|
||||
return {...state,
|
||||
return {
|
||||
...state,
|
||||
currentTeamId: action.teamId
|
||||
};
|
||||
|
||||
case TeamsTypes.FETCH_TEAMS_REQUEST:
|
||||
return {...state,
|
||||
return {
|
||||
...state,
|
||||
status: 'fetching',
|
||||
error: null
|
||||
};
|
||||
case TeamsTypes.FETCH_TEAMS_SUCCESS:
|
||||
return {...state,
|
||||
return {
|
||||
...state,
|
||||
status: 'fetched',
|
||||
data: action.data
|
||||
};
|
||||
case TeamsTypes.FETCH_TEAMS_FAILURE:
|
||||
return {...state,
|
||||
return {
|
||||
...state,
|
||||
status: 'failed',
|
||||
error: action.error
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,15 +1,13 @@
|
|||
/* eslint-disable
|
||||
class-methods-use-this,
|
||||
react/require-optimization */
|
||||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import React, {Component} from 'react';
|
||||
import {Scene, Router} from 'react-native-router-flux';
|
||||
|
||||
import ChannelsListContainer from 'containers/channels_list_container';
|
||||
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 MainContainer from 'containers/main_container.js';
|
||||
import Logout from 'components/logout';
|
||||
import * as logout from 'actions/logout';
|
||||
|
||||
|
|
@ -26,11 +24,6 @@ class Routes extends Component {
|
|||
return (
|
||||
<Router>
|
||||
<Scene key='root'>
|
||||
<Scene
|
||||
key='goToLogin'
|
||||
component={LoginContainer}
|
||||
title={formatMessage({id: 'mobile.routes.login', defaultMessage: 'Login'})}
|
||||
/>
|
||||
<Scene
|
||||
key='goToSelectServer'
|
||||
component={SelectServerContainer}
|
||||
|
|
@ -38,12 +31,9 @@ class Routes extends Component {
|
|||
initial={true}
|
||||
/>
|
||||
<Scene
|
||||
key='goToChannelsList'
|
||||
component={ChannelsListContainer}
|
||||
title={formatMessage({id: 'mobile.routes.channels', defaultMessage: 'Channels'})}
|
||||
renderRightButton={() =>
|
||||
<Logout actions={logout}/>
|
||||
}
|
||||
key='goToLogin'
|
||||
component={LoginContainer}
|
||||
title={formatMessage({id: 'mobile.routes.login', defaultMessage: 'Login'})}
|
||||
/>
|
||||
<Scene
|
||||
key='goToSelectTeam'
|
||||
|
|
@ -54,12 +44,9 @@ class Routes extends Component {
|
|||
}
|
||||
/>
|
||||
<Scene
|
||||
key='goToPostsList'
|
||||
component={PostsListContainer}
|
||||
title={formatMessage({id: 'mobile.routes.postsList', defaultMessage: 'Posts List'})}
|
||||
renderRightButton={() =>
|
||||
<Logout actions={logout}/>
|
||||
}
|
||||
key='goToMain'
|
||||
component={MainContainer}
|
||||
hideNavBar={true}
|
||||
/>
|
||||
</Scene>
|
||||
</Router>
|
||||
|
|
|
|||
|
|
@ -1,21 +1,23 @@
|
|||
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
|
||||
// See License.txt for license information.
|
||||
|
||||
import {applyMiddleware, createStore} from 'redux';
|
||||
import {composeWithDevTools} from 'remote-redux-devtools';
|
||||
import {applyMiddleware, compose, createStore} from 'redux';
|
||||
import devTools from 'remote-redux-devtools';
|
||||
import {enableBatching} from 'redux-batched-actions';
|
||||
import rootReducer from 'reducers/index.js';
|
||||
import thunk from 'redux-thunk';
|
||||
|
||||
export default function configureStore(preloadedState) {
|
||||
const store = createStore(
|
||||
rootReducer,
|
||||
enableBatching(rootReducer),
|
||||
preloadedState,
|
||||
composeWithDevTools({
|
||||
name: 'Mattermost',
|
||||
hostname: 'localhost',
|
||||
port: 5678
|
||||
})(
|
||||
applyMiddleware(thunk)
|
||||
compose(
|
||||
applyMiddleware(thunk),
|
||||
devTools({
|
||||
name: 'Mattermost',
|
||||
hostname: 'localhost',
|
||||
port: 5678
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
|
|
@ -28,4 +30,4 @@ export default function configureStore(preloadedState) {
|
|||
}
|
||||
|
||||
return store;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,13 +2,14 @@
|
|||
// See License.txt for license information.
|
||||
|
||||
import {applyMiddleware, createStore} from 'redux';
|
||||
import {enableBatching} from 'redux-batched-actions';
|
||||
import rootReducer from 'reducers/index.js';
|
||||
import thunk from 'redux-thunk';
|
||||
|
||||
export default function configureStore(preloadedState) {
|
||||
return createStore(
|
||||
rootReducer,
|
||||
enableBatching(rootReducer),
|
||||
preloadedState,
|
||||
applyMiddleware(thunk)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue