Add scene + action for login (#24)

This commit is contained in:
Thomas Hopkins 2016-10-17 18:15:25 -07:00 committed by GitHub
parent 829972694b
commit 5bdc432f71
8 changed files with 170 additions and 2 deletions

18
src/actions/login.js Normal file
View file

@ -0,0 +1,18 @@
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import {LoginTypes as types} from 'constants';
import Client from 'client/client_instance';
import {bindClientFunc} from 'actions/helpers.js';
export function login(loginId, password, mfaToken = '') {
return bindClientFunc(
Client.login,
types.LOGIN_REQUEST,
types.LOGIN_SUCCESS,
types.LOGIN_FAILURE,
loginId,
password,
mfaToken
);
}

105
src/components/login.js Normal file
View file

@ -0,0 +1,105 @@
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import React, {Component, PropTypes} from 'react';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
import {Image, StyleSheet, Text, TextInput, View} from 'react-native';
import {Actions as Routes} from 'react-native-router-flux';
import * as loginActions from 'actions/login';
import Button from 'components/button';
import ErrorText from 'components/error_text';
import {GlobalStyles} from 'styles';
import logo from 'images/logo.png';
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'column',
alignItems: 'center',
paddingTop: 200,
backgroundColor: 'white'
},
logo: {
marginBottom: 10
}
});
const propTypes = {
login: PropTypes.object.isRequired,
actions: PropTypes.object.isRequired
};
class Login extends Component {
static propTypes = propTypes;
state = {
loginId: '',
password: ''
};
componentWillReceiveProps(props) {
if (this.props.login.status === 'fetching' &&
props.login.status === 'fetched') {
Routes.SelectTeam(); // eslint-disable-line new-cap
}
}
signIn() {
if (this.props.login.status !== 'fetching') {
const {loginId, password} = this.state;
this.props.actions.login(loginId, password);
}
}
render() {
return (
<View style={styles.container}>
<Image
style={styles.logo}
source={logo}
/>
<Text>{'Mattermost'}</Text>
<Text>
{'All team communication in one place, searchable and accessible anywhere'}
</Text>
<TextInput
value={this.state.loginId}
onChangeText={(loginId) => this.setState({loginId})}
style={GlobalStyles.inputBox}
placeholder='Email or Username'
autoCorrect={false}
autoCapitalize='none'
/>
<TextInput
value={this.state.password}
onChangeText={(password) => this.setState({password})}
style={GlobalStyles.inputBox}
placeholder='Password'
autoCorrect={false}
autoCapitalize='none'
/>
<Button
onPress={() => this.signIn()}
text='Sign in'
/>
<ErrorText error={this.props.login.error}/>
</View>
);
}
}
function mapStateToProps(state) {
return {
login: state.views.login
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(loginActions, dispatch)
};
}
export default connect(mapStateToProps, mapDispatchToProps)(Login);

View file

@ -49,7 +49,7 @@ class SelectServerView extends Component {
onClick = () => {
Client.setUrl(this.state.serverUrl);
Routes.SelectTeam(); // eslint-disable-line new-cap
Routes.Login(); // eslint-disable-line new-cap
// this.props.getPing().then(() => {
// AsyncStorage.setItem('serverUrl', this.state.serverUrl, () => {

View file

@ -1,11 +1,13 @@
import * as ChannelsTypes from './channels';
import * as DeviceTypes from './device';
import * as GeneralTypes from './general';
import * as LoginTypes from './login';
import * as TeamsTypes from './teams';
export {
DeviceTypes,
GeneralTypes,
LoginTypes,
TeamsTypes,
ChannelsTypes
};

3
src/constants/login.js Normal file
View file

@ -0,0 +1,3 @@
export const LOGIN_REQUEST = 'LOGIN_REQUEST';
export const LOGIN_SUCCESS = 'LOGIN_SUCCESS';
export const LOGIN_FAILURE = 'LOGIN_FAILURE';

View file

@ -6,6 +6,7 @@ import {combineReducers} from 'redux';
import channels from './channels';
import device from './device.js';
import general from './general.js';
import login from './login';
import teams from './teams';
const entities = combineReducers({
@ -15,7 +16,8 @@ const entities = combineReducers({
});
const views = combineReducers({
device
device,
login
});
export default combineReducers({

32
src/reducers/login.js Normal file
View file

@ -0,0 +1,32 @@
// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import {LoginTypes as types} from 'constants';
const initState = {
status: 'not fetched',
error: null
};
export default function reduceLogin(state = initState, action) {
switch (action.type) {
case types.LOGIN_REQUEST:
return {...state,
status: 'fetching',
error: null
};
case types.LOGIN_SUCCESS:
return {...state,
status: 'fetched'
};
case types.LOGIN_FAILURE:
return {...state,
status: 'failed',
error: action.error
};
default:
return state;
}
}

View file

@ -6,6 +6,7 @@ import React, {Component} from 'react';
import {Scene, Router} from 'react-native-router-flux';
import ChannelsList from 'components/channels_list';
import Login from 'components/login';
import SelectServerView from 'components/select_server_view';
import SelectTeam from 'components/select_team';
@ -19,6 +20,11 @@ export default class Routes extends Component {
component={ChannelsList}
title='Channels'
/>
<Scene
key='Login'
component={Login}
title='Login'
/>
<Scene
key='SelectServerView'
component={SelectServerView}