├── ForgetPassword.js
├── Login.js
├── Navbar.js
├── Register.js
├── ResetPassword.js
├── RestrictedPage.js
├── Server.js
├── Style.js
├── cli.js
├── icons
├── ic_action_more.png
├── ic_action_more@2x.png
├── ic_action_more@3x.png
├── ic_action_refresh.png
├── ic_action_refresh@2x.png
├── ic_action_refresh@3x.png
├── ic_action_search.png
├── ic_action_search@2x.png
└── ic_action_search@3x.png
├── index.js
├── package.json
├── preview.gif
└── readme.md
/ForgetPassword.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React, {
4 | Component,
5 | ScrollView,
6 | Text,
7 | TouchableOpacity,
8 | TouchableHighlight,
9 | View,
10 | TextInput,
11 | ToastAndroid
12 | } from 'react-native';
13 |
14 | import api from './Server';
15 | import styles from './Style';
16 |
17 | export default class extends Component {
18 | constructor(props) {
19 | super(props);
20 |
21 | this.state = {
22 | data: {
23 | phone: undefined,
24 | role: 2
25 | },
26 | loading: false
27 | };
28 | }
29 |
30 | render() {
31 | let fields = [
32 | {ref: 'phone', placeholder: 'Phone Number', keyboardType: 'numeric', secureTextEntry: false, style: [styles.inputText]},
33 | ];
34 |
35 | return(
36 |
37 |
38 | {'FORGET PASSWORD'}
39 |
40 |
41 | this.onFocus({...fields[0]})} onChangeText={(text) => this.state.data.phone = text} />
42 |
43 | this.gotoRoute('reset')}>
44 | {'Reset password?'}
45 |
46 | this.onSubmit(fields)}>
47 | {this.state.loading ? 'Please Wait . . .' : 'Submit'}
48 |
49 |
50 | );
51 | }
52 |
53 | onFocus(argument) {
54 | setTimeout(() => {
55 | let scrollResponder = this.refs.forgetForm.getScrollResponder();
56 | scrollResponder.scrollResponderScrollNativeHandleToKeyboard(
57 | React.findNodeHandle(this.refs[argument.ref]), 110, true
58 | );
59 | }, 50);
60 | }
61 |
62 | onSubmit() {
63 | if (this.state.loading) {
64 | ToastAndroid.show('Please Wait . . .', ToastAndroid.SHORT);
65 | return null;
66 | }
67 |
68 | let valid = true;
69 |
70 | Object.keys(this.state.data).map((val, key) => {
71 | if ([null, undefined, 'null', 'undefined', ''].indexOf(this.state.data[val]) > -1) valid = false;
72 | });
73 |
74 | if (!valid) return null;
75 |
76 | this.setState({loading: true});
77 |
78 | api.auth.forget(this.state.data)
79 | .then((response) => {
80 | if (!response.ok) throw Error(response.statusText || response._bodyText);
81 | return response.json();
82 | })
83 | .then((responseData) => {
84 | console.log(responseData);
85 | ToastAndroid.show(JSON.stringify(responseData), ToastAndroid.LONG);
86 | })
87 | .catch((error) => {
88 | console.log(error);
89 | ToastAndroid.show(String(error).replace('Error: ',''), ToastAndroid.LONG);
90 | })
91 | .done(() => {
92 | this.setState({loading: false});
93 | });
94 | }
95 |
96 | goBack() {
97 | if (this.props.navigator) {
98 | this.props.navigator.pop();
99 | }
100 | }
101 |
102 | gotoRoute(name) {
103 | if (this.props.navigator && this.props.navigator.getCurrentRoutes()[this.props.navigator.getCurrentRoutes().length-1].name != name) {
104 | this.props.navigator.push({name: name});
105 | }
106 | }
107 |
108 | replaceRoute(name) {
109 | if (this.props.navigator && this.props.navigator.getCurrentRoutes()[this.props.navigator.getCurrentRoutes().length-1].name != name) {
110 | this.props.navigator.replace({name: name});
111 | }
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/Login.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React, {
4 | Component,
5 | ScrollView,
6 | Text,
7 | View,
8 | TextInput,
9 | TouchableHighlight,
10 | TouchableOpacity,
11 | ToastAndroid,
12 | AsyncStorage,
13 | Navigator,
14 | Image
15 | } from 'react-native';
16 |
17 | import styles from './Style';
18 | import api, {host, key} from './Server';
19 | import Register from './Register';
20 | import RestrictedPage from './RestrictedPage';
21 |
22 | export default class extends Component {
23 | constructor(props) {
24 | super(props);
25 |
26 | this.state = {
27 | data: {
28 | email: undefined,
29 | password: undefined,
30 | role: 0
31 | },
32 | loading: false,
33 | session: undefined
34 | };
35 | }
36 |
37 | render() {
38 | let fields = [
39 | {ref: 'email', placeholder: 'Email', keyboardType: 'email-address', secureTextEntry: false, style: [styles.inputText]},
40 | {ref: 'password', placeholder: 'Password', keyboardType: 'default', secureTextEntry: true, style: [styles.inputText]},
41 | ];
42 |
43 | return (
44 |
45 |
46 | {'LOGIN'}
47 |
48 |
49 | this.onFocus({...fields[0]})} onChangeText={(text) => this.state.data.email = text} />
50 |
51 |
52 | this.onFocus({...fields[1]})} onChangeText={(text) => this.state.data.password = text} />
53 |
54 | this.gotoRoute('forget')}>
55 | {'Forget password?'}
56 |
57 | this.onSubmit()}>
58 | {this.state.loading ? 'Please Wait . . .' : 'Submit'}
59 |
60 |
61 | {'Doesn\'t have an account? '}
62 | this.goBack()}>
63 | {'Register'}
64 |
65 |
66 |
67 | );
68 | }
69 |
70 | onFocus(argument) {
71 | setTimeout(() => {
72 | let scrollResponder = this.refs.loginFormC.getScrollResponder();
73 | scrollResponder.scrollResponderScrollNativeHandleToKeyboard(
74 | React.findNodeHandle(this.refs[argument.ref]), 110, true
75 | );
76 | }, 50);
77 | }
78 |
79 | onSubmit() {
80 | if (this.state.loading) {
81 | ToastAndroid.show('Please Wait . . .', ToastAndroid.SHORT);
82 | return;
83 | }
84 |
85 | let valid = true;
86 |
87 | Object.keys(this.state.data).map((val, key) => {
88 | if ([null, undefined, 'null', 'undefined', ''].indexOf(this.state.data[val]) > -1) valid = false;
89 | });
90 |
91 | if (!valid) return null;
92 |
93 | this.setState({loading: true});
94 |
95 | api.auth.login(this.state.data)
96 | .then((response) => {
97 | if (!response.ok) throw Error(response.statusText || response._bodyText);
98 | return response.json();
99 | })
100 | .then((responseData) => {
101 | console.log(responseData);
102 | ToastAndroid.show(JSON.stringify(responseData), ToastAndroid.LONG);
103 | this.onSuccess(responseData).done(() => this.replaceRoute('restricted'));
104 | })
105 | .catch((error) => {
106 | console.log(error);
107 | ToastAndroid.show(String(error).replace('Error: ',''), ToastAndroid.LONG);
108 | })
109 | .done(() => {
110 | this.setState({loading: false});
111 | });
112 | }
113 |
114 | async onSuccess(data) {
115 | try {
116 | await AsyncStorage.setItem(key, JSON.stringify(data));
117 | } catch (error) {
118 | ToastAndroid.show(String(error).replace('Error: ',''), ToastAndroid.LONG);
119 | }
120 | }
121 |
122 | goBack() {
123 | if (this.props.navigator) {
124 | this.props.navigator.pop();
125 | }
126 | }
127 |
128 | gotoRoute(name) {
129 | if (this.props.navigator && this.props.navigator.getCurrentRoutes()[this.props.navigator.getCurrentRoutes().length-1].name != name) {
130 | this.props.navigator.push({name: name});
131 | }
132 | }
133 |
134 | replaceRoute(name) {
135 | if (this.props.navigator && this.props.navigator.getCurrentRoutes()[this.props.navigator.getCurrentRoutes().length-1].name != name) {
136 | this.props.navigator.replace({name: name});
137 | }
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/Navbar.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React, {
4 | Component,
5 | StyleSheet,
6 | ToastAndroid,
7 | View,
8 | Text,
9 | TextInput,
10 | PropTypes,
11 | AsyncStorage
12 | } from 'react-native';
13 |
14 | import ToolbarAndroid from 'ToolbarAndroid';
15 |
16 | import {key} from './Server';
17 |
18 | export default class extends Component {
19 | static propTypes = {
20 | onSearch: PropTypes.func,
21 | onRefresh: PropTypes.func,
22 | onLogout: PropTypes.func
23 | };
24 |
25 | constructor(props) {
26 | super(props);
27 |
28 | this.state = {
29 | search: false,
30 | query: undefined,
31 | actions: actions,
32 | session: undefined
33 | };
34 | }
35 |
36 | componentWillMount() {
37 | let temp = [];
38 |
39 | this.state.actions.map((val, key) => {
40 | if (!val.auth) {
41 | temp.push(val);
42 | }
43 | });
44 |
45 | if (this.state.actions.length > temp.length) {
46 | AsyncStorage.getItem(key)
47 | .then((value) => {
48 | if (value !== null) {
49 | this.state.session = value;
50 | } else {
51 | this.state.actions = temp;
52 | }
53 | })
54 | .catch((error) => {
55 | ToastAndroid.show(String(error).replace('Error: ',''), ToastAndroid.SHORT);
56 | })
57 | .done();
58 | }
59 | }
60 |
61 | render() {
62 | return (
63 |
64 |
69 | {this.state.search ? this.renderSearch() : this.renderTitle()}
70 |
71 |
72 | );
73 | }
74 |
75 | renderTitle() {
76 | return (
77 |
78 | {this.props.title}
79 |
80 | );
81 | }
82 |
83 | renderSearch() {
84 | return (
85 |
86 | this.state.query = text}
93 | onSubmitEditing={() => this.onSearch()}
94 | />
95 |
96 | );
97 | }
98 |
99 | onActionSelected(position) {
100 | switch (position) {
101 | case 0: this.onSearch(); break;
102 | case 1: this.onRefresh(); break;
103 | case 5: this.onLogout(); break;
104 | default: ToastAndroid.show(`${actions[position].title} selected.`, ToastAndroid.SHORT);
105 | }
106 | }
107 |
108 | onSearch() {
109 | this.props.onSearch && this.props.onSearch();
110 |
111 | if (this.state.query) ToastAndroid.show(`${this.state.query} not found`, ToastAndroid.SHORT);
112 | this.setState({search: !this.state.search, query: undefined});
113 | }
114 |
115 | onRefresh() {
116 | this.props.onRefresh && this.props.onRefresh();
117 | }
118 |
119 | onLogout() {
120 | this.props.onLogout && this.props.onLogout();
121 | }
122 | }
123 |
124 | const icons = {
125 | more: require('./icons/ic_action_more.png'),
126 | search: require('./icons/ic_action_search.png'),
127 | refresh: require('./icons/ic_action_refresh.png'),
128 | };
129 |
130 | const actions = [
131 | {title: 'Search', icon: icons.search, show: 'always'},
132 | {title: 'Refresh', icon: icons.refresh, show: 'ifRoom'},
133 | {title: 'Single Sign On'},
134 | {title: 'Notifications'},
135 | {title: 'Profile', auth: true},
136 | {title: 'Logout', auth: true},
137 | ];
138 |
139 | const styles = StyleSheet.create({
140 | container: {
141 | flex: 1,
142 | alignItems: 'stretch',
143 | },
144 | toolbar: {
145 | height: 60,
146 | backgroundColor: '#00796B'
147 | },
148 | titleContainer: {
149 | flex: 1,
150 | justifyContent: 'center',
151 | backgroundColor: 'transparent',
152 | alignItems: 'center',
153 | },
154 | title: {
155 | alignSelf: 'flex-start',
156 | backgroundColor: 'transparent',
157 | fontSize: 20,
158 | color: '#ffffff'
159 | }
160 | });
161 |
--------------------------------------------------------------------------------
/Register.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React, {
4 | Component,
5 | ScrollView,
6 | Text,
7 | TouchableOpacity,
8 | TouchableHighlight,
9 | View,
10 | TextInput,
11 | ToastAndroid
12 | } from 'react-native';
13 |
14 | import api from './Server';
15 | import styles from './Style';
16 |
17 | export default class extends Component {
18 | constructor(props) {
19 | super(props);
20 |
21 | this.state = {
22 | data: {
23 | name: undefined,
24 | phone: undefined,
25 | password: undefined,
26 | passwordd: undefined,
27 | role: 'patient'
28 | },
29 | loading: false,
30 | messages: []
31 | };
32 | }
33 |
34 | render() {
35 | let fields = [
36 | {ref: 'name', placeholder: 'Full Name', keyboardType: 'default', secureTextEntry: false, message: '* Full Name cannot be blank', style: [styles.inputText]},
37 | {ref: 'phone', placeholder: 'Phone Number', keyboardType: 'numeric', secureTextEntry: false, message: '* Phone Number cannot be blank', style: [styles.inputText]},
38 | {ref: 'password', placeholder: 'Password', keyboardType: 'default', secureTextEntry: true, message: '* Password cannot be blank', style: [styles.inputText]},
39 | {ref: 'passwordd', placeholder: 'Password Confirmation', keyboardType: 'default', secureTextEntry: true, message: '* Password Confirmation cannot be blank', style: [styles.inputText]},
40 | ];
41 |
42 | return(
43 |
44 |
45 | REGISTER
46 |
47 |
48 | {this.renderMessages()}
49 |
50 |
51 | this.onFocus({...fields[0]})} onChangeText={(text) => this.state.data.name = text} />
52 |
53 |
54 | this.onFocus({...fields[1]})} onChangeText={(text) => this.state.data.phone = text} />
55 |
56 |
57 | this.onFocus({...fields[2]})} onChangeText={(text) => this.state.data.password = text} />
58 |
59 |
60 | this.onFocus({...fields[3]})} onChangeText={(text) => this.state.data.passwordd = text} />
61 |
62 | this.onSubmit(fields)}>
63 | {this.state.loading ? 'Please Wait . . .' : 'Submit'}
64 |
65 |
66 | {'Have an account? '}
67 | this.gotoRoute('login')}>
68 | {'Login'}
69 |
70 |
71 |
72 | );
73 | }
74 |
75 | renderMessages() {
76 | if (this.state.messages.length > 0) {
77 | let messages = this.state.messages.map((val, key) => {
78 | if (val.message) return {val.message};
79 | });
80 |
81 | return messages;
82 | }
83 | }
84 |
85 | onFocus(argument) {
86 | setTimeout(() => {
87 | let scrollResponder = this.refs.registerFormC.getScrollResponder();
88 | scrollResponder.scrollResponderScrollNativeHandleToKeyboard(
89 | React.findNodeHandle(this.refs[argument.ref]), 110, true
90 | );
91 | }, 50);
92 | }
93 |
94 | onSubmit(argument) {
95 | if (this.state.loading) {
96 | ToastAndroid.show('Please Wait . . .', ToastAndroid.SHORT);
97 | return null;
98 | }
99 |
100 | let keys = Object.keys(this.state.data).map((val, key) => {
101 | if ([null, undefined, 'null', 'undefined', ''].indexOf(this.state.data[val]) > -1) return val;
102 | });
103 |
104 | this.setState({messages: []});
105 |
106 | argument.map((val, key) => {
107 | if (keys.indexOf(val.ref) > -1) this.setState({messages: this.state.messages.concat(val)});
108 | });
109 |
110 | if (this.state.messages.length > 0) return null;
111 |
112 | this.gotoRoute('login'); return; // for demo only
113 |
114 | this.setState({loading: true});
115 |
116 | api.auth.register(this.state.data)
117 | .then((response) => {
118 | if (!response.ok) throw Error(response.statusText || response._bodyText);
119 | return response.json();
120 | })
121 | .then((responseData) => {
122 | console.log(responseData);
123 | ToastAndroid.show(JSON.stringify(responseData), ToastAndroid.LONG);
124 | this.replaceRoute('login');
125 | })
126 | .catch((error) => {
127 | console.log(error);
128 | ToastAndroid.show(String(error).replace('Error: ',''), ToastAndroid.LONG);
129 | })
130 | .done(() => {
131 | this.setState({loading: false});
132 | });
133 | }
134 |
135 | goBack() {
136 | if (this.props.navigator) {
137 | this.props.navigator.pop();
138 | }
139 | }
140 |
141 | gotoRoute(name) {
142 | if (this.props.navigator && this.props.navigator.getCurrentRoutes()[this.props.navigator.getCurrentRoutes().length-1].name != name) {
143 | this.props.navigator.push({name: name});
144 | }
145 | }
146 |
147 | replaceRoute(name) {
148 | if (this.props.navigator && this.props.navigator.getCurrentRoutes()[this.props.navigator.getCurrentRoutes().length-1].name != name) {
149 | this.props.navigator.replace({name: name});
150 | }
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/ResetPassword.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React, {
4 | Component,
5 | ScrollView,
6 | Text,
7 | TouchableOpacity,
8 | TouchableHighlight,
9 | View,
10 | TextInput,
11 | ToastAndroid
12 | } from 'react-native';
13 |
14 | import api from './Server';
15 | import styles from './Style';
16 |
17 | export default class extends Component {
18 | constructor(props) {
19 | super(props);
20 |
21 | this.state = {
22 | data: {
23 | phone: undefined,
24 | resetPassCode: undefined,
25 | password: undefined,
26 | passwordd: undefined,
27 | role: 2
28 | },
29 | loading: false,
30 | messages: []
31 | };
32 | }
33 |
34 | render() {
35 | let fields = [
36 | {ref: 'phone', placeholder: 'Phone Number', keyboardType: 'numeric', secureTextEntry: false, message: '* Phone number cannot be blank', style: [styles.inputText]},
37 | {ref: 'resetPassCode', placeholder: 'Reset Code', keyboardType: 'default', secureTextEntry: false, message: '* Reset Code cannot be blank', style: [styles.inputText]},
38 | {ref: 'password', placeholder: 'Password', keyboardType: 'default', secureTextEntry: true, message: '* Password cannot be blank', style: [styles.inputText]},
39 | {ref: 'passwordd', placeholder: 'Password Confirmation', keyboardType: 'default', secureTextEntry: true, message: '* Password Confirmation cannot be blank', style: [styles.inputText]},
40 | ];
41 |
42 | return(
43 |
44 |
45 | {'RESET PASSWORD'}
46 |
47 |
48 | {this.renderMessages()}
49 |
50 |
51 | this.onFocus({...fields[0]})} onChangeText={(text) => this.state.data.phone = text} />
52 |
53 |
54 | this.onFocus({...fields[1]})} onChangeText={(text) => this.state.data.resetPassCode = text} />
55 |
56 |
57 | this.onFocus({...fields[2]})} onChangeText={(text) => this.state.data.password = text} />
58 |
59 |
60 | this.onFocus({...fields[3]})} onChangeText={(text) => this.state.data.passwordd = text} />
61 |
62 | this.onSubmit(fields)}>
63 | {this.state.loading ? 'Please Wait . . .' : 'Submit'}
64 |
65 |
66 | );
67 | }
68 |
69 | renderMessages() {
70 | if (this.state.messages.length > 0) {
71 | let messages = this.state.messages.map((val, key) => {
72 | if (val.message) return {val.message};
73 | });
74 |
75 | return messages;
76 | }
77 | }
78 |
79 | onFocus(argument) {
80 | setTimeout(() => {
81 | let scrollResponder = this.refs.resetForm.getScrollResponder();
82 | scrollResponder.scrollResponderScrollNativeHandleToKeyboard(
83 | React.findNodeHandle(this.refs[argument.ref]), 110, true
84 | );
85 | }, 50);
86 | }
87 |
88 | onSubmit(argument) {
89 | if (this.state.loading) {
90 | ToastAndroid.show('Please Wait . . .', ToastAndroid.SHORT);
91 | return null;
92 | }
93 |
94 | let keys = Object.keys(this.state.data).map((val, key) => {
95 | if ([null, undefined, 'null', 'undefined', ''].indexOf(this.state.data[val]) > -1) return val;
96 | });
97 |
98 | this.setState({messages: []});
99 |
100 | argument.map((val, key) => {
101 | if (keys.indexOf(val.ref) > -1) this.setState({messages: this.state.messages.concat(val)});
102 | });
103 |
104 | if (this.state.messages.length > 0) return null;
105 |
106 | this.setState({loading: true});
107 |
108 | api.auth.reset(this.state.data)
109 | .then((response) => {
110 | if (!response.ok) throw Error(response.statusText || response._bodyText);
111 | return response.json();
112 | })
113 | .then((responseData) => {
114 | console.log(responseData);
115 | ToastAndroid.show(JSON.stringify(responseData), ToastAndroid.LONG);
116 | this.replaceRoute('login');
117 | })
118 | .catch((error) => {
119 | console.log(error);
120 | ToastAndroid.show(String(error).replace('Error: ',''), ToastAndroid.LONG);
121 | })
122 | .done(() => {
123 | this.setState({loading: false});
124 | });
125 | }
126 |
127 | goBack() {
128 | if (this.props.navigator) {
129 | this.props.navigator.pop();
130 | }
131 | }
132 |
133 | gotoRoute(name) {
134 | if (this.props.navigator && this.props.navigator.getCurrentRoutes()[this.props.navigator.getCurrentRoutes().length-1].name != name) {
135 | this.props.navigator.push({name: name});
136 | }
137 | }
138 |
139 | replaceRoute(name) {
140 | if (this.props.navigator && this.props.navigator.getCurrentRoutes()[this.props.navigator.getCurrentRoutes().length-1].name != name) {
141 | this.props.navigator.replace({name: name});
142 | }
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/RestrictedPage.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React, {
4 | Component,
5 | View,
6 | Text,
7 | AsyncStorage,
8 | ToastAndroid,
9 | ScrollView,
10 | ProgressBarAndroid
11 | } from 'react-native';
12 |
13 | import Login from './Login';
14 | import Navbar from './Navbar';
15 | import styles from './Style';
16 | import {key} from './Server';
17 |
18 | export default class extends Component {
19 | constructor(props) {
20 | super(props);
21 |
22 | this.state = {
23 | session: undefined,
24 | loading: true
25 | };
26 | }
27 |
28 | componentWillMount() {
29 | this.loadSession().done(() => this.setState({loading: false}));
30 | }
31 |
32 | async loadSession() {
33 | try {
34 | let value = await AsyncStorage.getItem(key);
35 |
36 | if (value !== null) this.setState({session: JSON.parse(value)});
37 | } catch (error) {
38 | ToastAndroid.show(String(error).replace('Error: ',''), ToastAndroid.LONG);
39 | }
40 | }
41 |
42 | render() {
43 | if (this.state.loading) return this.renderLoading();
44 | if (!this.state.session) return this.renderLogin();
45 |
46 | return this.renderScene();
47 | }
48 |
49 | renderScene() {
50 | return (
51 |
52 | this.onLogout().done(() => this.setState({session: undefined}))}
56 | />
57 |
58 |
59 | {JSON.stringify(this.state.session)}
60 |
61 |
62 |
63 | );
64 | }
65 |
66 | renderLogin() {
67 | return ;
68 | }
69 |
70 | renderLoading() {
71 | return ;
72 | }
73 |
74 | async onLogout() {
75 | try {
76 | await AsyncStorage.removeItem(key);
77 | ToastAndroid.show('Logout successfully!', ToastAndroid.SHORT);
78 | } catch (error) {
79 | ToastAndroid.show(String(error).replace('Error: ',''), ToastAndroid.SHORT);
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/Server.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | export const key = '@lussatech:session';
4 | export const host = 'http://aprs.lussa.net';
5 | export default {
6 | auth: {
7 | login: function (data) {
8 | let url = `${host}/auth/login`,
9 | opt = {
10 | method: 'post',
11 | headers: {
12 | 'Accept': 'application/json',
13 | 'Content-Type': 'application/json'
14 | },
15 | body: JSON.stringify(data)
16 | };
17 |
18 | return fetch(url, opt);
19 | },
20 | register: function (data) {
21 | let url = `${host}/auth/register`,
22 | opt = {
23 | method: 'post',
24 | headers: {
25 | 'Accept': 'application/json',
26 | 'Content-Type': 'application/json'
27 | },
28 | body: JSON.stringify(data)
29 | };
30 |
31 | return fetch(url, opt);
32 | },
33 | forget: function (data) {
34 | let url = `${host}/auth/forget`,
35 | opt = {
36 | method: 'post',
37 | headers: {
38 | 'Accept': 'application/json',
39 | 'Content-Type': 'application/json'
40 | },
41 | body: JSON.stringify(data)
42 | };
43 |
44 | return fetch(url, opt);
45 | },
46 | reset: function (data) {
47 | let url = `${host}/auth/reset`,
48 | opt = {
49 | method: 'post',
50 | headers: {
51 | 'Accept': 'application/json',
52 | 'Content-Type': 'application/json'
53 | },
54 | body: JSON.stringify(data)
55 | };
56 |
57 | return fetch(url, opt);
58 | }
59 | }
60 | };
61 |
--------------------------------------------------------------------------------
/Style.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import {StyleSheet} from 'react-native';
4 |
5 | export default StyleSheet.create({
6 | titleContainer: {
7 | backgroundColor: '#00BFA5',
8 | justifyContent: 'center',
9 | alignItems: 'center',
10 | padding: 10,
11 | height: 100
12 | },
13 | title: {
14 | color: '#FFFFFF',
15 | fontWeight: 'bold',
16 | fontSize: 27
17 | },
18 | button: {
19 | backgroundColor: '#26a69a',
20 | padding: 15,
21 | marginTop: 20,
22 | justifyContent: 'center',
23 | alignSelf: 'stretch'
24 | },
25 | buttonDisabled: {
26 | backgroundColor: '#2bbbad',
27 | padding: 15,
28 | marginTop: 20,
29 | justifyContent: 'center',
30 | alignSelf: 'stretch'
31 | },
32 | buttonText: {
33 | fontSize: 15,
34 | color: 'white',
35 | alignSelf: 'center'
36 | },
37 | container: {
38 | flex: 1,
39 | alignItems: 'stretch',
40 | },
41 | welcome: {
42 | fontSize: 20,
43 | textAlign: 'center',
44 | margin: 10,
45 | },
46 | instructions: {
47 | textAlign: 'center',
48 | color: '#333333',
49 | marginBottom: 5,
50 | },
51 | toolbar: {
52 | height: 60,
53 | backgroundColor: '#D6D2D2'
54 | },
55 | message: {
56 | color: 'red',
57 | marginLeft: 5
58 | },
59 | inputContainer: {
60 | borderBottomWidth: 1,
61 | borderBottomColor: 'transparent'
62 | },
63 | inputText: {
64 | backgroundColor: '#FFFFFF',
65 | height: 60
66 | }
67 | });
68 |
--------------------------------------------------------------------------------
/cli.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var fs = require('fs')
4 | , path = require('path')
5 | , source = path.resolve('node_modules', 'react-native-base-authentication')
6 | , target = path.resolve(process.cwd(), 'lib')
7 | , ignore = ['cli.js', 'package.json', 'readme.md', 'preview.gif'];
8 |
9 | function run() {
10 | try {
11 | var packages = JSON.parse(fs.readFileSync(path.resolve(process.cwd(), 'package.json'), 'utf8')) || undefined;
12 | } catch (e) {
13 | console.error('package.json couldn\'t be found, maybe `%s` is not the root of project directory', process.cwd());
14 | process.exit(1);
15 | } finally {
16 | var hasReactNative = packages.dependencies.hasOwnProperty('react-native');
17 |
18 | if (hasReactNative) {
19 | writing(source, target);
20 | } else {
21 | console.error('react-native dependencies couldn\'t be found, maybe `%s` is not the root of react-native project directory', process.cwd());
22 | process.exit(1);
23 | }
24 | }
25 | }
26 |
27 | function writing(source, target) {
28 | if (!fs.existsSync(target)) {
29 | fs.mkdirSync(target);
30 | }
31 |
32 | copyFolderSync(source, target);
33 | }
34 |
35 | function copyFileSync(source, target) {
36 | var targetFile = target;
37 |
38 | if (ignore.indexOf(path.basename(source)) > -1) return;
39 |
40 | if(fs.existsSync(target)) {
41 | if(fs.lstatSync(target).isDirectory()) {
42 | targetFile = path.join(target, path.basename(source));
43 | }
44 | }
45 |
46 | fs.writeFileSync(targetFile, fs.readFileSync(source));
47 | }
48 |
49 | function copyFolderSync(source, target) {
50 | var files = [];
51 | var targetFolder = path.join(target, path.basename(source));
52 |
53 | if (!fs.existsSync(targetFolder)) {
54 | fs.mkdirSync(targetFolder);
55 | }
56 |
57 | if(fs.lstatSync(source).isDirectory()) {
58 | files = fs.readdirSync(source);
59 | files.forEach(function (file) {
60 | var curSource = path.join(source, file);
61 |
62 | if (fs.lstatSync(curSource).isDirectory()) {
63 | copyFolderSync(curSource, targetFolder);
64 | } else {
65 | copyFileSync(curSource, targetFolder);
66 | }
67 | });
68 | }
69 | }
70 |
71 | module.exports = {
72 | run: run
73 | };
74 |
--------------------------------------------------------------------------------
/icons/ic_action_more.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lussatech/react-native-base-authentication/e1c07024be98fbd0412a1c0cdafb8c40dc78bff9/icons/ic_action_more.png
--------------------------------------------------------------------------------
/icons/ic_action_more@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lussatech/react-native-base-authentication/e1c07024be98fbd0412a1c0cdafb8c40dc78bff9/icons/ic_action_more@2x.png
--------------------------------------------------------------------------------
/icons/ic_action_more@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lussatech/react-native-base-authentication/e1c07024be98fbd0412a1c0cdafb8c40dc78bff9/icons/ic_action_more@3x.png
--------------------------------------------------------------------------------
/icons/ic_action_refresh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lussatech/react-native-base-authentication/e1c07024be98fbd0412a1c0cdafb8c40dc78bff9/icons/ic_action_refresh.png
--------------------------------------------------------------------------------
/icons/ic_action_refresh@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lussatech/react-native-base-authentication/e1c07024be98fbd0412a1c0cdafb8c40dc78bff9/icons/ic_action_refresh@2x.png
--------------------------------------------------------------------------------
/icons/ic_action_refresh@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lussatech/react-native-base-authentication/e1c07024be98fbd0412a1c0cdafb8c40dc78bff9/icons/ic_action_refresh@3x.png
--------------------------------------------------------------------------------
/icons/ic_action_search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lussatech/react-native-base-authentication/e1c07024be98fbd0412a1c0cdafb8c40dc78bff9/icons/ic_action_search.png
--------------------------------------------------------------------------------
/icons/ic_action_search@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lussatech/react-native-base-authentication/e1c07024be98fbd0412a1c0cdafb8c40dc78bff9/icons/ic_action_search@2x.png
--------------------------------------------------------------------------------
/icons/ic_action_search@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lussatech/react-native-base-authentication/e1c07024be98fbd0412a1c0cdafb8c40dc78bff9/icons/ic_action_search@3x.png
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | import React, {
4 | AppRegistry,
5 | Component,
6 | StyleSheet,
7 | Text,
8 | View,
9 | Navigator,
10 | TouchableHighlight,
11 | ScrollView,
12 | ToastAndroid,
13 | BackAndroid
14 | } from 'react-native';
15 |
16 | import ForgetPassword from './ForgetPassword';
17 | import Login from './Login';
18 | import Navbar from './Navbar';
19 | import Register from './Register';
20 | import ResetPassword from './ResetPassword';
21 | import RestrictedPage from './RestrictedPage';
22 | import Server, {host as Host, key as Key} from './Server';
23 | import Style from './Style';
24 |
25 | export {ForgetPassword, Login, Navbar, Register, ResetPassword, RestrictedPage, Server, Host, Key, Style};
26 |
27 | export default class extends Component {
28 | constructor(props) {
29 | super(props);
30 | }
31 |
32 | render() {
33 | return (
34 | {
38 | return route.sceneConfig ? route.sceneConfig : Navigator.SceneConfigs.HorizontalSwipeJump;
39 | }}
40 | />
41 | );
42 | }
43 |
44 | renderScene(route, navigator) {
45 | _navigator = navigator;
46 | switch (route.name) {
47 | case 'login':
48 | return
49 | break;
50 | case 'forget':
51 | return
52 | break;
53 | case 'reset':
54 | return
55 | break;
56 | case 'restricted':
57 | return
58 | break;
59 | default:
60 | return
61 | }
62 | }
63 | }
64 |
65 | let _navigator;
66 |
67 | BackAndroid.addEventListener('hardwareBackPress', () => {
68 | if (_navigator.getCurrentRoutes().length === 1 ) {
69 | return false;
70 | }
71 | _navigator.pop();
72 | return true;
73 | });
74 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-base-authentication",
3 | "version": "1.0.3",
4 | "main": "index.js",
5 | "scripts": {
6 | "test": "echo \"Error: no test specified\" && exit 1"
7 | },
8 | "author": "Lussa Teknologi",
9 | "license": "ISC",
10 | "description": "an authentication, policy and session management template for react native to connect with your server API",
11 | "repository": {
12 | "type": "git",
13 | "url": "https://github.com/lussatech/react-native-base-authentication.git"
14 | },
15 | "bugs": {
16 | "url": "https://github.com/lussatech/react-native-base-authentication/issues"
17 | },
18 | "homepage": "https://github.com/lussatech/react-native-base-authentication"
19 | }
20 |
--------------------------------------------------------------------------------
/preview.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lussatech/react-native-base-authentication/e1c07024be98fbd0412a1c0cdafb8c40dc78bff9/preview.gif
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | ### Installation
4 | npm i react-native-base-authentication
5 |
6 | ### Generate Files
7 | Before generate library files to your react-native-project, make sure that `lussatech-cli` is installed globally in your machine, otherwise use this command to install it:
8 |
9 | npm i lussatech-cli -g
10 |
11 | If `lussatech-cli` have been installed, change directory to your react-native-project and run this command:
12 |
13 | lussatech generate react-native-base-authentication
14 |
15 | then the library files will be added automatically inside your react-native-project, e.g.
16 |
17 | react-native-project
18 | |_ ...
19 | |_ lib
20 | |_ react-native-base-authentication
21 | |_ ...
22 | |_ index.js
23 | |_ ...
24 |
25 | ### Usage
26 | ```javascript
27 | ...
28 | import BaseAuth, { // sample app
29 | /* available components */
30 | Navbar, // sample navigation bar
31 | Login, // sample login view
32 | Register, // sample register view
33 | ForgetPassword, // sample forget password view
34 | ResetPassword, // sample reset password view
35 | RestrictedPage, // sample restricted view
36 | /* available constants */
37 | Server, // sample api end-point
38 | Host, // sample host for api end-point
39 | Key, // sample key for asynstorage
40 | Style // sample styles
41 | } from './lib/react-native-base-authentication';
42 |
43 | class Name extends Component {
44 | render() {
45 | return (
46 | // sample calling component
47 | );
48 | }
49 | }
50 | ...
51 | ```
52 |
53 | ###### Manage API end-point
54 | To manage api end-point, update `Server.js` based on your api end-point, e.g.
55 |
56 | ```javascript
57 | # lib/react-native-base-authentication/Server.js
58 |
59 | ...
60 | export const key = '@lussatech:session'; // key for asynstorage
61 | export const host = 'http://example.com'; // host for api end-point
62 | export default {
63 | auth: {
64 | login: function (data) {
65 | let url = `${host}/auth/login`, // api url for login
66 | opt = { // optional second argument
67 | method: 'post', // to customize the HTTP request
68 | headers: {
69 | 'Accept': 'application/json',
70 | 'Content-Type': 'application/json'
71 | },
72 | body: JSON.stringify(data)
73 | };
74 |
75 | return fetch(url, opt);
76 | },
77 | ...
78 | },
79 | ...
80 | };
81 | ...
82 | ```
83 |
84 | ###### Customize navigation bar
85 | To customize navigation bar, update `Navbar.js` based on your need, e.g.
86 |
87 | ```javascript
88 | # lib/react-native-base-authentication/Navbar.js
89 |
90 | ...
91 | export default class extends Component {
92 | /* to validate props value */
93 | static propTypes = {
94 | onLogout: PropTypes.func,
95 | ...
96 | };
97 |
98 | constructor(props) {
99 | super(props);
100 |
101 | this.state = {
102 | actions: actions,
103 | ...
104 | };
105 | }
106 |
107 | componentWillMount() {
108 | let temp = [];
109 |
110 | this.state.actions.map((val, key) => {
111 | if (!val.auth) temp.push(val);
112 | });
113 |
114 | if (this.state.actions.length > temp.length) {
115 | AsyncStorage.getItem(Key) // check session at asynstorage
116 | .then((value) => {
117 | if (value !== null) this.state.session = value;
118 | else this.state.actions = temp; // if no session, hide auth menu
119 | })
120 | .catch((error) => {
121 | ...
122 | })
123 | .done();
124 | }
125 | }
126 |
127 | /* when a menu is selected */
128 | onActionSelected(position) {
129 | switch (position) {
130 | ...
131 | case 5: this.onLogout(); break;
132 | default: ToastAndroid.show(`${actions[position].title} selected.`, ToastAndroid.SHORT);
133 | }
134 | }
135 | ...
136 |
137 | /* when selected menu is `Logout` */
138 | onLogout() {
139 | /* calling onLogout props action if available */
140 | this.props.onLogout && this.props.onLogout();
141 | }
142 | ...
143 | }
144 |
145 | /* list of menu */
146 | const actions = [
147 | {title: 'Search', icon: icons.search, show: 'always'},
148 | {title: 'Refresh', icon: icons.refresh, show: 'ifRoom'},
149 | ...
150 | /* only authenticated user can see this menu */
151 | {title: 'Profile', auth: true},
152 | {title: 'Logout', auth: true},
153 | ];
154 | ...
155 | ```
156 |
157 | then include the navigation bar inside your react-native-project, e.g.
158 |
159 | ```javascript
160 | # lib/react-native-base-authentication/RestrictedPage.js
161 |
162 | ...
163 | render() {
164 | return (
165 |
166 | this.onLogout().done())} />
167 |
168 | ...
169 |
170 |
171 | );
172 | }
173 |
174 | async onLogout() {
175 | ...
176 | }
177 | ...
178 | ```
179 |
180 | #### Customize views
181 | To customize views, update `ForgetPassword.js`, `Login.js`, `Register.js`, `ResetPassword.js` and `RestrictedPage.js` based on your need, e.g.
182 |
183 | ```javascript
184 | # lib/react-native-base-authentication/Login.js
185 |
186 | ...
187 | export default class extends Component {
188 | constructor(props) {
189 | super(props);
190 |
191 | this.state = {
192 | data: {
193 | email: undefined,
194 | password: undefined,
195 | ...
196 | },
197 | ...
198 | };
199 | }
200 |
201 | render() {
202 | let fields = [
203 | {ref: 'email', placeholder: 'Email', keyboardType: 'email-address', secureTextEntry: false, style: [styles.inputText]},
204 | {ref: 'password', placeholder: 'Password', keyboardType: 'default', secureTextEntry: true, style: [styles.inputText]},
205 | ...
206 | ];
207 |
208 | return(
209 | ...
210 |
211 | this.state.data.email = text} />
212 |
213 |
214 | this.state.data.password = text} />
215 |
216 | ...
217 | this.onSubmit()}>
218 | {'Submit'}
219 |
220 | ...
221 | );
222 | }
223 |
224 | onSubmit() {
225 | ...
226 | api.auth.login(this.state.data) // call api url for login
227 | .then((response) => {
228 | if (!response.ok) throw Error(response.statusText || response._bodyText);
229 | return response.json();
230 | })
231 | .then((responseData) => {
232 | this.onSuccess(responseData).done(); // store session at asynstorage
233 | })
234 | .catch((error) => {
235 | ...
236 | })
237 | .done(() => {
238 | ...
239 | });
240 | }
241 |
242 | async onSuccess(data) {
243 | try {
244 | await AsyncStorage.setItem(Key, JSON.stringify(data)); // save response data on asynstorage as session
245 | ...
246 | } catch (error) {
247 | ...
248 | }
249 | }
250 | }
251 | ...
252 | ```
253 |
254 | ```javascript
255 | # lib/react-native-base-authentication/RestrictedPage.js
256 |
257 | ...
258 | export default class extends Component {
259 | ...
260 | componentWillMount() {
261 | this.loadSession().done(); // check session at asynstorage
262 | }
263 |
264 | render() {
265 | if (!this.state.session) return this.renderLogin(); // if no session at asynstorage, render login page
266 | ...
267 | }
268 |
269 | async loadSession() {
270 | try {
271 | let value = await AsyncStorage.getItem(Key);
272 |
273 | if (value !== null) this.setState({session: JSON.parse(value)});
274 | } catch (error) {
275 | ...
276 | }
277 | }
278 |
279 | async onLogout() {
280 | try {
281 | await AsyncStorage.removeItem(key);
282 | } catch (error) {
283 | ...
284 | }
285 | }
286 | ...
287 | }
288 | ...
289 | ```
290 |
--------------------------------------------------------------------------------