Added feature to register a new user

This commit is contained in:
Gilmar Quinelato 2017-09-21 23:24:33 -03:00
parent d55db0fca5
commit dbf7c18b4e
13 changed files with 306 additions and 12 deletions

View File

@ -10,7 +10,7 @@ function createRequestTypes(base, types = defaultTypes) {
}
// Login events
export const LOGIN = createRequestTypes('LOGIN', [...defaultTypes, 'SET_TOKEN', 'SUBMIT']);
export const LOGIN = createRequestTypes('LOGIN', [...defaultTypes, 'SET_TOKEN', 'SUBMIT', 'REGISTER_SUBMIT', 'REGISTER_REQUEST', 'REGISTER_SUCCESS']);
export const ROOMS = createRequestTypes('ROOMS');
export const APP = createRequestTypes('APP', ['READY', 'INIT']);
export const MESSAGES = createRequestTypes('MESSAGES');

View File

@ -13,6 +13,25 @@ export function loginRequest(credentials) {
};
}
export function registerSubmit(credentials) {
return {
type: types.LOGIN.REGISTER_SUBMIT,
credentials
};
}
export function registerRequest(credentials) {
return {
type: types.LOGIN.REGISTER_REQUEST,
credentials
};
}
export function registerSuccess() {
return {
type: types.LOGIN.REGISTER_SUCCESS
};
}
export function loginSuccess(user) {
return {
type: types.LOGIN.SUCCESS,

View File

@ -6,6 +6,7 @@ import Icon from 'react-native-vector-icons/FontAwesome';
import ListServerView from '../../views/ListServerView';
import NewServerView from '../../views/NewServerView';
import LoginView from '../../views/LoginView';
import RegisterView from '../../views/RegisterView';
const PublicRoutes = StackNavigator(
{
@ -36,6 +37,12 @@ const PublicRoutes = StackNavigator(
navigationOptions: {
title: 'Login'
}
},
Register: {
screen: RegisterView,
navigationOptions: {
title: 'Register'
}
}
},
{

View File

@ -1,5 +1,5 @@
import Realm from 'realm';
// import { AsyncStorage } from 'react-native';
import { AsyncStorage } from 'react-native';
const serversSchema = {
name: 'servers',

View File

@ -122,6 +122,21 @@ const RocketChat = {
});
},
register(params, callback) {
return new Promise((resolve, reject) => {
return Meteor.call('registerUser', params, (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
if (typeof callback === 'function') {
callback(err, result);
}
});
});
},
loginWithPassword({ username, password, code }, callback) {
let params = {};
const state = reduxStore.getState();

View File

@ -1,10 +1,11 @@
import React from 'react';
import PropTypes from 'prop-types';
import { KeyboardAvoidingView } from 'react-native';
import { ViewPropTypes } from 'react-native';
import { KeyboardAwareScrollView } from 'react-native-keyboard-aware-scroll-view';
export default class KeyboardView extends React.PureComponent {
static propTypes = {
style: KeyboardAvoidingView.propTypes.style,
style: ViewPropTypes.style,
keyboardVerticalOffset: PropTypes.number,
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.node),
@ -14,9 +15,15 @@ export default class KeyboardView extends React.PureComponent {
render() {
return (
<KeyboardAvoidingView style={this.props.style} behavior='padding' keyboardVerticalOffset={this.props.keyboardVerticalOffset}>
<KeyboardAwareScrollView
contentContainerStyle={this.props.style}
behavior='position'
extraHeight={this.props.keyboardVerticalOffset}
keyboardDismissMode='interactive'
keyboardShouldPersistTaps='always'
>
{this.props.children}
</KeyboardAvoidingView>
</KeyboardAwareScrollView>
);
}
}

View File

@ -40,6 +40,20 @@ export default function login(state = initialState, action) {
token: action.token,
user: action.user
};
case types.LOGIN.REGISTER_SUBMIT:
return {
...state,
isFetching: true,
isAuthenticated: false,
failure: false
};
case types.LOGIN.REGISTER_SUCCESS:
return {
...state,
isFetching: false,
isAuthenticated: false,
failure: false
};
default:
return state;
}

View File

@ -1,13 +1,14 @@
import { AsyncStorage } from 'react-native';
import { take, put, call, takeEvery, select, all, race } from 'redux-saga/effects';
import * as types from '../actions/actionsTypes';
import { loginRequest, loginSuccess, loginFailure, setToken, logout } from '../actions/login';
import { loginRequest, registerRequest, loginSuccess, registerSuccess, loginFailure, setToken, logout } from '../actions/login';
import RocketChat from '../lib/rocketchat';
const TOKEN_KEY = 'reactnativemeteor_usertoken';
const getUser = state => state.login;
const getServer = state => state.server.server;
const loginCall = args => (args.resume ? RocketChat.login(args) : RocketChat.loginWithPassword(args));
const registerCall = args => RocketChat.register(args);
const getToken = function* getToken() {
const currentServer = yield select(getServer);
@ -86,10 +87,31 @@ const handleLoginSubmit = function* handleLoginSubmit({ credentials }) {
});
};
const handleRegisterRequest = function* handleRegisterRequest({ credentials }) {
try {
yield call(registerCall, credentials);
yield put(registerSuccess());
} catch (err) {
yield put(loginFailure(err));
}
};
const handleRegisterSubmit = function* handleRegisterSubmit({ credentials }) {
// put a login request
yield put(registerRequest(credentials));
// wait for a response
yield race({
success: take(types.LOGIN.SUCCESS),
error: take(types.LOGIN.FAILURE)
});
};
const root = function* root() {
yield takeEvery(types.SERVER.CHANGED, handleLoginWhenServerChanges);
yield takeEvery(types.LOGIN.REQUEST, handleLoginRequest);
yield takeEvery(types.LOGIN.SUCCESS, saveToken);
yield takeEvery(types.LOGIN.SUBMIT, handleLoginSubmit);
yield takeEvery(types.LOGIN.REGISTER_REQUEST, handleRegisterRequest);
yield takeEvery(types.LOGIN.REGISTER_SUBMIT, handleRegisterSubmit);
};
export default root;

View File

@ -18,7 +18,8 @@ class LoginView extends React.Component {
loginSubmit: PropTypes.func.isRequired,
Accounts_EmailOrUsernamePlaceholder: PropTypes.string,
Accounts_PasswordPlaceholder: PropTypes.string,
login: PropTypes.object
login: PropTypes.object,
navigation: PropTypes.object.isRequired
}
static navigationOptions = () => ({
@ -44,6 +45,10 @@ class LoginView extends React.Component {
Keyboard.dismiss();
}
register = () => {
this.props.navigation.navigate('Register');
}
renderTOTP = () => {
if (this.props.login.errorMessage && this.props.login.errorMessage.error === 'totp-required') {
return (
@ -68,7 +73,7 @@ class LoginView extends React.Component {
<KeyboardView style={styles.container} keyboardVerticalOffset={128}>
<View style={{ alignItems: 'center' }}>
<Image
style={styles.logo}
style={styles.loginLogo}
source={require('../images/logo.png')}
/>
</View>
@ -82,7 +87,6 @@ class LoginView extends React.Component {
autoCorrect={false}
returnKeyType='next'
autoCapitalize='none'
autoFocus
underlineColorAndroid='transparent'
onSubmitEditing={() => { this.password.focus(); }}
@ -102,10 +106,17 @@ class LoginView extends React.Component {
onSubmitEditing={this.submit}
placeholder={this.props.Accounts_PasswordPlaceholder || 'Password'}
/>
{this.renderTOTP()}
<TouchableOpacity style={styles.buttonContainer}>
<Text style={styles.button} onPress={this.submit}>LOGIN</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.buttonContainer}>
<Text style={styles.button} onPress={this.register}>REGISTER</Text>
</TouchableOpacity>
{this.props.login.error && <Text style={styles.error}>{this.props.login.error}</Text>}
</View>
<Spinner visible={this.props.login.isFetching} textContent={'Loading...'} textStyle={{ color: '#FFF' }} />

184
app/views/RegisterView.js Normal file
View File

@ -0,0 +1,184 @@
import React from 'react';
import Spinner from 'react-native-loading-spinner-overlay';
import PropTypes from 'prop-types';
import { Keyboard, Text, TextInput, View, Image, TouchableOpacity } from 'react-native';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
// import * as actions from '../actions';
import * as loginActions from '../actions/login';
import KeyboardView from '../presentation/KeyboardView';
// import { Keyboard } from 'react-native'
import styles from './Styles';
class RegisterView extends React.Component {
static propTypes = {
registerSubmit: PropTypes.func.isRequired,
Accounts_NamePlaceholder: PropTypes.string,
Accounts_EmailOrUsernamePlaceholder: PropTypes.string,
Accounts_PasswordPlaceholder: PropTypes.string,
Accounts_UsernamePlaceholder: PropTypes.string,
Accounts_RepeatPasswordPlaceholder: PropTypes.string,
login: PropTypes.object,
navigation: PropTypes.object.isRequired
}
constructor(props) {
super(props);
this.state = {
name: '',
username: '',
email: '',
password: '',
confirmPassword: ''
};
}
componentDidUpdate(prevProps) {
if (!this.props.login.isFetching && prevProps.login.isFetching &&
!this.props.login.failure) {
this.props.navigation.goBack();
}
}
submit = () => {
const { name, username, email, password, confirmPassword, code } = this.state;
if (name.trim() === '' || email.trim() === '' || username.trim() === '' ||
password.trim() === '' || confirmPassword.trim() === '') {
return;
}
this.props.registerSubmit({ name, username, email, pass: password, code });
Keyboard.dismiss();
}
renderTOTP = () => {
if (this.props.login.errorMessage && this.props.login.errorMessage.error === 'totp-required') {
return (
<TextInput
ref={ref => this.codeInput = ref}
style={styles.input}
onChangeText={code => this.setState({ code })}
keyboardType='numeric'
autoCorrect={false}
returnKeyType='done'
autoCapitalize='none'
onSubmitEditing={this.submit}
placeholder='Code'
/>
);
}
}
// {this.props.login.isFetching && <Text> LOGANDO</Text>}
render() {
return (
<KeyboardView style={styles.container} keyboardVerticalOffset={150}>
<View style={styles.loginView}>
<View style={styles.formContainer}>
<TextInput
ref={(e) => { this.name = e; }}
placeholderTextColor={'rgba(255,255,255,.2)'}
style={styles.input}
onChangeText={name => this.setState({ name })}
autoCorrect={false}
returnKeyType='next'
autoCapitalize='words'
underlineColorAndroid='transparent'
onSubmitEditing={() => { this.username.focus(); }}
placeholder={this.props.Accounts_NamePlaceholder || 'Name'}
/>
<TextInput
ref={(e) => { this.username = e; }}
placeholderTextColor={'rgba(255,255,255,.2)'}
style={styles.input}
onChangeText={username => this.setState({ username })}
autoCorrect={false}
returnKeyType='next'
autoCapitalize='none'
underlineColorAndroid='transparent'
onSubmitEditing={() => { this.email.focus(); }}
placeholder={this.props.Accounts_UsernamePlaceholder || 'Username'}
/>
<TextInput
ref={(e) => { this.email = e; }}
placeholderTextColor={'rgba(255,255,255,.2)'}
style={styles.input}
onChangeText={email => this.setState({ email })}
keyboardType='email-address'
autoCorrect={false}
returnKeyType='next'
autoCapitalize='none'
underlineColorAndroid='transparent'
onSubmitEditing={() => { this.password.focus(); }}
placeholder={this.props.Accounts_EmailOrUsernamePlaceholder || 'Email'}
/>
<TextInput
ref={(e) => { this.password = e; }}
placeholderTextColor={'rgba(255,255,255,.2)'}
style={styles.input}
onChangeText={password => this.setState({ password })}
secureTextEntry
autoCorrect={false}
returnKeyType='next'
autoCapitalize='none'
underlineColorAndroid='transparent'
onSubmitEditing={() => { this.confirmPassword.focus(); }}
placeholder={this.props.Accounts_PasswordPlaceholder || 'Password'}
/>
<TextInput
ref={(e) => { this.confirmPassword = e; }}
placeholderTextColor={'rgba(255,255,255,.2)'}
style={styles.input}
onChangeText={confirmPassword => this.setState({ confirmPassword })}
secureTextEntry
autoCorrect={false}
returnKeyType='done'
autoCapitalize='none'
underlineColorAndroid='transparent'
onSubmitEditing={this.submit}
placeholder={this.props.Accounts_RepeatPasswordPlaceholder || 'Repeat Password'}
/>
{this.renderTOTP()}
<TouchableOpacity style={styles.buttonContainer}>
<Text style={styles.button} onPress={this.submit}>REGISTER</Text>
</TouchableOpacity>
{this.props.login.error && <Text style={styles.error}>{this.props.login.error}</Text>}
</View>
<Spinner visible={this.props.login.isFetching} textContent={'Loading...'} textStyle={{ color: '#FFF' }} />
</View>
</KeyboardView>
);
}
}
function mapStateToProps(state) {
// console.log(Object.keys(state));
return {
server: state.server.server,
Accounts_UsernamePlaceholder: state.settings.Accounts_UsernamePlaceholder,
Accounts_EmailOrUsernamePlaceholder: state.settings.Accounts_EmailOrUsernamePlaceholder,
Accounts_PasswordPlaceholder: state.settings.Accounts_PasswordPlaceholder,
Accounts_RepeatPasswordPlaceholder: state.settings.Accounts_RepeatPasswordPlaceholder,
login: state.login
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(loginActions, dispatch);
}
export default connect(mapStateToProps, mapDispatchToProps)(RegisterView);

View File

@ -39,6 +39,12 @@ export default StyleSheet.create({
borderRadius: 5,
resizeMode: 'contain'
},
loginLogo: {
width: Dimensions.get('window').width - 80,
height: Dimensions.get('window').width - 80,
borderRadius: 5,
resizeMode: 'contain'
},
formContainer: {
// marginBottom: 20
},

View File

@ -28,6 +28,7 @@
"react-native-fetch-blob": "^0.10.8",
"react-native-image-picker": "^0.26.4",
"react-native-img-cache": "^1.4.0",
"react-native-keyboard-aware-scroll-view": "^0.3.0",
"react-native-loading-spinner-overlay": "^0.5.2",
"react-native-meteor": "^1.1.0",
"react-native-modal": "^3.1.0",

View File

@ -2055,7 +2055,7 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4:
safe-buffer "^5.0.1"
sha.js "^2.4.8"
create-react-class@^15.5.2:
create-react-class@^15.5.2, create-react-class@^15.6.0:
version "15.6.0"
resolved "https://registry.yarnpkg.com/create-react-class/-/create-react-class-15.6.0.tgz#ab448497c26566e1e29413e883207d57cfe7bed4"
dependencies:
@ -5729,6 +5729,14 @@ react-native-img-cache@^1.4.0:
dependencies:
crypto-js "^3.1.9-1"
react-native-keyboard-aware-scroll-view@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/react-native-keyboard-aware-scroll-view/-/react-native-keyboard-aware-scroll-view-0.3.0.tgz#b9d7b0d5b47d2bb4285fe50a3d274b10a3b5e1a7"
dependencies:
create-react-class "^15.6.0"
prop-types "^15.5.10"
react-timer-mixin "^0.13.3"
react-native-loading-spinner-overlay@^0.5.2:
version "0.5.2"
resolved "https://registry.yarnpkg.com/react-native-loading-spinner-overlay/-/react-native-loading-spinner-overlay-0.5.2.tgz#b7bcd277476d596615fd7feee601789f9bdc7acc"
@ -5943,7 +5951,7 @@ react-test-renderer@16.0.0-alpha.12:
fbjs "^0.8.9"
object-assign "^4.1.0"
react-timer-mixin@^0.13.2:
react-timer-mixin@^0.13.2, react-timer-mixin@^0.13.3:
version "0.13.3"
resolved "https://registry.yarnpkg.com/react-timer-mixin/-/react-timer-mixin-0.13.3.tgz#0da8b9f807ec07dc3e854d082c737c65605b3d22"