Skip to content

Commit f86c75f

Browse files
committed
mobile: simple ios-only starter
1 parent b934340 commit f86c75f

File tree

5 files changed

+293
-32
lines changed

5 files changed

+293
-32
lines changed

mobile/App/auth.ios.js

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
'use strict';
2+
3+
let React = require('react-native');
4+
5+
let {
6+
Text,
7+
View,
8+
ListView,
9+
AsyncStorage,
10+
StyleSheet,
11+
TouchableHighlight
12+
} = React;
13+
14+
let t = require('tcomb-form-native');
15+
let {Form} = t.form;
16+
17+
let STORAGE_KEY = '@Jobpig:jwt';
18+
let AuthModel = t.struct({
19+
email: t.Str,
20+
password: t.Str,
21+
});
22+
let options = {
23+
fields: {
24+
email: {keyboardType: 'email-address'},
25+
password: {secureTextEntry: true},
26+
}
27+
}
28+
29+
let token = null;
30+
module.exports.logout = ()=> AsyncStorage.removeItem(STORAGE_KEY);
31+
module.exports.getToken = ()=> token;
32+
33+
module.exports.Auth = React.createClass({
34+
componentDidMount() {
35+
this._loadUser().done();
36+
},
37+
async _loadUser() {
38+
try {
39+
token = await AsyncStorage.getItem(STORAGE_KEY);
40+
if (!token) return;
41+
this.props.login();
42+
} catch (error) {
43+
alert('AsyncStorage error: ' + error.message);
44+
}
45+
},
46+
render: function() {
47+
console.log('auth render');
48+
return(
49+
<View style={styles.container}>
50+
{/* display */}
51+
<Form
52+
ref="form"
53+
type={AuthModel}
54+
options={options}
55+
/>
56+
<TouchableHighlight style={styles.button} onPress={this.onPress} underlayColor='#99d9f4'>
57+
<Text style={styles.buttonText}>Login</Text>
58+
</TouchableHighlight>
59+
</View>
60+
);
61+
},
62+
onPress: async function() {
63+
try {
64+
let res = await fetch('http://jobpigapp.herokuapp.com/login', {
65+
method: 'post',
66+
headers: {
67+
'Accept': 'application/json',
68+
'Content-Type': 'application/json'
69+
},
70+
body: JSON.stringify(this.refs.form.getValue())
71+
});
72+
token = (await res.json()).token;
73+
await AsyncStorage.setItem(STORAGE_KEY, token);
74+
this._loadUser().done();
75+
} catch (err) {
76+
console.log(err);
77+
}
78+
},
79+
});
80+
81+
let styles = StyleSheet.create({
82+
container: {
83+
justifyContent: 'center',
84+
marginTop: 50,
85+
padding: 20,
86+
backgroundColor: '#ffffff',
87+
},
88+
title: {
89+
fontSize: 30,
90+
alignSelf: 'center',
91+
marginBottom: 30
92+
},
93+
buttonText: {
94+
fontSize: 18,
95+
color: 'white',
96+
alignSelf: 'center'
97+
},
98+
button: {
99+
height: 36,
100+
backgroundColor: '#48BBEC',
101+
borderColor: '#48BBEC',
102+
borderWidth: 1,
103+
borderRadius: 8,
104+
marginBottom: 10,
105+
alignSelf: 'stretch',
106+
justifyContent: 'center'
107+
}
108+
});

mobile/App/index.ios.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
'use strict';
2+
3+
let React = require('react-native');
4+
5+
let {
6+
Text,
7+
View,
8+
ListView,
9+
AsyncStorage,
10+
StyleSheet,
11+
TouchableHighlight
12+
} = React;
13+
14+
let auth = require('./auth.ios');
15+
let Job = require('./job.ios');
16+
17+
let Main = React.createClass({
18+
getInitialState(){
19+
return {loggedIn: false};
20+
},
21+
render() {
22+
return this.state.loggedIn ? <Job logout={this._logout} /> : <auth.Auth login={this._login} />;
23+
},
24+
_login(){
25+
this.setState({loggedIn: true});
26+
},
27+
_logout(){
28+
auth.logout().then( ()=> this.setState({loggedIn:false}) );
29+
}
30+
});
31+
32+
let styles = StyleSheet.create({
33+
container: {
34+
justifyContent: 'center',
35+
marginTop: 50,
36+
padding: 20,
37+
backgroundColor: '#ffffff',
38+
},
39+
title: {
40+
fontSize: 30,
41+
alignSelf: 'center',
42+
marginBottom: 30
43+
},
44+
});
45+
46+
module.exports = Main;

