Commit 24a28dab by Tonk

add redux-form to LoginPage

parent 1bbceb40
......@@ -233,7 +233,7 @@ const AppStack = createSwitchNavigator(
Main: MainStack,
},
{
initialRouteName: 'Main',
initialRouteName: 'Intro',
}
);
......
import React from 'react';
import { StyleSheet, View, Text, TextInput } from 'react-native';
import { color, theme } from '../../constants/Styles';
import { Icon } from 'native-base';
const styles = StyleSheet.create({
container: {
borderBottomWidth: 1,
flexDirection: 'row',
justifyContent: 'space-between',
borderBottomColor: color.lightGrey,
},
icon: {
fontSize: 20,
marginRight: 5,
marginBottom: 2,
color: color.grey,
},
errPosition: {
bottom: -15,
position: 'absolute',
},
});
const Input = ({
placeholder,
keyboardType,
isPass,
passVisible,
onPressEye,
meta: { touched, error, warning },
input: { onChange, ...restInput },
}) => {
return (
<View>
<View style={styles.container}>
<TextInput
placeholder={placeholder ? placeholder : ''}
placeholderTextColor={color.lightGrey}
style={theme.input}
secureTextEntry={isPass ? !passVisible : false}
keyboardType={keyboardType}
onChangeText={onChange}
{...restInput}
/>
<View style={{ flexDirection: 'row', alignItems: 'flex-end' }}>
{isPass && <Icon onPress={onPressEye} name={passVisible ? 'eye' : 'eye-off'} style={styles.icon} />}
{touched && (error && <Icon name="close" style={[styles.icon, theme.textDanger]} />)}
{touched && (!error && <Icon name="checkmark" style={[styles.icon, theme.textSuccess]} />)}
</View>
</View>
{touched &&
((error && <Text style={[theme.smDescription, theme.textDanger, styles.errPosition]}>{error}</Text>) ||
(warning && (
<Text style={[theme.smDescription, theme.textWarning, styles.errPosition]}>{warning}</Text>
)))}
</View>
);
};
export default Input;
import React, { Component } from 'react';
import { Field, reduxForm } from 'redux-form';
import { View } from 'react-native';
import Input from './Input';
import { theme } from '../../constants/Styles';
import GradientBtn from '../GradientBtn';
// validation
const required = value => (value ? undefined : 'This is a required field.');
const minChar = value => (value && !/^.{6,}$/i.test(value) ? 'At least 6 characters' : undefined);
class Login extends Component {
state = {
passVisible: false,
};
render() {
return (
<>
<View style={theme.containerWithVerticalMargin}>
<Field
name="username"
keyboardType="default"
component={Input}
validate={[required]}
placeholder={'Username'}
/>
<Field
name="password"
keyboardType="default"
component={Input}
validate={[required, minChar]}
placeholder={'Password'}
isPass
passVisible={this.state.passVisible}
onPressEye={() => this.setState({ passVisible: !this.state.passVisible })}
/>
</View>
{this.props.children}
<GradientBtn onPress={this.props.handleSubmit} title={'login'} />
</>
);
}
}
const LoginForm = reduxForm({
form: 'login',
})(Login);
export default LoginForm;
import React from 'react';
import { Field, reduxForm } from 'redux-form';
import { View, Text, TouchableOpacity } from 'react-native';
import Input from './Input';
//Validation
const required = value => (value ? undefined : 'Required');
const maxLength15 = value => (value && value.length > 15 ? `Must be 15 characters or less` : undefined);
const number = value => (value && isNaN(Number(value)) ? 'Must be a number' : undefined);
const minValue = min => value => (value && value < min ? `Must be at least ${min}` : undefined);
const minValue18 = minValue(18);
const isValidEmail = value =>
value && !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i.test(value) ? 'Invalid email address' : undefined;
//Warning
const over70YearsOld = value => (value && value > 70 ? 'You might be too old for using this' : undefined);
const isYahooMail = value => (value && /.+@yahoo\.com/.test(value) ? 'Really? You still use yahoo mail ?' : undefined);
// const submit = values => {
// alert(`Validation success. Values = ~${JSON.stringify(values)}`);
// };
const ContactComponent = props => {
const { handleSubmit } = props;
return (
<View>
<Field
name="username"
keyboardType="default"
component={Input}
validate={[required, maxLength15]}
placeholder={'Username'}
/>
<Field
name="email"
keyboardType="email-address"
component={Input}
validate={isValidEmail}
warn={isYahooMail}
placeholder={'E-mail'}
/>
<Field
name="password"
keyboardType="default"
placeholder="password"
component={Input}
validate={[required]}
isPass
/>
{props.children}
<TouchableOpacity onPress={handleSubmit} style={{ margin: 10, alignItems: 'center' }}>
<Text
style={{
backgroundColor: 'steelblue',
color: 'white',
fontSize: 16,
height: 37,
width: 200,
textAlign: 'center',
padding: 10,
}}
>
Submit
</Text>
</TouchableOpacity>
</View>
);
};
const ContactForm = reduxForm({
form: 'contact', // a unique identifier for this form
})(ContactComponent);
export default ContactForm;
......@@ -20,7 +20,6 @@ export const theme = StyleSheet.create({
backgroundColor: color.defaultBg,
},
rowContainer: {
// flex: 1,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'flex-start',
......@@ -80,6 +79,9 @@ export const theme = StyleSheet.create({
textSuccess: {
color: color.success,
},
textWarning: {
color: 'orange',
},
textDanger: {
color: color.primary,
},
......
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -32,6 +32,7 @@
"react-navigation-header-buttons": "^3.0.1",
"react-redux": "^7.1.0",
"redux": "^4.0.4",
"redux-form": "^8.2.4",
"redux-thunk": "^2.3.0"
},
"devDependencies": {
......
import allReducers from './reducers'
import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
// import allReducers from './reducers';
// import { createStore, applyMiddleware } from 'redux';
// import thunk from 'redux-thunk';
export default (store = createStore(allReducers, applyMiddleware(thunk)))
// export default (store = createStore(allReducers, applyMiddleware(thunk)));
import { createStore, combineReducers } from 'redux';
import { reducer as form } from 'redux-form';
const reducers = combineReducers({
form,
// your other reducers
});
export default createStore(reducers);
import React, { Component } from 'react';
import { Container, Text, Content, Left, Right, Row, CheckBox } from 'native-base';
import { theme, color } from '../../constants/Styles';
import { Form, Field } from 'react-native-validate-form';
import InputField from '../../components/InputField';
import { StyleSheet } from 'react-native';
import GradientBtn from '../../components/GradientBtn';
import AsyncStorage from '@react-native-community/async-storage';
// validations
const required = value => (value ? undefined : 'This is a required field.');
const minChar = value => (value && !/^.{6,}$/i.test(value) ? 'At least 6 characters' : undefined);
// styles
const styles = StyleSheet.create({});
import LoginForm from '../../components/Form/LoginForm';
export default class LoginScreen extends Component {
state = {
......@@ -21,91 +11,55 @@ export default class LoginScreen extends Component {
passVisible: false,
};
submitForm() {
let submitResults = this.registerForm.validate();
let errors = [];
submitResults.forEach(item => {
errors.push({ field: item.fieldName, error: item.error });
});
this.setState({ errors: errors });
}
async submitSuccess() {
console.log('Submit Success!');
async success() {
console.log('call success function');
await AsyncStorage.setItem('alreadyLaunched', 'true');
console.log('Remembered me?', this.state.isCheck);
this.state.isCheck === true && (await AsyncStorage.setItem('RememberedLogin', 'true'));
this.props.navigation.navigate('SmartMeter');
}
submitFailed() {
console.log('Submit Failed!');
}
submit = values => {
if (values.username == 'test' && values.password == 'password') {
console.log('Login Success!', values);
this.success();
} else if (values.username == 'test') {
alert('Wrong password! \n ==> password');
} else {
alert('Wrong username & password! \n ==> (test, password)');
}
};
render() {
return (
<Container>
<Content style={theme.introContainer}>
<Text style={[theme.title, theme.textDark]}>Login</Text>
<Form
ref={ref => (this.registerForm = ref)}
validate={true}
submit={this.submitSuccess.bind(this)}
failed={this.submitFailed.bind(this)}
errors={this.state.errors}
style={theme.containerWithVerticalMargin}
>
{/* ---Username--- */}
<Field
required
component={InputField}
validations={[required]}
name="Username"
value={this.state.username}
onChangeText={val => this.setState({ username: val })}
placeholder="Username"
/>
{/* ---password--- */}
<Field
required
component={InputField}
validations={[required, minChar]}
name="password"
value={this.state.password}
onChangeText={val => this.setState({ password: val })}
placeholder="Password"
isPass={true}
passVisible={this.state.passVisible}
onPressEye={() => this.setState({ passVisible: !this.state.passVisible })}
/>
</Form>
<Row style={{ marginTop: 10 }}>
<Left>
<Row style={{ alignItems: 'center' }}>
<CheckBox
color={color.grey}
checked={this.state.isCheck}
onPress={() => this.setState({ isCheck: !this.state.isCheck })}
style={{ left: 0 }}
/>
<Text style={[theme.description, { marginLeft: 5 }]}>Remember me</Text>
</Row>
</Left>
<Right>
<Text
style={theme.description}
onPress={() => {
this.props.navigation.navigate('Forget');
}}
>
Forgot password?
</Text>
</Right>
</Row>
<GradientBtn onPress={this.submitForm.bind(this)} title={'login'} />
<LoginForm onSubmit={this.submit}>
<Row style={theme.mt1}>
<Left>
<Row style={{ alignItems: 'center' }}>
<CheckBox
color={color.grey}
checked={this.state.isCheck}
onPress={() => this.setState({ isCheck: !this.state.isCheck })}
style={{ left: 0 }}
/>
<Text style={[theme.description, { marginLeft: 5 }]}>Remember me</Text>
</Row>
</Left>
<Right>
<Text
style={theme.description}
onPress={() => {
this.props.navigation.navigate('Forget');
}}
>
Forgot password?
</Text>
</Right>
</Row>
</LoginForm>
<Text style={[theme.normalText, theme.textDark, theme.centerText, { marginTop: 20 }]}>
Don't have an account?{' '}
<Text
......
......@@ -609,7 +609,7 @@
pirates "^4.0.0"
source-map-support "^0.5.9"
"@babel/runtime@^7.0.0", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5":
"@babel/runtime@^7.0.0", "@babel/runtime@^7.2.0", "@babel/runtime@^7.4.5", "@babel/runtime@^7.5.5":
version "7.5.5"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.5.5.tgz#74fba56d35efbeca444091c7850ccd494fd2f132"
integrity sha512-28QvEGyQyNkB0/m2B4FU7IEZGK2NUrcMtT6BZEFALTguLk+AUT6ofsHtPk5QyjAdUkpMJ+/Em+quwz4HOt30AQ==
......@@ -2261,6 +2261,11 @@ es-to-primitive@^1.2.0:
is-date-object "^1.0.1"
is-symbol "^1.0.2"
es6-error@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d"
integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==
escape-html@~1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
......@@ -3149,6 +3154,11 @@ image-size@^0.6.0:
resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.6.3.tgz#e7e5c65bb534bd7cdcedd6cb5166272a85f75fb2"
integrity sha512-47xSUiQioGaB96nqtp5/q55m0aBQSQdyIloMOc/x+QVTDZLNmXE892IIDrJ0hM1A5vcNUDD5tDffkSP5lCaIIA==
immutable@3.8.2:
version "3.8.2"
resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.2.tgz#c2439951455bb39913daf281376f1530e104adf3"
integrity sha1-wkOZUUVbs5kT2vKBN28VMOEErfM=
import-fresh@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546"
......@@ -4136,6 +4146,11 @@ locate-path@^3.0.0:
p-locate "^3.0.0"
path-exists "^3.0.0"
lodash-es@^4.17.11:
version "4.17.15"
resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.15.tgz#21bd96839354412f23d7a10340e5eac6ee455d78"
integrity sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ==
lodash.merge@^4.6.1:
version "4.6.2"
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
......@@ -5819,6 +5834,24 @@ realpath-native@^1.1.0:
dependencies:
util.promisify "^1.0.0"
redux-form@^8.2.4:
version "8.2.4"
resolved "https://registry.yarnpkg.com/redux-form/-/redux-form-8.2.4.tgz#6c21c49e31b473cebe718def03487fc9d4665923"
integrity sha512-+MMD5XWVUgrtgXBuQiIYtHstPT7Lz0Izuc6vqBr1S9+7HbGgvJg4V5qDr2nigE1V5hKgsqnoAmWOWtzXc9ErZA==
dependencies:
"@babel/runtime" "^7.2.0"
es6-error "^4.1.1"
hoist-non-react-statics "^3.2.1"
invariant "^2.2.4"
is-promise "^2.1.0"
lodash "^4.17.11"
lodash-es "^4.17.11"
prop-types "^15.6.1"
react-is "^16.7.0"
react-lifecycles-compat "^3.0.4"
optionalDependencies:
immutable "3.8.2"
redux-thunk@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment