How to build a dating app using React Native and Firebase – part 3

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.