This is the third part of our dating app. If you haven’t gone through the previous parts/articles make sure you read them , so that, you are able to better understand this part of article.
In this part we we will be doing one of the most important feature of dating app. Tinder app got famous because of this feature. We will be doing the distance filter feature in this part of article. Distance filter allows users to connect to the other people near to to there location. They will be able to set the distance around which they want people to filter out.
To start with we will get the users location in form of latitude and longitude then they login and save it under their profile in firebase database. The code for the same will be some thing like this :
componentDidMount() {
this.lat = 0
this.long = 0
navigator.geolocation.getCurrentPosition(
(position) => {
this.lat = position.coords.latitude
this.long = position.coords.longitude
},
(error) => {},
{ enableHighAccuracy: true, timeout: 20000, maximumAge: 1000 },
);
}
In the above code user will we asked access to their location. In case they deny access to the location, latitude and and longitude will be set to 0.
Next, we will add one more button, Distance button , to our Profile Component, below the Height button. On pressing the Distance button user will be navigated to Distance Component with they user info as parameter. Below is the updated code of Profile component :
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
Button,
Switch
} from 'react-native';
import firebase from 'firebase'
export default class Profile extends Component {
constructor(props) {
super(props)
this.state = {
user: this.props.screenProps,
SwitchGender: true
}
}
componentWillUnmount() {
const { uid } = this.state.user
this.firebaseRef.child(uid).child('dp').off('value')
}
componentDidMount(){
//console.warn("Profile");
const { uid } = this.state.user
this.firebaseRef = firebase.database().ref('users')
this.firebaseRef.child(uid).on('value', snap => {
const user = snap.val()
this.setState({ user: user, SwitchGender: user.filterGender })
})
}
toggleGender(value){
let user = this.state.user
const userData = {
filterGender: value
}
firebase.database().ref('users').child(user.uid).update({ ...userData})
this.setState({SwitchGender: value})
}
render() {
return (
<View style={styles.container}>
<Button
onPress={() => this.props.navigation.navigate('Age', { user: this.state.user })}
title={ "Age("+this.state.user.ageRange[0]+"yrs "+"to"+this.state.user.ageRange[1]+"yrs)" }
/>
<Button
onPress={() => this.props.navigation.navigate('Height', { user: this.state.user })}
title={"Height("+this.state.user.heightRange[0]+"'"+this.state.user.heightRange[1]+'"'+"to"+this.state.user.heightRange[2]+"'"+this.state.user.heightRange[3]+'"'+")"}
/>
<Button
onPress={() => this.props.navigation.navigate('Distance', { user: this.state.user })}
title={ "Distance("+this.state.user.distance+"kms)" }
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});
Distance component is quite simple. We are using only one slider to change distance, so, we don,t even need any validation like Age (http://calsob.in/how-to-build-a-dating-app-using-react-native-and-firebase-part-1) and Height (http://calsob.in/how-to-build-a-dating-app-using-react-native-and-firebase-part-2) Component. Here is the code for Distance component :
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
Slider
} from 'react-native';
import firebase from 'firebase'
export default class Distance extends Component {
constructor(props) {
super(props)
this.state = { user: this.props.navigation.state.params.user, distance: 160}
}
componentDidMount(){
const { distance } = this.state.user
this.setState({ distance: distance })
}
getValfrom(val){
const { uid } = this.state.user
const userData = {
distance: val
}
this.agefrom = val
firebase.database().ref('users').child(uid).update({ ...userData})
}
render(){
return (
<View style={styles.container}>
<Text style={styles.instructions}>
Distance :
</Text>
<Slider
style={{ width: 300 }}
step={1}
minimumValue={2}
maximumValue={160}
value={this.state.distance}
onValueChange={val => this.setState({ distance: val }) }
onSlidingComplete={ val => this.getValfrom(val)}
/>
<Text style={styles.welcome}>
{this.state.distance}
</Text>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
}
});
In the above code we are take input of distance using the slider component and updating distance in the firebase database.
Now, lets update the Match component. We will add a formula to calculate distance between two latitude and longitude pair, and, using this formula we will filter the members who are near to the logged in user. Here is the code for the match component :
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
ListView,
Image
} from 'react-native';
import _ from 'lodash'
import moment from 'moment'
import firebase from 'firebase'
export default class Match extends Component {
constructor(props) {
super(props)
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
user: this.props.screenProps,
dataSource: ds.cloneWithRows([]),
}
}
calcDistance(lat1, lon1, lat2, lon2) {
var p = 0.017453292519943295; // Math.PI / 180
var c = Math.cos;
var a = 0.5 - c((lat2 - lat1) * p)/2 +
c(lat1 * p) * c(lat2 * p) *
(1 - c((lon2 - lon1) * p))/2;
return 12742 * Math.asin(Math.sqrt(a)); // 2 * R; R = 6371 km
}
componentDidMount(){
const { uid } = this.state.user
this.firebaseRef = firebase.database().ref('users')
this.firebaseRef.on('value', snap => {
let users = snap.val();
const rejectMe = _.reject(users, user => user.uid === uid)
const filterMe = _.filter(users, user => user.uid === uid)
const user = filterMe[0];
/* Age filter start */
const userBday = moment(user.birthday, 'MM/DD/YYYY')
const userAge = moment().diff(userBday, 'years')
const filterByAge = _.filter(rejectMe, profile => {
const profileBday = moment(profile.birthday, 'MM/DD/YYYY')
const profileAge = moment().diff(profileBday, 'years')
const inRangeUser = _.inRange(profileAge, user.ageRange[0], user.ageRange[1] + 1)
const inRangeProfile = _.inRange(userAge, profile.ageRange[0], profile.ageRange[1] + 1)
return inRangeUser && inRangeProfile
})
/* Age filter end */
/* Height filter start */
const userheightin = user.height[0]*12+user.height[1]
const userheightrangefrom = user.heightRange[0]*12+user.heightRange[1]
const userheightrangeto = user.heightRange[2]*12+user.heightRange[3]
const filterByHeight = _.filter(filterByAge, profile => {
const profileheightin = profile.height[0]*12+profile.height[1]
const profileheightrangefrom = profile.heightRange[0]*12+profile.heightRange[1]
const profileheightrangeto = profile.heightRange[2]*12+profile.heightRange[3]
const inRangeUserHeight = _.inRange(profileheightin, userheightrangefrom, userheightrangeto + 1)
const inRangeProfileHeight = _.inRange(userheightin, profileheightrangefrom, profileheightrangeto + 1)
return inRangeUserHeight && inRangeProfileHeight
})
/* Height filter end */
/* Distance filter start */
const filterByDistance = _.filter(filterByHeight, profile => {
const distance = parseInt(this.calcDistance(user.location[0],user.location[1],profile.location[0],profile.location[1]))
const inRangeUserDistance = _.inRange(distance, 1, profile.distance + 1)
const inRangeProfileDistance = _.inRange(distance, 1, user.distance + 1)
return inRangeUserDistance&& inRangeProfileDistance
})
/* Distance filter end */
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.setState({ dataSource: ds.cloneWithRows(filterByDistance) })
})
}
renderRow(rowData, sectionID, rowID) {
return (
<View style={styles.rowContainer}>
<Image source={{ uri: rowData.dp }} style={styles.photo} />
<Text style={styles.text}>
{`${rowData.first_name} ${rowData.last_name}`}
</Text>
</View>
)
}
render() {
return (
<ListView
enableEmptySections={true}
removeClippedSubviews={false}
style={styles.container}
dataSource={this.state.dataSource}
renderRow={this.renderRow.bind(this)}
/>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 20,
},
rowContainer: {
flex: 1,
padding: 12,
flexDirection: 'row',
alignItems: 'center',
},
text: {
marginLeft: 12,
fontSize: 16,
},
photo: {
height: 40,
width: 40,
borderRadius: 20,
},
});
In the above code, the filtered array that we are getting from Height filter is getting passed to distance filter and the result of distance filter is finally passed to List component. So, after passing three filters members list is finally getting rendered.