parent
556cf0d813
commit
7027656f4b
|
@ -20,6 +20,10 @@ export const LOGIN = createRequestTypes('LOGIN', [
|
|||
'SET_USERNAME_REQUEST',
|
||||
'SET_USERNAME_SUCCESS'
|
||||
]);
|
||||
export const FORGOT_PASSWORD = createRequestTypes('FORGOT_PASSWORD', [
|
||||
...defaultTypes,
|
||||
'INIT'
|
||||
]);
|
||||
export const ROOMS = createRequestTypes('ROOMS');
|
||||
export const APP = createRequestTypes('APP', ['READY', 'INIT']);
|
||||
export const MESSAGES = createRequestTypes('MESSAGES');
|
||||
|
|
|
@ -81,3 +81,29 @@ export function logout() {
|
|||
type: types.LOGOUT
|
||||
};
|
||||
}
|
||||
|
||||
export function forgotPasswordInit() {
|
||||
return {
|
||||
type: types.FORGOT_PASSWORD.INIT
|
||||
};
|
||||
}
|
||||
|
||||
export function forgotPasswordRequest(email) {
|
||||
return {
|
||||
type: types.FORGOT_PASSWORD.REQUEST,
|
||||
email
|
||||
};
|
||||
}
|
||||
|
||||
export function forgotPasswordSuccess() {
|
||||
return {
|
||||
type: types.FORGOT_PASSWORD.SUCCESS
|
||||
};
|
||||
}
|
||||
|
||||
export function forgotPasswordFailure(err) {
|
||||
return {
|
||||
type: types.FORGOT_PASSWORD.FAILURE,
|
||||
err
|
||||
};
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import ListServerView from '../../views/ListServerView';
|
|||
import NewServerView from '../../views/NewServerView';
|
||||
import LoginView from '../../views/LoginView';
|
||||
import RegisterView from '../../views/RegisterView';
|
||||
import ForgotPasswordView from '../../views/ForgotPasswordView';
|
||||
|
||||
const PublicRoutes = StackNavigator(
|
||||
{
|
||||
|
@ -43,6 +44,12 @@ const PublicRoutes = StackNavigator(
|
|||
navigationOptions: {
|
||||
title: 'Register'
|
||||
}
|
||||
},
|
||||
ForgotPassword: {
|
||||
screen: ForgotPasswordView,
|
||||
navigationOptions: {
|
||||
title: 'Forgot my password'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
@ -144,6 +144,17 @@ const RocketChat = {
|
|||
});
|
||||
},
|
||||
|
||||
forgotPassword(email) {
|
||||
return new Promise((resolve, reject) => {
|
||||
Meteor.call('sendForgotPasswordEmail', email, (err, result) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
loginWithPassword({ username, password, code }, callback) {
|
||||
let params = {};
|
||||
const state = reduxStore.getState();
|
||||
|
|
|
@ -69,6 +69,28 @@ export default function login(state = initialState, action) {
|
|||
isFetching: false,
|
||||
isRegistering: false
|
||||
};
|
||||
case types.FORGOT_PASSWORD.INIT:
|
||||
return initialState;
|
||||
case types.FORGOT_PASSWORD.REQUEST:
|
||||
return {
|
||||
...state,
|
||||
isFetching: true,
|
||||
failure: false,
|
||||
success: false
|
||||
};
|
||||
case types.FORGOT_PASSWORD.SUCCESS:
|
||||
return {
|
||||
...state,
|
||||
isFetching: false,
|
||||
success: true
|
||||
};
|
||||
case types.FORGOT_PASSWORD.FAILURE:
|
||||
return {
|
||||
...state,
|
||||
isFetching: false,
|
||||
failure: true,
|
||||
error: action.err
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
|
@ -11,7 +11,9 @@ import {
|
|||
logout,
|
||||
registerSuccess,
|
||||
setUsernameRequest,
|
||||
setUsernameSuccess
|
||||
setUsernameSuccess,
|
||||
forgotPasswordSuccess,
|
||||
forgotPasswordFailure
|
||||
} from '../actions/login';
|
||||
import RocketChat from '../lib/rocketchat';
|
||||
|
||||
|
@ -22,6 +24,7 @@ const loginCall = args => (args.resume ? RocketChat.login(args) : RocketChat.log
|
|||
const registerCall = args => RocketChat.register(args);
|
||||
const setUsernameCall = args => RocketChat.setUsername(args);
|
||||
const logoutCall = args => RocketChat.logout(args);
|
||||
const forgotPasswordCall = args => RocketChat.forgotPassword(args);
|
||||
|
||||
const getToken = function* getToken() {
|
||||
const currentServer = yield select(getServer);
|
||||
|
@ -138,6 +141,15 @@ const handleLogout = function* handleLogout() {
|
|||
yield call(logoutCall, { server });
|
||||
};
|
||||
|
||||
const handleForgotPasswordRequest = function* handleForgotPasswordRequest({ email }) {
|
||||
try {
|
||||
yield call(forgotPasswordCall, email);
|
||||
yield put(forgotPasswordSuccess());
|
||||
} catch (err) {
|
||||
yield put(forgotPasswordFailure(err));
|
||||
}
|
||||
};
|
||||
|
||||
const root = function* root() {
|
||||
yield takeEvery(types.SERVER.CHANGED, handleLoginWhenServerChanges);
|
||||
yield takeLatest(types.LOGIN.REQUEST, handleLoginRequest);
|
||||
|
@ -149,5 +161,6 @@ const root = function* root() {
|
|||
yield takeLatest(types.LOGIN.SET_USERNAME_SUBMIT, handleSetUsernameSubmit);
|
||||
yield takeLatest(types.LOGIN.SET_USERNAME_REQUEST, handleSetUsernameRequest);
|
||||
yield takeLatest(types.LOGOUT, handleLogout);
|
||||
yield takeLatest(types.FORGOT_PASSWORD.REQUEST, handleForgotPasswordRequest);
|
||||
};
|
||||
export default root;
|
||||
|
|
|
@ -0,0 +1,116 @@
|
|||
import React from 'react';
|
||||
import Spinner from 'react-native-loading-spinner-overlay';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Text, TextInput, View, TouchableOpacity, Alert } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import * as loginActions from '../actions/login';
|
||||
import KeyboardView from '../presentation/KeyboardView';
|
||||
|
||||
import styles from './Styles';
|
||||
|
||||
class ForgotPasswordView extends React.Component {
|
||||
static propTypes = {
|
||||
forgotPasswordInit: PropTypes.func.isRequired,
|
||||
forgotPasswordRequest: PropTypes.func.isRequired,
|
||||
login: PropTypes.object,
|
||||
navigation: PropTypes.object.isRequired
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
email: '',
|
||||
invalidEmail: false
|
||||
};
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
this.props.forgotPasswordInit();
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
const { login } = this.props;
|
||||
if (login.success) {
|
||||
this.props.navigation.goBack();
|
||||
setTimeout(() => {
|
||||
Alert.alert(
|
||||
'Alert',
|
||||
'If this email is registered, ' +
|
||||
'we\'ll send instructions on how to reset your password. ' +
|
||||
'If you do not receive an email shortly, please come back and try again.'
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
validate = (email) => {
|
||||
/* eslint-disable no-useless-escape */
|
||||
const reg = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||
if (!reg.test(email)) {
|
||||
this.setState({ invalidEmail: true });
|
||||
return;
|
||||
}
|
||||
this.setState({ email, invalidEmail: false });
|
||||
}
|
||||
|
||||
resetPassword = () => {
|
||||
if (this.state.invalidEmail) {
|
||||
return;
|
||||
}
|
||||
this.props.forgotPasswordRequest(this.state.email);
|
||||
}
|
||||
|
||||
backLogin = () => {
|
||||
this.props.navigation.goBack();
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<KeyboardView
|
||||
contentContainerStyle={styles.container}
|
||||
keyboardVerticalOffset={128}
|
||||
>
|
||||
<View style={styles.loginView}>
|
||||
<View style={styles.formContainer}>
|
||||
<TextInput
|
||||
style={[styles.input_white, this.state.invalidEmail ? { borderColor: 'red' } : {}]}
|
||||
onChangeText={email => this.validate(email)}
|
||||
keyboardType='email-address'
|
||||
autoCorrect={false}
|
||||
returnKeyType='next'
|
||||
autoCapitalize='none'
|
||||
underlineColorAndroid='transparent'
|
||||
onSubmitEditing={() => this.resetPassword()}
|
||||
placeholder='Email'
|
||||
/>
|
||||
|
||||
<TouchableOpacity style={styles.buttonContainer} onPress={this.resetPassword}>
|
||||
<Text style={styles.button}>RESET PASSWORD</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
<TouchableOpacity style={styles.buttonContainer} onPress={this.backLogin}>
|
||||
<Text style={styles.button}>BACK TO LOGIN</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
{this.props.login.failure && <Text style={styles.error}>{this.props.login.error.reason}</Text>}
|
||||
</View>
|
||||
<Spinner visible={this.props.login.isFetching} textContent={'Loading...'} textStyle={{ color: '#FFF' }} />
|
||||
</View>
|
||||
</KeyboardView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
login: state.login
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return bindActionCreators(loginActions, dispatch);
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ForgotPasswordView);
|
|
@ -49,6 +49,10 @@ class LoginView extends React.Component {
|
|||
this.props.navigation.navigate('Register');
|
||||
}
|
||||
|
||||
forgotPassword = () => {
|
||||
this.props.navigation.navigate('ForgotPassword');
|
||||
}
|
||||
|
||||
renderTOTP = () => {
|
||||
if (this.props.login.errorMessage && this.props.login.errorMessage.error === 'totp-required') {
|
||||
return (
|
||||
|
@ -106,10 +110,14 @@ class LoginView extends React.Component {
|
|||
<Text style={styles.button} onPress={this.submit}>LOGIN</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
<TouchableOpacity style={[styles.buttonContainer, styles.registerContainer]}>
|
||||
<TouchableOpacity style={styles.buttonContainer}>
|
||||
<Text style={styles.button} onPress={this.register}>REGISTER</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
<TouchableOpacity style={styles.buttonContainer} onPress={this.forgotPassword}>
|
||||
<Text style={styles.button}>FORGOT MY PASSWORD</Text>
|
||||
</TouchableOpacity>
|
||||
|
||||
{this.props.login.failure && <Text style={styles.error}>{this.props.login.error.reason}</Text>}
|
||||
</View>
|
||||
<Spinner visible={this.props.login.isFetching} textContent={'Loading...'} textStyle={{ color: '#FFF' }} />
|
||||
|
|
|
@ -93,9 +93,6 @@ export default StyleSheet.create({
|
|||
backgroundColor: '#1d74f5',
|
||||
marginBottom: 20
|
||||
},
|
||||
registerContainer: {
|
||||
marginBottom: 0
|
||||
},
|
||||
button: {
|
||||
textAlign: 'center',
|
||||
color: 'white',
|
||||
|
|
Loading…
Reference in New Issue