mobile/App/job.ios.js

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
'use strict';
2+
3+
let React = require('react-native');
4+
let {
5+
Text,
6+
View,
7+
ListView,
8+
AsyncStorage,
9+
StyleSheet,
10+
TouchableHighlight,
11+
} = React;
12+
13+
let _ = require('lodash');
14+
let auth = require('./auth.ios');
15+
16+
let Job = React.createClass({
17+
getInitialState(){
18+
return {job: false};
19+
},
20+
21+
async componentDidMount(){
22+
try {
23+
let res = await fetch('http://jobpigapp.herokuapp.com/jobs/inbox', {
24+
headers: {'x-access-token': auth.getToken()}
25+
});
26+
res = await res.json();
27+
if(res.message)
28+
this.props.logout();
29+
this.setState({job:res[0]});
30+
} catch (err) {
31+
console.log(err);
32+
}
33+
},
34+
35+
render() {
36+
if (!this.state.job)
37+
return <View style={styles.container}>
38+
<Text>Loading...</Text>
39+
</View>;
40+
41+
let job = this.state.job;
42+
return <View style={styles.container}>
43+
<Text style={styles.title}>{job.title}</Text>
44+
<Text style={styles.meta}>{this._meta(job)}</Text>
45+
<View>{
46+
[{k:'applied',v:'Applied'}, {k:'hidden',v:'Skip'}].map(button=>
47+
<TouchableHighlight style={[styles.button, styles.metaBtn]} onPress={()=>this._setStatus(button.k)} underlayColor='#99d9f4'>
48+
<Text style={styles.buttonText}>{button.v}</Text>
49+
</TouchableHighlight>
50+
)
51+
}</View>
52+
<View>{
53+
[{k: 'liked', v: 'Like'}, {k: 'disliked', v: 'Dislike'}].map(button=>
54+
<TouchableHighlight style={[styles.button, styles.thumb]} onPress={()=>this._setStatus(button.k)} underlayColor='#99d9f4'>
55+
<Text style={styles.buttonText}>{button.v}</Text>
56+
</TouchableHighlight>
57+
)
58+
}</View>
59+
<Text>{this._htmlToString(job.description)}</Text>
60+
</View>;
61+
},
62+
63+
_meta(job){
64+
let score = job.score>0 ? `+${job.score}` : job.score<0 ? job.score : false;
65+
let meta = _.filter([
66+
{name:"Source", text:job.source, icon:'find_in_page'},
67+
{name:"Company", text:job.company, icon:'supervisor_account'},
68+
{name:"Location", text:job.location, icon:'room'},
69+
{name:"Budget", text:job.budget, icon:'attach_money'},
70+
{name:"Score", text:score, icon:'thumb_up'},
71+
{name:"Tags", icon:'label', style:{color:'rgb(0, 188, 212)', textTransform:'uppercase', fontWeight:500}, text: _.pluck(job.tags, 'key').join(', ') }
72+
], 'text');
73+
return <Text>{meta.map(m=> `${m.name}: ${m.text}`).join('; ')}</Text>;
74+
},
75+
76+
_htmlToString(str) {
77+
return String(str).replace(/<p>/g, '\n\n').replace(/&#x2F;/g, '/').replace('<i>', '').replace('</i>', '').replace(/&#x27;/g, '\'').replace(/&quot;/g, '\"').replace(/<a\s+(?:[^>]*?\s+)?href="([^"]*)" rel="nofollow">(.*)?<\/a>/g, "$1");
78+
},
79+
80+
_setStatus(status){
81+
//JobActions.setStatus({id:this.props.job.id,status});
82+
}
83+
84+
});
85+
86+
let styles = StyleSheet.create({
87+
container: {
88+
justifyContent: 'center',
89+
marginTop: 50,
90+
padding: 20,
91+
backgroundColor: '#ffffff',
92+
},
93+
title: {
94+
fontSize: 30,
95+
alignSelf: 'center',
96+
marginBottom: 30
97+
},
98+
btnText: {
99+
fontSize: 18,
100+
color: 'white',
101+
alignSelf: 'center'
102+
},
103+
buttonText: {
104+
fontSize: 18,
105+
color: 'white',
106+
alignSelf: 'center'
107+
},
108+
button: {
109+
height: 36,
110+
backgroundColor: '#48BBEC',
111+
borderColor: '#48BBEC',
112+
borderWidth: 1,
113+
borderRadius: 8,
114+
marginBottom: 10,
115+
alignSelf: 'stretch',
116+
justifyContent: 'center'
117+
}
118+
});
119+
120+
module.exports = Job;

mobile/index.ios.js

Lines changed: 14 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,36 @@
1-
/**
2-
* Sample React Native App
3-
* https://github.com/facebook/react-native
4-
*/
51
'use strict';
62

73
var React = require('react-native');
4+
85
var {
96
AppRegistry,
107
StyleSheet,
11-
Text,
12-
View,
8+
NavigatorIOS,
139
} = React;
1410

11+
var AppView = require('./App/index.ios.js');
12+
1513
var Jobpig = React.createClass({
1614
render: function() {
1715
return (
18-
<View style={styles.container}>
19-
<Text style={styles.welcome}>
20-
Welcome to React Native!
21-
</Text>
22-
<Text style={styles.instructions}>
23-
To get started, edit index.ios.js
24-
</Text>
25-
<Text style={styles.instructions}>
26-
Press Cmd+R to reload,{'\n'}
27-
Cmd+D or shake for dev menu
28-
</Text>
29-
</View>
16+
<NavigatorIOS
17+
style={styles.container}
18+
tintColor='#FF6600'
19+
initialRoute={{
20+
title: 'Jobpig',
21+
component: AppView,
22+
}}/>
3023
);
3124
}
3225
});
3326

3427
var styles = StyleSheet.create({
3528
container: {
3629
flex: 1,
37-
justifyContent: 'center',
38-
alignItems: 'center',
39-
backgroundColor: '#F5FCFF',
40-
},
41-
welcome: {
42-
fontSize: 20,
43-
textAlign: 'center',
44-
margin: 10,
45-
},
46-
instructions: {
47-
textAlign: 'center',
48-
color: '#333333',
49-
marginBottom: 5,
30+
backgroundColor: '#F6F6EF',
5031
},
5132
});
5233

5334
AppRegistry.registerComponent('Jobpig', () => Jobpig);
35+
36+
module.exports = Jobpig;

mobile/package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66
"start": "node_modules/react-native/packager/packager.sh"
77
},
88
"dependencies": {
9-
"react-native": "^0.12.0"
9+
"lodash": "^3.10.1",
10+
"react-native": "^0.12.0",
11+
"tcomb": "^1.1.0",
12+
"tcomb-form-native": "^0.2.7",
13+
"tcomb-validation": "^1.0.4"
1014
}
1115
}

0 commit comments

Comments
 (0)